-
Notifications
You must be signed in to change notification settings - Fork 2k
Go: Improved JWT query, JWT decoding without verification #14075
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
68392e7
40ff16b
bc6a0fc
2136929
1e12a86
a96b001
da864bf
c78f390
8d47a7b
f0f60c3
aa127b1
7d73808
7d36c23
2579791
38b0ed8
82483a2
db9f74b
877605d
4499048
5e27323
8a3aa2c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,9 @@ | ||
| /** | ||
| * @name Decoding JWT with hardcoded key | ||
| * @description Decoding JWT Secrect with a Constant value lead to authentication or authorization bypass | ||
| * @description Decoding JWT Secret with a Constant value lead to authentication or authorization bypass | ||
| * @kind path-problem | ||
| * @problem.severity error | ||
| * @id go/hardcoded-key | ||
| * @id go/parse-jwt-with-hardcoded-key | ||
| * @tags security | ||
| * experimental | ||
| * external/cwe/cwe-321 | ||
|
|
@@ -12,10 +12,13 @@ | |
| import go | ||
| import semmle.go.security.JWT | ||
|
|
||
| module JwtConfig implements DataFlow::ConfigSig { | ||
| module JwtPaseWithConstantKeyConfig implements DataFlow::ConfigSig { | ||
| predicate isSource(DataFlow::Node source) { source.asExpr() instanceof StringLit } | ||
|
|
||
| predicate isSink(DataFlow::Node sink) { | ||
| // first part is the JWT Parsing Functions that get a func type as an argument | ||
| // Find a node that has flow to a key Function argument | ||
| // then find the first result node of this Function which is the secret key | ||
| exists(FuncDef fd, DataFlow::Node n, DataFlow::ResultNode rn | | ||
| GolangJwtKeyFunc::flow(n, _) and fd = n.asExpr() | ||
| | | ||
|
|
@@ -31,6 +34,9 @@ module JwtConfig implements DataFlow::ConfigSig { | |
| rn.getRoot() = fd.getFuncDecl() and | ||
| rn.getIndex() = 0 | ||
| ) | ||
|
Comment on lines
+22
to
+31
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think If I merge them it can be much longer that it is. |
||
| or | ||
| // second part is the JWT Parsing Functions that get a string or byte as an argument | ||
| sink = any(JwtParse jp).getKeyArg() | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -42,24 +48,17 @@ module GolangJwtKeyFuncConfig implements DataFlow::ConfigSig { | |
| } | ||
|
|
||
| predicate isSink(DataFlow::Node sink) { | ||
| sink = | ||
| [ | ||
| any(GolangJwtParse parseWithClaims).getKeyFuncArg(), | ||
| any(GolangJwtParseWithClaims parseWithClaims).getKeyFuncArg(), | ||
| any(GolangJwtParseFromRequest parseWithClaims).getKeyFuncArg(), | ||
| any(GolangJwtParseFromRequestWithClaims parseWithClaims).getKeyFuncArg(), | ||
| any(GoJoseClaims parseWithClaims).getKeyFuncArg(), | ||
| ] | ||
| sink = any(JwtParseWithKeyFunction parseJWT).getKeyFuncArg() | ||
|
|
||
| } | ||
| } | ||
|
|
||
| module Jwt = TaintTracking::Global<JwtConfig>; | ||
| module JwtPaseWithConstantKey = TaintTracking::Global<JwtPaseWithConstantKeyConfig>; | ||
|
am0o0 marked this conversation as resolved.
Outdated
|
||
|
|
||
| module GolangJwtKeyFunc = TaintTracking::Global<GolangJwtKeyFuncConfig>; | ||
|
|
||
| import Jwt::PathGraph | ||
| import JwtPaseWithConstantKey::PathGraph | ||
|
|
||
| from Jwt::PathNode source, Jwt::PathNode sink | ||
| where Jwt::flowPath(source, sink) | ||
| from JwtPaseWithConstantKey::PathNode source, JwtPaseWithConstantKey::PathNode sink | ||
| where JwtPaseWithConstantKey::flowPath(source, sink) | ||
| select sink.getNode(), source, sink, "This $@.", source.getNode(), | ||
| "Constant Key is used as JWT Secret key" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,16 @@ | ||
| edges | ||
| | golang-jwt-v5/golang-jwt-v5.go:19:14:19:34 | type conversion | golang-jwt-v5/golang-jwt-v5.go:37:9:37:14 | JwtKey | | ||
| | golang-jwt-v5/golang-jwt-v5.go:19:21:19:33 | "AllYourBase" | golang-jwt-v5/golang-jwt-v5.go:19:14:19:34 | type conversion | | ||
| | go-jose.v3.go:11:14:11:34 | type conversion | go-jose.v3.go:23:32:23:37 | JwtKey | | ||
| | go-jose.v3.go:11:21:11:33 | "AllYourBase" | go-jose.v3.go:11:14:11:34 | type conversion | | ||
| | golang-jwt-v5.go:19:15:19:35 | type conversion | golang-jwt-v5.go:27:9:27:15 | JwtKey1 | | ||
| | golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | golang-jwt-v5.go:19:15:19:35 | type conversion | | ||
| nodes | ||
| | golang-jwt-v5/golang-jwt-v5.go:19:14:19:34 | type conversion | semmle.label | type conversion | | ||
| | golang-jwt-v5/golang-jwt-v5.go:19:21:19:33 | "AllYourBase" | semmle.label | "AllYourBase" | | ||
| | golang-jwt-v5/golang-jwt-v5.go:37:9:37:14 | JwtKey | semmle.label | JwtKey | | ||
| | go-jose.v3.go:11:14:11:34 | type conversion | semmle.label | type conversion | | ||
| | go-jose.v3.go:11:21:11:33 | "AllYourBase" | semmle.label | "AllYourBase" | | ||
| | go-jose.v3.go:23:32:23:37 | JwtKey | semmle.label | JwtKey | | ||
| | golang-jwt-v5.go:19:15:19:35 | type conversion | semmle.label | type conversion | | ||
| | golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | semmle.label | "AllYourBase" | | ||
| | golang-jwt-v5.go:27:9:27:15 | JwtKey1 | semmle.label | JwtKey1 | | ||
| subpaths | ||
| #select | ||
| | golang-jwt-v5/golang-jwt-v5.go:37:9:37:14 | JwtKey | golang-jwt-v5/golang-jwt-v5.go:19:21:19:33 | "AllYourBase" | golang-jwt-v5/golang-jwt-v5.go:37:9:37:14 | JwtKey | This $@. | golang-jwt-v5/golang-jwt-v5.go:19:21:19:33 | "AllYourBase" | Constant Key is used as JWT Secret key | | ||
| | go-jose.v3.go:23:32:23:37 | JwtKey | go-jose.v3.go:11:21:11:33 | "AllYourBase" | go-jose.v3.go:23:32:23:37 | JwtKey | This $@. | go-jose.v3.go:11:21:11:33 | "AllYourBase" | Constant Key is used as JWT Secret key | | ||
| | golang-jwt-v5.go:27:9:27:15 | JwtKey1 | golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | golang-jwt-v5.go:27:9:27:15 | JwtKey1 | This $@. | golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | Constant Key is used as JWT Secret key | |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package jwt | ||
|
|
||
| //go:generate depstubber -vendor github.com/go-jose/go-jose/v3/jwt JSONWebToken ParseSigned | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "github.com/go-jose/go-jose/v3/jwt" | ||
| "net/http" | ||
| ) | ||
|
|
||
| var JwtKey = []byte("AllYourBase") | ||
|
|
||
| func main2(r *http.Request) { | ||
| // NOT OK | ||
|
am0o0 marked this conversation as resolved.
Outdated
|
||
| signedToken := r.URL.Query().Get("signedToken") | ||
| verifyJWT(signedToken) | ||
| } | ||
|
|
||
| func verifyJWT(signedToken string) { | ||
| fmt.Println("verifying JWT") | ||
| DecodedToken, _ := jwt.ParseSigned(signedToken) | ||
| out := CustomerInfo{} | ||
| if err := DecodedToken.Claims(JwtKey, &out); err != nil { | ||
| panic(err) | ||
| } | ||
| fmt.Printf("%v\n", out) | ||
| } | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| module main | ||
|
|
||
| go 1.18 | ||
| go 1.21 | ||
|
|
||
| require ( | ||
| github.com/gin-gonic/gin v1.9.1 | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.