Skip to content

Commit 04422ee

Browse files
authored
Merge pull request github#9378 from porcupineyhairs/goJwtSign
Golang : Add query to detect JWT signing vulnerabilities
2 parents 70c74bf + d5ac719 commit 04422ee

File tree

21 files changed

+2542
-0
lines changed

21 files changed

+2542
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
<overview>
4+
<p>
5+
A JSON Web Token (JWT) is used for authenticating and managing users in an application.
6+
</p>
7+
<p>
8+
Using a hard-coded secret key for signing JWT tokens in open source projects
9+
can leave the application using the token vulnerable to authentication bypasses.
10+
</p>
11+
12+
<p>
13+
A JWT token is safe for enforcing authentication and access control as long as it can't be forged by a malicious actor. However, when a project exposes this secret publicly, these seemingly unforgeable tokens can now be easily forged.
14+
Since the authentication as well as access control is typically enforced through these JWT tokens, an attacker armed with the secret can create a valid authentication token for any user and may even gain access to other privileged parts of the application.
15+
</p>
16+
17+
</overview>
18+
<recommendation>
19+
20+
<p>
21+
Generating a cryptograhically secure secret key during application initialization and using this generated key for future JWT signing requests can prevent this vulnerability.
22+
</p>
23+
24+
</recommendation>
25+
<example>
26+
27+
<p>
28+
The following code uses a hard-coded string as a secret for signing the tokens. In this case, an attacker can very easily forge a token by using the hard-coded secret.
29+
</p>
30+
31+
<sample src="HardcodedKeysBad.go" />
32+
33+
</example>
34+
<example>
35+
36+
<p>
37+
In the following case, the application uses a programatically generated string as a secret for signing the tokens. In this case, since the secret can't be predicted, the code is secure. A function like `GenerateCryptoString` can be run to generate a secure secret key at the time of application installation/initialization. This generated key can then be used for all future signing requests.
38+
</p>
39+
40+
<sample src="HardcodedKeysGood.go" />
41+
42+
</example>
43+
<references>
44+
<li>
45+
CVE-2022-0664:
46+
<a href="https://nvd.nist.gov/vuln/detail/CVE-2022-0664">Use of Hard-coded Cryptographic Key in Go github.com/gravitl/netmaker prior to 0.8.5,0.9.4,0.10.0,0.10.1. </a>
47+
</li>
48+
</references>
49+
50+
</qhelp>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @name Use of a hardcoded key for signing JWT
3+
* @description Using a fixed hardcoded key for signing JWT's can allow an attacker to compromise security.
4+
* @kind path-problem
5+
* @problem.severity error
6+
* @id go/hardcoded-key
7+
* @tags security
8+
* external/cwe/cwe-321
9+
*/
10+
11+
import go
12+
import HardcodedKeysLib
13+
import DataFlow::PathGraph
14+
15+
from HardcodedKeys::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
16+
where cfg.hasFlowPath(source, sink)
17+
select sink.getNode(), source, sink, "$@ is used to sign a JWT token.", source.getNode(),
18+
"Hardcoded String"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
mySigningKey := []byte("AllYourBase")
2+
3+
claims := &jwt.RegisteredClaims{
4+
ExpiresAt: jwt.NewNumericDate(time.Unix(1516239022, 0)),
5+
Issuer: "test",
6+
}
7+
8+
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
9+
ss, err := token.SignedString(mySigningKey)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
func GenerateCryptoString(n int) (string, error) {
2+
const chars = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
3+
ret := make([]byte, n)
4+
for i := range ret {
5+
num, err := crand.Int(crand.Reader, big.NewInt(int64(len(chars))))
6+
if err != nil {
7+
return "", err
8+
}
9+
ret[i] = chars[num.Int64()]
10+
}
11+
return string(ret), nil
12+
}
13+
14+
mySigningKey := GenerateCryptoString(64)
15+
16+
17+
claims := &jwt.RegisteredClaims{
18+
ExpiresAt: jwt.NewNumericDate(time.Unix(1516239022, 0)),
19+
Issuer: "test",
20+
}
21+
22+
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
23+
ss, err := token.SignedString(mySigningKey)

0 commit comments

Comments
 (0)