{"id":"GHSA-g2qj-prgh-4g9r","revision":null,"records":[{"revision":"3d7f5a2f1a3f491fae6060a700700536","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-34969","aliases":[],"title":"Nhost Leaks the Refresh Token via URL Query Parameter in OAuth Provider Callback","description":"Nhost is an open source Firebase alternative with GraphQL. Prior to 0.48.0, the auth service's OAuth provider callback flow places the refresh token directly into the redirect URL as a query parameter. Refresh tokens in URLs are logged in browser history, server access logs, HTTP Referer headers, and proxy/CDN logs. Note that the refresh token is one-time use and all of these leak vectors are on owned infrastructure or services integrated by the application developer. This vulnerability is fixed in 0.48.0.","state":0,"publishedAt":"2026-04-06T16:01:10.174+00:00","modifiedAt":"2026-04-07T16:00:25.296+00:00","affected":[{"ecosystem":"Vendor product","name":"nhost","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"nhost\",\n                    \"product\": \"nhost\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 0.48.0\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 4.0","vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:N/SA:N","score":2.3,"severity":"LOW"}],"references":["https://github.com/nhost/nhost/security/advisories/GHSA-g2qj-prgh-4g9r"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-34969","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-34969","identifiers":["CVE-2026-34969"]},"retained":false,"historicalAffected":[]},{"revision":"5f33b53ce5f54ec8bd25b3b29f0f7b35","observedAt":"2026-10-25T08:17:35.884+00:00","statement":{"source":"nvd","id":"CVE-2026-34969","aliases":[],"title":"Nhost is an open source Firebase alternative with GraphQL. Prior to 0.48.0, the auth service's OAuth provider callback flow places the refresh token directly into the redirect URL as a query parameter. Refresh tokens in URLs are logged in b…","description":"Nhost is an open source Firebase alternative with GraphQL. Prior to 0.48.0, the auth service's OAuth provider callback flow places the refresh token directly into the redirect URL as a query parameter. Refresh tokens in URLs are logged in browser history, server access logs, HTTP Referer headers, and proxy/CDN logs. Note that the refresh token is one-time use and all of these leak vectors are on owned infrastructure or services integrated by the application developer. This vulnerability is fixed in 0.48.0.","state":0,"publishedAt":"2026-04-06T16:16:38.457+00:00","modifiedAt":"2026-10-23T23:10:00.157+00:00","affected":[],"metrics":[{"system":"CVSS 4.0 · security-advisories@github.com · Secondary","vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","score":2.3,"severity":"LOW"},{"system":"CVSS 3.1 · nvd@nist.gov · Primary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","score":7.5,"severity":"HIGH"}],"references":["https://github.com/nhost/nhost/security/advisories/GHSA-g2qj-prgh-4g9r"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-34969","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Analyzed","weaknesses":["CWE-200","CWE-598"],"productCriteria":["cpe:2.3:a:nhost:nhost\\/auth:*:*:*:*:*:*:*:*"],"sourceDates":{"published":"2026-04-06T16:16:38.457","lastModified":"2026-10-23T23:10:00.157"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-34969","identifiers":["CVE-2026-34969"]},"retained":false,"historicalAffected":[]},{"revision":"6e72a9525b8845bebefdded2b764c20e","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GHSA-g2qj-prgh-4g9r","aliases":["CVE-2026-34969","GO-2026-5388"],"title":"Nhost Leaks Refresh Tokens via URL Query Parameter in OAuth Provider Callback","description":"# Refresh Token Leaked via URL Query Parameter in OAuth Provider Callback\n\n## Summary\n\nThe auth service's OAuth provider callback flow places the refresh token directly into the redirect URL as a query parameter. Refresh tokens in URLs are logged in browser history, server access logs, HTTP Referer headers, and proxy/CDN logs.\n\nNote that the refresh token is one-time use and all of these leak vectors are on owned infrastructure or services integrated by the application developer.\n\n## Affected Component\n\n- **Repository**: `github.com/nhost/nhost`\n- **Service**: `services/auth`\n- **File**: `services/auth/go/controller/sign_in_provider_callback_get.go`\n- **Function**: `signinProviderProviderCallback` (lines 257-261)\n\n## Root Cause\n\nIn `sign_in_provider_callback_get.go:257-261`, after successful OAuth sign-in, the refresh token is appended as a URL query parameter:\n\n```go\nif session != nil {\n    values := redirectTo.Query()\n    values.Add(\"refreshToken\", session.RefreshToken)\n    redirectTo.RawQuery = values.Encode()\n}\n```\n\nThis results in a redirect like:\n```\nHTTP/1.1 302 Found\nLocation: https://myapp.com/callback?refreshToken=a1b2c3d4-e5f6-7890-abcd-ef1234567890\n```\n\n## Proof of Concept\n\n### Step 1: Initiate OAuth login\n```\nGET /signin/provider/github?redirectTo=https://myapp.com/callback\n```\n\n### Step 2: Complete OAuth flow with provider\n\n### Step 3: Auth service redirects with token in URL\n```\nHTTP/1.1 302 Found\nLocation: https://myapp.com/callback?refreshToken=a1b2c3d4-e5f6-7890-abcd-ef1234567890\n```\n\n### Step 4: Token is now visible in owned infrastructure and services:\n\n**Browser History:**\n```\n# User's browser history now contains the refresh token\n```\n\n**HTTP Referer Header:**\n```\n# If the callback page loads ANY external resource (image, script, etc.):\nGET /resource.js HTTP/1.1\nHost: cdn.example.com\nReferer: https://myapp.com/callback?refreshToken=a1b2c3d4-e5f6-...\n# Note: modern browsers default to strict-origin-when-cross-origin policy,\n# which strips query parameters from cross-origin Referer headers.\n# Additionally, the Referer is only sent to services integrated by the\n# application developer (analytics, CDNs, etc.), not arbitrary third parties.\n```\n\n**Server Access Logs:**\n```\n# Reverse proxy, CDN, or load balancer logs on owned infrastructure:\n2026-03-08 12:00:00 GET /callback?refreshToken=a1b2c3d4-e5f6-... 200\n```\n\n### Step 5: Attacker uses stolen refresh token\n```bash\n# Exchange stolen refresh token for new access token\ncurl -X POST https://auth.nhost.run/v1/token \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"refreshToken\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\"}'\n\n# Note: refresh tokens are one-time use, so this only works if the\n# legitimate client has not already consumed the token and if the attacker has\n# compromised your infrastructure to get access to this information\n```\n\n## Impact\n\n1. **Session Hijacking**: Anyone who obtains the token before it is consumed by the legitimate client can generate new access tokens, though the refresh token is one-time use and cannot be reused after consumption.\n\n2. **Leak Vectors**: URL query parameters are visible in owned infrastructure and integrated services:\n   - Browser history (local access)\n   - HTTP Referer headers (mitigated by modern browser default referrer policies; only sent to developer-integrated services)\n   - Server access logs (owned infrastructure)\n   - Proxy/CDN/WAF logs (owned infrastructure)\n\n3. **Affects All OAuth Providers**: Every OAuth provider flow (GitHub, Google, Apple, etc.) goes through the same callback handler.\n\n## Fix\n\nImplemented PKCE (Proof Key for Code Exchange) for the OAuth flow. With PKCE, the authorization code cannot be exchanged without the `code_verifier` that only the original client possesses, preventing token misuse even if the URL is logged.\n\nSee: https://docs.nhost.io/products/auth/pkce/\n\n## Resources\n\n- OWASP: Session Management - Token Transport: \"Session tokens should not be transported in the URL\"\n- RFC 6749 Section 10.3: \"Access tokens and refresh tokens MUST NOT be included in the redirect URI\"\n- CWE-598: Use of GET Request Method With Sensitive Query Strings\n- CWE-200: Exposure of Sensitive Information to an Unauthorized Actor","state":0,"publishedAt":"2026-04-01T23:36:10+00:00","modifiedAt":"2026-06-25T19:56:06.5308121+00:00","affected":[{"ecosystem":"Go","name":"github.com/nhost/nhost","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"0.0.0-20260330133707-294954e0fc3a"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","score":null,"severity":null},{"system":"CVSS_V4","vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:N/SA:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"LOW"}],"references":["https://github.com/nhost/nhost/security/advisories/GHSA-g2qj-prgh-4g9r","https://nvd.nist.gov/vuln/detail/CVE-2026-34969","https://docs.nhost.io/products/auth/pkce","https://github.com/nhost/nhost"],"sourceUrl":"https://github.com/advisories/GHSA-g2qj-prgh-4g9r","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-g2qj-prgh-4g9r","identifiers":["GHSA-g2qj-prgh-4g9r","CVE-2026-34969","GO-2026-5388"]},"retained":false,"historicalAffected":[]},{"revision":"281b38f28ec04957805dd9cbe550e377","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GO-2026-5388","aliases":["CVE-2026-34969","GHSA-g2qj-prgh-4g9r"],"title":"Nhost Leaks Refresh Tokens via URL Query Parameter in OAuth Provider Callback in github.com/nhost/nhost","description":"Nhost Leaks Refresh Tokens via URL Query Parameter in OAuth Provider Callback in github.com/nhost/nhost","state":0,"publishedAt":"2026-06-25T18:43:19+00:00","modifiedAt":"2026-06-25T19:45:37.5536241+00:00","affected":[{"ecosystem":"Go","name":"github.com/nhost/nhost","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"0.0.0-20260330133707-294954e0fc3a"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[],"references":["https://github.com/nhost/nhost/security/advisories/GHSA-g2qj-prgh-4g9r","https://nvd.nist.gov/vuln/detail/CVE-2026-34969","https://docs.nhost.io/products/auth/pkce"],"sourceUrl":"https://osv.dev/vulnerability/GO-2026-5388","license":"OSV · upstream source license applies","key":"osv:GO-2026-5388","identifiers":["GO-2026-5388","CVE-2026-34969","GHSA-g2qj-prgh-4g9r"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-10-26T11:36:23.2920201+00:00","complete":true,"records":1756,"lastAttemptAt":"2026-10-26T11:36:23.2920201+00:00","lastSuccessAt":"2026-10-26T11:36:23.5142731+00:00","error":null,"lastVerifiedAt":"2026-10-26T11:36:23.5142748+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-5ba1945314e042c2ced14b33f3680fa71692812d925544312b218f5eb8a26e71-e7962156c388e029d9e5dbbcda79fbd40ade61bdd41d51546adb4696b3e29228","complete":true,"records":414161,"lastAttemptAt":"2026-10-26T11:36:10.3293998+00:00","lastSuccessAt":"2026-10-26T11:36:10.2537359+00:00","error":null,"lastVerifiedAt":"2026-10-26T11:36:10.3293998+00:00","snapshotPublishedAt":"2026-10-26T11:28:55+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-43321610382942b8ad8c85917eab1546","complete":false,"records":4735,"lastAttemptAt":"2026-10-26T11:36:27.570174+00:00","lastSuccessAt":"2026-10-24T23:18:49.862+00:00","error":"InvalidDataException","lastVerifiedAt":"2026-10-24T23:18:49.862+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":4764,"expectedIdentifiers":13477},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-10-26T11:36:23.2920201+00:00","complete":true,"records":1093,"lastAttemptAt":"2026-10-26T11:36:23.2920201+00:00","lastSuccessAt":"2026-10-26T11:36:23.6271598+00:00","error":null,"lastVerifiedAt":"2026-10-26T11:36:23.6271612+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-114096430e32489f93a3ae96f397b2db","complete":true,"records":13416,"lastAttemptAt":"2026-10-26T11:36:25.3338531+00:00","lastSuccessAt":"2026-10-26T11:36:25.326+00:00","error":null,"lastVerifiedAt":"2026-10-26T11:36:25.326+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13477,"expectedIdentifiers":13477},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-2375a4e597279abf92e8747e140fe8da9d421877cd49cae3ac69a2cb78469b2c","complete":true,"records":9779,"lastAttemptAt":"2026-10-26T11:36:10.7420905+00:00","lastSuccessAt":"2026-10-24T13:19:53.1346702+00:00","error":null,"lastVerifiedAt":"2026-10-26T11:36:10.7420905+00:00","snapshotPublishedAt":"2026-10-24T03:03:12+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-16ad89dfea13ab9e9b0108e63a759d63bd4559cc68b58491107d62ee35bbbaf2","complete":true,"records":1965,"lastAttemptAt":"2026-10-26T11:36:10.517478+00:00","lastSuccessAt":"2026-10-23T16:01:26.4487605+00:00","error":null,"lastVerifiedAt":"2026-10-26T11:36:10.517478+00:00","snapshotPublishedAt":"2026-10-21T12:18:07+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-7d18ad8657bc2f95d55948e9e16b3c82a8ac06830df5ec8d356048b8420b72c3","complete":true,"records":2972,"lastAttemptAt":"2026-10-26T11:36:23.2053293+00:00","lastSuccessAt":"2026-10-26T11:36:23.0131888+00:00","error":null,"lastVerifiedAt":"2026-10-26T11:36:23.2053293+00:00","snapshotPublishedAt":"2026-10-26T11:33:16+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-654e90fb44abcd729ddecf56d54c1b46ce87ca3181850e8bc65d5c8befceb659","complete":true,"records":230805,"lastAttemptAt":"2026-10-26T11:36:10.6367182+00:00","lastSuccessAt":"2026-10-26T08:22:30.1390577+00:00","error":null,"lastVerifiedAt":"2026-10-26T11:36:10.6367182+00:00","snapshotPublishedAt":"2026-10-26T07:48:28+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null}]}