OSV 1.4.0 · github-reviewed · 修改于 2026-07-21 03:24
发布时间
2026-07-08 04:56
GitHub 审查时间
2026-07-08 04:56
NVD 发布时间
2026-07-16 02:16
源文件
advisories/github-reviewed/2026/07/GHSA-5rr4-8452-hf4v/GHSA-5rr4-8452-hf4v.json
Users are affected if all of the following are true:
@better-auth/sso at a version >= 0.1.0, < 1.6.11 on the stable line, or any 1.7.0-beta.x on the pre-release line.sso() plugin is added to their application's betterAuth({ plugins: [...] }) array.POST /sso/register (the plugin's default gate accepts any session).For the non-blind SSRF impact (full IAM credential or internal HTTP body exfiltration), no further configuration is required.
For the account takeover escalation, additionally:
sso({ trustEmailVerified: true, ... }).email overlaps with attacker-chosen domains.If developers do not enable the SSO plugin, their application is not affected.
Fix:
@better-auth/[email protected] or later.The @better-auth/sso plugin's POST /sso/register endpoint accepts attacker-controlled oidcConfig.userInfoEndpoint, tokenEndpoint, and jwksEndpoint URLs when skipDiscovery: true is set, persists them on the ssoProvider row without origin validation, then issues server-side fetches to those URLs during the OIDC callback. The fetched response body is reflected through the user profile, producing a non-blind SSRF reachable by any authenticated session. The same primitive exists on POST /sso/update-provider.
The schema field types accept bare strings: no .url() validator, no origin gate. The discovery branch (skipDiscovery: false) routes URLs through validateDiscoveryUrl; the skip-discovery branch persists them as-is. At callback time three fetch sites read the stored URLs: for the token endpoint, for the userInfo endpoint, and for the JWKS endpoint.
validateAuthorizationCodebetterFetchvalidateTokenWhen trustEmailVerified: true is configured, the attacker can escalate to account linking. A malicious userInfo response with emailVerified: true and a chosen email triggers OAuth auto-link against any pre-existing user row with that email, compounding the SSRF into account takeover.
Fixed in @better-auth/[email protected]. Provider registration (POST /sso/register with skipDiscovery: true) and every POST /sso/update-provider request now validate each supplied OIDC endpoint URL (authorizationEndpoint, tokenEndpoint, userInfoEndpoint, jwksEndpoint, discoveryEndpoint) at registration time. A URL is rejected unless it satisfies one of two conditions:
@better-auth/core/utils/host.isPublicRoutableHost gate. RFC 1918 private ranges, RFC 4193 unique-local addresses, link-local addresses (including the cloud-metadata IP 169.254.169.254), loopback, multicast, broadcast, and reserved ranges are rejected, along with cloud-metadata FQDNs.trustedOrigins configuration. This preserves the documented escape hatch for customers running internal IdPs intentionally on private networks.The schema also tightens from z.string() to z.url() on those fields, so malformed URLs fail at parse time rather than at fetch time. Deployments running internal IdPs that previously worked must add the IdP's origin to trustedOrigins to keep working after upgrade.
If developers cannot upgrade immediately:
sso({ providersLimit: 0 }). The limit is enforced before the schema branch, blocking every /sso/register regardless of skipDiscovery.POST /sso/register and POST /sso/update-provider at the edge, or restrict to a denylist of source IPs and a small admin user list.169.254.0.0/16, fe80::/10), and the cloud-metadata FQDN list at the firewall or VPC level. AWS users should additionally enforce IMDSv2 (HttpTokens: required).trustEmailVerified: false until upgrade. This caps the impact at non-blind SSRF and removes the account-takeover escalation, but does not stop the SSRF.trustEmailVerified: true): the attacker mints a malicious userInfo response asserting emailVerified: true for an arbitrary email, triggering OAuth auto-link against pre-existing user rows.Reported by Vaadata.