Skip to content

Commit 1f0f8e5

Browse files
committed
changed to just detect the .net usage, updated tests, added qhelp
1 parent 07dfa9b commit 1f0f8e5

6 files changed

Lines changed: 77 additions & 50 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
7+
<p>Using the <code>"none"</code> algorithm when creating a JWT token with
8+
<code>JwtSecurityTokenHandler</code> disables signature verification. This allows attackers to
9+
forge arbitrary tokens that will be accepted as valid, potentially leading to authentication
10+
bypass and privilege escalation.</p>
11+
12+
</overview>
13+
<recommendation>
14+
15+
<p>Always use a secure signing algorithm such as <code>HS256</code>, <code>RS256</code>, or
16+
<code>ES256</code> when creating JWT tokens. Ensure that tokens are signed with a strong secret
17+
or key pair.</p>
18+
19+
</recommendation>
20+
<example>
21+
22+
<p>In this example, a JWT token is created using the <code>"none"</code> algorithm, which produces
23+
an unsigned token:</p>
24+
25+
<sample src="examples/JwtNoneAlgorithmBad.ps1" />
26+
27+
<p>The fix is to use a secure algorithm instead:</p>
28+
29+
<sample src="examples/JwtNoneAlgorithmGood.ps1" />
30+
31+
</example>
32+
<references>
33+
34+
<li>
35+
RFC 7518:
36+
<a href="https://datatracker.ietf.org/doc/html/rfc7518#section-3.6">JSON Web Algorithms - Unsecured JWS</a>.
37+
</li>
38+
39+
<li>
40+
CWE-347:
41+
<a href="https://cwe.mitre.org/data/definitions/347.html">Improper Verification of Cryptographic Signature</a>.
42+
</li>
43+
44+
</references>
45+
</qhelp>

powershell/ql/src/queries/security/cwe-347/JwtNoneAlgorithm.ql

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,61 +5,33 @@
55
* @kind problem
66
* @problem.severity error
77
* @security-severity 9.8
8-
* @precision medium
9-
* @id powershell/microsoft/security/jwt-none-algorithm
8+
* @precision high
9+
* @id powershell/jwt-none-algorithm
1010
* @tags security
1111
* external/cwe/cwe-347
1212
*/
1313

1414
import powershell
1515
import semmle.code.powershell.dataflow.DataFlow
1616

17-
// NOTE: PowerShell is a beta language for CodeQL and has no built-in JWT library modeling.
18-
// This query detects common patterns of JWT "none" algorithm usage in PowerShell modules
19-
// such as PSJwt, JWTPS, and direct .NET JWT library calls.
20-
// Coverage may be limited for less common JWT libraries.
21-
22-
/**
23-
* A string literal containing "none" used in a JWT-related cmdlet call.
24-
*/
25-
class JwtNoneAlgorithmLiteral extends StringConstExpr {
26-
JwtNoneAlgorithmLiteral() {
27-
this.getValueString().toLowerCase() = "none" and
28-
exists(CmdCall call |
29-
call.matchesName([
30-
"New-Jwt", "New-JsonWebToken", "ConvertTo-Jwt",
31-
"New-JWTToken", "ConvertTo-JWTToken"
32-
]) and
33-
this = call.getAnArgument()
34-
)
35-
}
36-
}
37-
3817
/**
39-
* A string literal "none" passed as an algorithm argument to .NET JWT methods.
18+
* A string literal "none" passed as an algorithm argument to .NET JWT methods
19+
* on a JwtSecurityTokenHandler instance.
4020
*/
4121
class JwtNoneInDotNetCall extends StringConstExpr {
4222
JwtNoneInDotNetCall() {
4323
this.getValueString().toLowerCase() = "none" and
44-
exists(InvokeMemberExpr call |
45-
call.matchesName([
46-
"CreateToken", "WriteToken", "CreateJwtSecurityToken", "CreateEncodedJwt"
47-
]) and
48-
this = call.getAnArgument()
24+
exists(DataFlow::CallNode cn, DataFlow::ObjectCreationNode ocn |
25+
cn.getQualifier().getALocalSource() = ocn and
26+
ocn.getConstructedTypeNode().asExpr().getExpr().(TypeNameExpr).hasQualifiedName("system.identitymodel.tokens.jwt", "jwtsecuritytokenhandler") and
27+
cn.getLowerCaseName() in [
28+
"createtoken", "writetoken", "createjwtsecuritytoken", "createencodedjwt"
29+
] and
30+
cn.getAnArgument().asExpr().getExpr() = this
4931
)
5032
}
5133
}
5234

53-
from StringConstExpr noneAlg, string msg
54-
where
55-
(
56-
noneAlg instanceof JwtNoneAlgorithmLiteral and
57-
msg = "JWT token created with 'none' algorithm, disabling signature verification."
58-
)
59-
or
60-
(
61-
noneAlg instanceof JwtNoneInDotNetCall and
62-
msg =
63-
"JWT token created with 'none' algorithm via .NET API, disabling signature verification."
64-
)
65-
select noneAlg, msg
35+
from JwtNoneInDotNetCall noneAlg
36+
select noneAlg,
37+
"JWT token created with 'none' algorithm via .NET API, disabling signature verification."
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
$handler = [System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler]::new()
2+
3+
# BAD: Creating a JWT token with the "none" algorithm disables signature verification.
4+
$token = $handler.CreateToken("none")
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
$handler = [System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler]::new()
2+
3+
# GOOD: Creating a JWT token with a secure algorithm.
4+
$token = $handler.CreateToken("HS256")
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
| test.ps1:6:29:6:34 | none | JWT token created with 'none' algorithm, disabling signature verification. |
1+
| test.ps1:7:31:7:36 | none | JWT token created with 'none' algorithm via .NET API, disabling signature verification. |
2+
| test.ps1:10:36:10:41 | none | JWT token created with 'none' algorithm via .NET API, disabling signature verification. |

powershell/ql/test/query-tests/security/cwe-347/JwtNoneAlgorithm/test.ps1

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22
# ========== TRUE POSITIVES (should trigger alert) ==================
33
# ===================================================================
44

5-
# --- Case 1: PSJwt module with "none" algorithm ---
6-
$token = New-Jwt -Algorithm "none" -Payload @{sub="user"} # BAD
5+
# --- Case 1: .NET JwtSecurityTokenHandler.CreateToken with "none" ---
6+
$handler = [System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler]::new()
7+
$token = $handler.CreateToken("none") # BAD
8+
9+
# --- Case 2: .NET JwtSecurityTokenHandler.CreateEncodedJwt with "none" ---
10+
$token = $handler.CreateEncodedJwt("none") # BAD
711

812
# ===================================================================
913
# ========== TRUE NEGATIVES (should NOT trigger alert) ==============
1014
# ===================================================================
1115

12-
# --- Safe: JWT with HS256 ---
13-
$token = New-Jwt -Algorithm "HS256" -Payload @{sub="user"} -Secret $key # GOOD
14-
15-
# --- Safe: Unrelated string "none" ---
16-
$value = "none" # GOOD
16+
# --- Safe: .NET CreateToken with HS256 ---
17+
$token = $handler.CreateToken("HS256") # GOOD

0 commit comments

Comments
 (0)