-
-
Notifications
You must be signed in to change notification settings - Fork 0
Security Conformance
A single published mapping of the plugin's authentication surface to OWASP ASVS 5.0 (May 2025) and the OAuth 2.0 Security Best Current Practice (RFC 9700, Jan 2025). This is a maintainer self-assessment, not a certification or an audit result; it exists so the RC gate's "zero open security findings" is checkable against a named baseline with honestly-recorded residuals. It complements the Security Model (the control narratives) and the Review Gate (how they are verified).
Scope. The plugin is an OIDC/SAML relying party: it delegates
authentication to the operator's IdP, exchanges the authorization code for an
id_token / consumes a signed SAML assertion, and mints a Jellyfin session. It
is not an authorization server and does not issue or downstream OAuth access
tokens — several server-side ASVS/RFC requirements are therefore N/A by role.
Assessment date: 2026-07. Re-assess when a control changes; each row cites the implementing source so a drift is caught by the Review Gate.
| Area (ASVS chapter) | Requirement, as it applies here | Status | Where |
|---|---|---|---|
| V1 Encoding / output | Server-rendered pages auto-escape; the intermediate auth page and error pages carry a per-response nonce CSP, X-Frame-Options: DENY, nosniff; IdP-supplied text is sanitized at the log call |
Met |
Api/Shared/FlowResponses.cs, Api/Shared/AuthPageCsp.cs, Api/Shared/BrowserErrorPage.cs; Security Model § Response hardening
|
| V6 Authentication | Identity bound to the stable sub / SAML NameID, never a mutable username; issuer-bound account links (TOFU); fail-closed on any missing signature/binding |
Met |
Api/Linking/CanonicalLinkService.cs (issuer binding #186), Api/Identity/VerifiedIdentity.cs; Security Model § Identity binding
|
| V6 Authentication — id_token |
id_token signature validated against discovery JWKS; iss, aud/azp, exp/nbf (clock-skew) all checked; asymmetric-only algorithm allow-list; alg:none and RS→HS confusion rejected |
Met |
Api/Oidc/OidcIdTokenValidator.cs; Security Model § OpenID id_token validation
|
| V6 Authentication — SAML | XML-DSig verified before use; exactly one same-document reference covering the consumed assertion; enveloped-signature + position-bound XPath (XSW-resistant); SHA-256+ algorithm/canonicalization/transform allow-list; audience (strict AND), recipient, time-window, bearer-confirmation; replay cache; DTD/XXE prohibited | Met |
Api/Saml/SamlResponse.cs, SamlAssertionValidator.cs, SamlSignatureAlgorithms.cs, SamlReplayCache.cs; Security Model § SAML response validation
|
| V7 Session management | CSRF/replay-resistant login: state + nonce, one-time authorize-state redemption, one-time login-outcome token, capped+swept state store; browser-binding cookie on both flows (forced-login defense) |
Met |
Api/Oidc/OidcStateStore.cs, Api/Saml/SamlRequestCache.cs, the authorize-state binding; Security Model § Login browser binding
|
| V7 Session management — revocation | In-flight revocation gate re-checks the link is live before any side effect and again before AuthenticateDirect
|
Met |
Api/Session/SessionMinter.cs (#232) |
| V8 Authorization | Un-forgeable role→permission mapping; default-deny; last-admin / break-glass guard so no config strands the server; SoD via the SSO-only lever | Met |
Api/Authz/PermissionRolePolicy.cs, Api/Session/SsoOnlyLoginGuard.cs; Security Model § SSO-only login
|
| V11 Cryptography | Secrets AES-256-GCM at rest (ssoenc:v1, separate key file, redacted on export); no weak-primitive acceptance (SHA-1 XML-DSig rejected); a fresh nonce per encryption; tamper → fail-closed |
Met |
Api/Secrets/SecretEnvelope.cs, SecretStore.cs; Security Model § Secrets encrypted at rest
|
| V11 Cryptography — key strength | Minimum asymmetric signing-key-strength floor for IdP JWKS keys and SAML certs: RSA keys below 2048 bits and EC keys off the approved NIST P-256/384/521 curves are rejected (a single shared floor gates both the OIDC id_token JWKS and SAML-certificate paths, fail-closed with a sanitized log) | Met |
Api/Crypto/SigningKeyStrength.cs, Api/Oidc/OidcIdTokenValidator.cs, Api/Saml/SamlCertificate.cs; Security Model § OpenID id_token validation
|
| V12 Secure outbound / SSRF | Both the avatar fetch and the OpenID backchannel (discovery / JWKS / token / userinfo) route every connection — incl. redirect hops — through one shared ConnectCallback that resolves the host and connects only to a non-blocked public IP, closing SSRF + DNS-rebinding | Met |
Api/Net/SsoHttp.cs (the shared hardened transport), Api/Net/IpAddressClassifier.cs, Api/Avatar/AvatarService.cs; the OIDC parity landed in #755
|
| V6/V7 — brute force | Optional per-client rate limiting that throttles, never permanently locks (sole-org availability); keyed on the Net client-IP classifier; fail-open on an unattributable client | Met |
Api/Shared/SsoRateLimitGate.cs, Api/RateLimit/SsoRateLimiter.cs; Security Model § Rate limiting
|
| BCP item | Status | Note |
|---|---|---|
| PKCE (S256) for the code flow | Met | Sent on every OIDC request; a downgrade (AS not advertising S256 under the require-PKCE setting) is detected. Api/Oidc/*; Security Model § PKCE downgrade detection
|
| Exact redirect-URI matching | Met | The redirect_uri is built from the canonical base URL (Host-header-independent), never reflected from the request; byte-exact echo. Api/Net/CanonicalBaseUrl.cs, Api/Oidc/OidcRedirectUriBuilder.cs
|
| RFC 9207 issuer identification / mix-up defense | Met | The authorization-response iss is required and validated against the expected provider when the AS advertises it. Api/Oidc/DiscoveryFacts.cs, OidcDiscoveryReader.cs; Security Model § authorization-response issuer
|
| No implicit / hybrid / ROPC | Met | Authorization-code flow only; no implicit, hybrid, or resource-owner-password grant is offered |
| PAR (Pushed Authorization Requests) | N/A (opt-out) | Not required of a confidential RP whose request is already integrity-protected by state/PKCE; would be a provider-side option, not a change to this RP |
| DPoP (RFC 9449) / sender-constrained access tokens | N/A by role | The plugin exchanges the code for an id_token and mints a Jellyfin session; it does not use the OAuth access token downstream, so there is no bearer access token to sender-constrain |
| JARM (signed authorization responses) | N/A by role | The authorization response carries only code/state/iss; the security-bearing artifact (the id_token) is itself signed and fully validated, so signing the authorization response adds nothing here |
Honestly recorded; each has a reasoned status and a tracking issue.
- Minimum signing-key strength floor (#733) — fixed. A shared floor now rejects an IdP signing key below the bar (RSA < 2048 bits, or an EC key off the approved NIST P-256/384/521 curves) on both the OIDC id_token JWKS path and the SAML-certificate path, fail-closed; closed, recorded here for completeness.
- Response-header completeness — Met, tracked for drift. The intermediate auth page and error pages carry the CSP + defensive headers; HSTS ownership is the reverse proxy's (documented). No open gap.
- IdP-error reflection (#708) — fixed (#709). IdP-supplied error text is no longer reflected unsanitized into the served page; closed, recorded here for completeness.
This is the maintainer's own mapping, produced with the AI-assisted process disclosed in the README. It claims no ASVS certification, no external audit, and no CRA/CE conformity (CRA position). It states only which controls the code implements, verified against the cited sources.
Repository · Issues · Releases · Security policy — report vulnerabilities privately, never in a public issue. Pages describe what is implemented today; if the wiki disagrees with the code, the code wins.
Getting started
- Installation
- Provider Setup
- Hardening & Options Reference
- Migrating from 9p4
- Troubleshooting
- Rollback
How it works
Security
- Security Model
- Security Conformance (ASVS / RFC 9700)
- Threat Model
- SSO-Only Login — design record
- Single Logout — design record
Standards & process (internal / maintainer)