mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Merge pull request #5911 from atorralba/atorralba/promote-missing-jwt-signature-check
Java: Promote Missing JWT signature check query from experimental
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
lgtm,codescanning
|
||||||
|
* The query "Missing JWT signature check" (`java/missing-jwt-signature-check`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @intrigus-lgtm](https://github.com/github/codeql/pull/5597).
|
||||||
@@ -25,9 +25,9 @@ by overriding the <code>onPlaintextJws</code> or <code>onClaimsJws</code> of <co
|
|||||||
<example>
|
<example>
|
||||||
|
|
||||||
<p>The following example shows four cases where a signing key is set for a parser.
|
<p>The following example shows four cases where a signing key is set for a parser.
|
||||||
In the first bad case the <code>parse</code> method is used which will not validate the signature.
|
In the first 'BAD' case the <code>parse</code> method is used, which will not validate the signature.
|
||||||
The second bad case uses a <code>JwtHandlerAdapter</code> where the <code>onPlaintextJwt</code> method is overriden so it will not validate the signature.
|
The second 'BAD' case uses a <code>JwtHandlerAdapter</code> where the <code>onPlaintextJwt</code> method is overriden, so it will not validate the signature.
|
||||||
The third and fourth good cases use <code>parseClaimsJws</code> method or override the <code>onPlaintextJws</code> method.
|
The third and fourth 'GOOD' cases use <code>parseClaimsJws</code> method or override the <code>onPlaintextJws</code> method.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<sample src="MissingJWTSignatureCheck.java" />
|
<sample src="MissingJWTSignatureCheck.java" />
|
||||||
20
java/ql/src/Security/CWE/CWE-347/MissingJWTSignatureCheck.ql
Normal file
20
java/ql/src/Security/CWE/CWE-347/MissingJWTSignatureCheck.ql
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @name Missing JWT signature check
|
||||||
|
* @description Failing to check the Json Web Token (JWT) signature may allow an attacker to forge their own tokens.
|
||||||
|
* @kind path-problem
|
||||||
|
* @problem.severity error
|
||||||
|
* @security-severity 7.8
|
||||||
|
* @precision high
|
||||||
|
* @id java/missing-jwt-signature-check
|
||||||
|
* @tags security
|
||||||
|
* external/cwe/cwe-347
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java
|
||||||
|
import semmle.code.java.security.MissingJWTSignatureCheckQuery
|
||||||
|
import DataFlow::PathGraph
|
||||||
|
|
||||||
|
from DataFlow::PathNode source, DataFlow::PathNode sink, MissingJwtSignatureCheckConf conf
|
||||||
|
where conf.hasFlowPath(source, sink)
|
||||||
|
select sink.getNode(), source, sink, "A signing key is set $@, but the signature is not verified.",
|
||||||
|
source.getNode(), "here"
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name Missing JWT signature check
|
|
||||||
* @description Not checking the JWT signature allows an attacker to forge their own tokens.
|
|
||||||
* @kind problem
|
|
||||||
* @problem.severity error
|
|
||||||
* @precision high
|
|
||||||
* @id java/missing-jwt-signature-check
|
|
||||||
* @tags security
|
|
||||||
* external/cwe/cwe-347
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java
|
|
||||||
import semmle.code.java.dataflow.DataFlow
|
|
||||||
|
|
||||||
/** The interface `io.jsonwebtoken.JwtParser`. */
|
|
||||||
class TypeJwtParser extends Interface {
|
|
||||||
TypeJwtParser() { this.hasQualifiedName("io.jsonwebtoken", "JwtParser") }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The interface `io.jsonwebtoken.JwtParser` or a type derived from it. */
|
|
||||||
class TypeDerivedJwtParser extends RefType {
|
|
||||||
TypeDerivedJwtParser() { this.getASourceSupertype*() instanceof TypeJwtParser }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The interface `io.jsonwebtoken.JwtParserBuilder`. */
|
|
||||||
class TypeJwtParserBuilder extends Interface {
|
|
||||||
TypeJwtParserBuilder() { this.hasQualifiedName("io.jsonwebtoken", "JwtParserBuilder") }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The interface `io.jsonwebtoken.JwtHandler`. */
|
|
||||||
class TypeJwtHandler extends Interface {
|
|
||||||
TypeJwtHandler() { this.hasQualifiedName("io.jsonwebtoken", "JwtHandler") }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The class `io.jsonwebtoken.JwtHandlerAdapter`. */
|
|
||||||
class TypeJwtHandlerAdapter extends Class {
|
|
||||||
TypeJwtHandlerAdapter() { this.hasQualifiedName("io.jsonwebtoken", "JwtHandlerAdapter") }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The `parse(token, handler)` method defined in `JwtParser`. */
|
|
||||||
private class JwtParserParseHandlerMethod extends Method {
|
|
||||||
JwtParserParseHandlerMethod() {
|
|
||||||
this.hasName("parse") and
|
|
||||||
this.getDeclaringType() instanceof TypeJwtParser and
|
|
||||||
this.getNumberOfParameters() = 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The `parse(token)`, `parseClaimsJwt(token)` and `parsePlaintextJwt(token)` methods defined in `JwtParser`. */
|
|
||||||
private class JwtParserInsecureParseMethod extends Method {
|
|
||||||
JwtParserInsecureParseMethod() {
|
|
||||||
this.hasName(["parse", "parseClaimsJwt", "parsePlaintextJwt"]) and
|
|
||||||
this.getNumberOfParameters() = 1 and
|
|
||||||
this.getDeclaringType() instanceof TypeJwtParser
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The `on(Claims|Plaintext)Jwt` methods defined in `JwtHandler`. */
|
|
||||||
private class JwtHandlerOnJwtMethod extends Method {
|
|
||||||
JwtHandlerOnJwtMethod() {
|
|
||||||
this.hasName(["onClaimsJwt", "onPlaintextJwt"]) and
|
|
||||||
this.getNumberOfParameters() = 1 and
|
|
||||||
this.getDeclaringType() instanceof TypeJwtHandler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The `on(Claims|Plaintext)Jwt` methods defined in `JwtHandlerAdapter`. */
|
|
||||||
private class JwtHandlerAdapterOnJwtMethod extends Method {
|
|
||||||
JwtHandlerAdapterOnJwtMethod() {
|
|
||||||
this.hasName(["onClaimsJwt", "onPlaintextJwt"]) and
|
|
||||||
this.getNumberOfParameters() = 1 and
|
|
||||||
this.getDeclaringType() instanceof TypeJwtHandlerAdapter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `parseHandlerExpr` is an insecure `JwtHandler`.
|
|
||||||
* That is, it overrides a method from `JwtHandlerOnJwtMethod` and the override is not defined on `JwtHandlerAdapter`.
|
|
||||||
* `JwtHandlerAdapter`'s overrides are safe since they always throw an exception.
|
|
||||||
*/
|
|
||||||
private predicate isInsecureJwtHandler(Expr parseHandlerExpr) {
|
|
||||||
exists(RefType t |
|
|
||||||
parseHandlerExpr.getType() = t and
|
|
||||||
t.getASourceSupertype*() instanceof TypeJwtHandler and
|
|
||||||
exists(Method m |
|
|
||||||
m = t.getAMethod() and
|
|
||||||
m.getASourceOverriddenMethod+() instanceof JwtHandlerOnJwtMethod and
|
|
||||||
not m.getSourceDeclaration() instanceof JwtHandlerAdapterOnJwtMethod
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An access to an insecure parsing method.
|
|
||||||
* That is, either a call to a `parse(token)`, `parseClaimsJwt(token)` or `parsePlaintextJwt(token)` method or
|
|
||||||
* a call to a `parse(token, handler)` method where the `handler` is considered insecure.
|
|
||||||
*/
|
|
||||||
private class JwtParserInsecureParseMethodAccess extends MethodAccess {
|
|
||||||
JwtParserInsecureParseMethodAccess() {
|
|
||||||
this.getMethod().getASourceOverriddenMethod*() instanceof JwtParserInsecureParseMethod
|
|
||||||
or
|
|
||||||
this.getMethod().getASourceOverriddenMethod*() instanceof JwtParserParseHandlerMethod and
|
|
||||||
isInsecureJwtHandler(this.getArgument(1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `signingMa` directly or indirectly sets a signing key for `expr`, which is a `JwtParser`.
|
|
||||||
* The `setSigningKey` and `setSigningKeyResolver` methods set a signing key for a `JwtParser`.
|
|
||||||
* Directly means code like this:
|
|
||||||
* ```java
|
|
||||||
* Jwts.parser().setSigningKey(key).parse(token);
|
|
||||||
* ```
|
|
||||||
* Here the signing key is set directly on a `JwtParser`.
|
|
||||||
* Indirectly means code like this:
|
|
||||||
* ```java
|
|
||||||
* Jwts.parserBuilder().setSigningKey(key).build().parse(token);
|
|
||||||
* ```
|
|
||||||
* In this case, the signing key is set on a `JwtParserBuilder` indirectly setting the key of `JwtParser` that is created by the call to `build`.
|
|
||||||
*/
|
|
||||||
private predicate isSigningKeySetter(Expr expr, MethodAccess signingMa) {
|
|
||||||
any(SigningToInsecureMethodAccessDataFlow s)
|
|
||||||
.hasFlow(DataFlow::exprNode(signingMa), DataFlow::exprNode(expr))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An expr that is a (sub-type of) `JwtParser` for which a signing key has been set and which is used as
|
|
||||||
* the qualifier to a `JwtParserInsecureParseMethodAccess`.
|
|
||||||
*/
|
|
||||||
private class JwtParserWithSigningKeyExpr extends Expr {
|
|
||||||
MethodAccess signingMa;
|
|
||||||
|
|
||||||
JwtParserWithSigningKeyExpr() {
|
|
||||||
this.getType() instanceof TypeDerivedJwtParser and
|
|
||||||
isSigningKeySetter(this, signingMa)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the method access that sets the signing key for this parser. */
|
|
||||||
MethodAccess getSigningMethodAccess() { result = signingMa }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Models flow from `SigningKeyMethodAccess`es to qualifiers of `JwtParserInsecureParseMethodAccess`es.
|
|
||||||
* This is used to determine whether a `JwtParser` has a signing key set.
|
|
||||||
*/
|
|
||||||
private class SigningToInsecureMethodAccessDataFlow extends DataFlow::Configuration {
|
|
||||||
SigningToInsecureMethodAccessDataFlow() { this = "SigningToExprDataFlow" }
|
|
||||||
|
|
||||||
override predicate isSource(DataFlow::Node source) {
|
|
||||||
source.asExpr() instanceof SigningKeyMethodAccess
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isSink(DataFlow::Node sink) {
|
|
||||||
any(JwtParserInsecureParseMethodAccess ma).getQualifier() = sink.asExpr()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Models the builder style of `JwtParser` and `JwtParserBuilder`. */
|
|
||||||
override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
|
||||||
(
|
|
||||||
pred.asExpr().getType() instanceof TypeDerivedJwtParser or
|
|
||||||
pred.asExpr().getType().(RefType).getASourceSupertype*() instanceof TypeJwtParserBuilder
|
|
||||||
) and
|
|
||||||
succ.asExpr().(MethodAccess).getQualifier() = pred.asExpr()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** An access to the `setSigningKey` or `setSigningKeyResolver` method (or an overridden method) defined in `JwtParser` and `JwtParserBuilder`. */
|
|
||||||
private class SigningKeyMethodAccess extends MethodAccess {
|
|
||||||
SigningKeyMethodAccess() {
|
|
||||||
exists(Method m |
|
|
||||||
m.hasName(["setSigningKey", "setSigningKeyResolver"]) and
|
|
||||||
m.getNumberOfParameters() = 1 and
|
|
||||||
(
|
|
||||||
m.getDeclaringType() instanceof TypeJwtParser or
|
|
||||||
m.getDeclaringType() instanceof TypeJwtParserBuilder
|
|
||||||
)
|
|
||||||
|
|
|
||||||
m = this.getMethod().getASourceOverriddenMethod*()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if the `MethodAccess` `ma` occurs in a test file. A test file is any file that
|
|
||||||
* is a direct or indirect child of a directory named `test`, ignoring case.
|
|
||||||
*/
|
|
||||||
private predicate isInTestFile(MethodAccess ma) {
|
|
||||||
exists(string lowerCasedAbsolutePath |
|
|
||||||
lowerCasedAbsolutePath = ma.getLocation().getFile().getAbsolutePath().toLowerCase()
|
|
||||||
|
|
|
||||||
lowerCasedAbsolutePath.matches("%/test/%") and
|
|
||||||
not lowerCasedAbsolutePath
|
|
||||||
.matches("%/ql/test/experimental/query-tests/security/CWE-347/%".toLowerCase())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
from JwtParserInsecureParseMethodAccess ma, JwtParserWithSigningKeyExpr parserExpr
|
|
||||||
where ma.getQualifier() = parserExpr and not isInTestFile(ma)
|
|
||||||
select ma, "A signing key is set $@, but the signature is not verified.",
|
|
||||||
parserExpr.getSigningMethodAccess(), "here"
|
|
||||||
127
java/ql/src/semmle/code/java/security/JWT.qll
Normal file
127
java/ql/src/semmle/code/java/security/JWT.qll
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/** Provides classes for working with JSON Web Token (JWT) libraries. */
|
||||||
|
|
||||||
|
import java
|
||||||
|
private import semmle.code.java.dataflow.DataFlow
|
||||||
|
|
||||||
|
/** A method access that assigns signing keys to a JWT parser. */
|
||||||
|
class JwtParserWithInsecureParseSource extends DataFlow::Node {
|
||||||
|
JwtParserWithInsecureParseSource() {
|
||||||
|
exists(MethodAccess ma, Method m |
|
||||||
|
m.getDeclaringType().getASupertype*() instanceof TypeJwtParser or
|
||||||
|
m.getDeclaringType().getASupertype*() instanceof TypeJwtParserBuilder
|
||||||
|
|
|
||||||
|
this.asExpr() = ma and
|
||||||
|
ma.getMethod() = m and
|
||||||
|
m.hasName(["setSigningKey", "setSigningKeyResolver"])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The qualifier of an insecure parsing method.
|
||||||
|
* That is, either the qualifier of a call to the `parse(token)`,
|
||||||
|
* `parseClaimsJwt(token)` or `parsePlaintextJwt(token)` methods or
|
||||||
|
* the qualifier of a call to a `parse(token, handler)` method
|
||||||
|
* where the `handler` is considered insecure.
|
||||||
|
*/
|
||||||
|
class JwtParserWithInsecureParseSink extends DataFlow::Node {
|
||||||
|
MethodAccess insecureParseMa;
|
||||||
|
|
||||||
|
JwtParserWithInsecureParseSink() {
|
||||||
|
insecureParseMa.getQualifier() = this.asExpr() and
|
||||||
|
exists(Method m |
|
||||||
|
insecureParseMa.getMethod() = m and
|
||||||
|
m.getDeclaringType().getASupertype*() instanceof TypeJwtParser and
|
||||||
|
m.hasName(["parse", "parseClaimsJwt", "parsePlaintextJwt"]) and
|
||||||
|
(
|
||||||
|
m.getNumberOfParameters() = 1
|
||||||
|
or
|
||||||
|
isInsecureJwtHandler(insecureParseMa.getArgument(1))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the method access that does the insecure parsing. */
|
||||||
|
MethodAccess getParseMethodAccess() { result = insecureParseMa }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unit class for adding additional flow steps.
|
||||||
|
*
|
||||||
|
* Extend this class to add additional flow steps that should apply to the `MissingJwtSignatureCheckConf`.
|
||||||
|
*/
|
||||||
|
class JwtParserWithInsecureParseAdditionalFlowStep extends Unit {
|
||||||
|
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A set of additional flow steps to consider when working with JWT parsing related data flows. */
|
||||||
|
private class DefaultJwtParserWithInsecureParseAdditionalFlowStep extends JwtParserWithInsecureParseAdditionalFlowStep {
|
||||||
|
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||||
|
jwtParserStep(node1.asExpr(), node2.asExpr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Models the builder style of `JwtParser` and `JwtParserBuilder`. */
|
||||||
|
private predicate jwtParserStep(Expr parser, MethodAccess ma) {
|
||||||
|
(
|
||||||
|
parser.getType().(RefType).getASourceSupertype*() instanceof TypeJwtParser or
|
||||||
|
parser.getType().(RefType).getASourceSupertype*() instanceof TypeJwtParserBuilder
|
||||||
|
) and
|
||||||
|
ma.getQualifier() = parser
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `parseHandlerExpr` is an insecure `JwtHandler`.
|
||||||
|
* That is, it overrides a method from `JwtHandlerOnJwtMethod` and
|
||||||
|
* the override is not defined on `JwtHandlerAdapter`.
|
||||||
|
* `JwtHandlerAdapter`'s overrides are safe since they always throw an exception.
|
||||||
|
*/
|
||||||
|
private predicate isInsecureJwtHandler(Expr parseHandlerExpr) {
|
||||||
|
exists(RefType t |
|
||||||
|
parseHandlerExpr.getType() = t and
|
||||||
|
t.getASourceSupertype*() instanceof TypeJwtHandler and
|
||||||
|
exists(Method m |
|
||||||
|
m = t.getAMethod() and
|
||||||
|
m.getASourceOverriddenMethod+() instanceof JwtHandlerOnJwtMethod and
|
||||||
|
not m.getSourceDeclaration() instanceof JwtHandlerAdapterOnJwtMethod
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The interface `io.jsonwebtoken.JwtParser`. */
|
||||||
|
private class TypeJwtParser extends Interface {
|
||||||
|
TypeJwtParser() { this.hasQualifiedName("io.jsonwebtoken", "JwtParser") }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The interface `io.jsonwebtoken.JwtParserBuilder`. */
|
||||||
|
private class TypeJwtParserBuilder extends Interface {
|
||||||
|
TypeJwtParserBuilder() { this.hasQualifiedName("io.jsonwebtoken", "JwtParserBuilder") }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The interface `io.jsonwebtoken.JwtHandler`. */
|
||||||
|
private class TypeJwtHandler extends Interface {
|
||||||
|
TypeJwtHandler() { this.hasQualifiedName("io.jsonwebtoken", "JwtHandler") }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The class `io.jsonwebtoken.JwtHandlerAdapter`. */
|
||||||
|
private class TypeJwtHandlerAdapter extends Class {
|
||||||
|
TypeJwtHandlerAdapter() { this.hasQualifiedName("io.jsonwebtoken", "JwtHandlerAdapter") }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The `on(Claims|Plaintext)Jwt` methods defined in `JwtHandler`. */
|
||||||
|
private class JwtHandlerOnJwtMethod extends Method {
|
||||||
|
JwtHandlerOnJwtMethod() {
|
||||||
|
this.hasName(["onClaimsJwt", "onPlaintextJwt"]) and
|
||||||
|
this.getNumberOfParameters() = 1 and
|
||||||
|
this.getDeclaringType() instanceof TypeJwtHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The `on(Claims|Plaintext)Jwt` methods defined in `JwtHandlerAdapter`. */
|
||||||
|
private class JwtHandlerAdapterOnJwtMethod extends Method {
|
||||||
|
JwtHandlerAdapterOnJwtMethod() {
|
||||||
|
this.hasName(["onClaimsJwt", "onPlaintextJwt"]) and
|
||||||
|
this.getNumberOfParameters() = 1 and
|
||||||
|
this.getDeclaringType() instanceof TypeJwtHandlerAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/** Provides classes to be used in queries related to JSON Web Token (JWT) signature vulnerabilities. */
|
||||||
|
|
||||||
|
import java
|
||||||
|
import semmle.code.java.dataflow.DataFlow
|
||||||
|
import semmle.code.java.security.JWT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models flow from signing keys assignments to qualifiers of JWT insecure parsers.
|
||||||
|
* This is used to determine whether a `JwtParser` performing unsafe parsing has a signing key set.
|
||||||
|
*/
|
||||||
|
class MissingJwtSignatureCheckConf extends DataFlow::Configuration {
|
||||||
|
MissingJwtSignatureCheckConf() { this = "SigningToExprDataFlow" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) {
|
||||||
|
source instanceof JwtParserWithInsecureParseSource
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node sink) { sink instanceof JwtParserWithInsecureParseSink }
|
||||||
|
|
||||||
|
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||||
|
any(JwtParserWithInsecureParseAdditionalFlowStep c).step(node1, node2)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
| MissingJWTSignatureCheck.java:96:9:96:27 | parse(...) | A signing key is set $@, but the signature is not verified. | MissingJWTSignatureCheck.java:18:16:18:66 | setSigningKey(...) | here |
|
|
||||||
| MissingJWTSignatureCheck.java:96:9:96:27 | parse(...) | A signing key is set $@, but the signature is not verified. | MissingJWTSignatureCheck.java:22:16:22:73 | setSigningKey(...) | here |
|
|
||||||
| MissingJWTSignatureCheck.java:96:9:96:27 | parse(...) | A signing key is set $@, but the signature is not verified. | MissingJWTSignatureCheck.java:26:16:26:75 | setSigningKey(...) | here |
|
|
||||||
| MissingJWTSignatureCheck.java:100:9:105:22 | parse(...) | A signing key is set $@, but the signature is not verified. | MissingJWTSignatureCheck.java:18:16:18:66 | setSigningKey(...) | here |
|
|
||||||
| MissingJWTSignatureCheck.java:100:9:105:22 | parse(...) | A signing key is set $@, but the signature is not verified. | MissingJWTSignatureCheck.java:22:16:22:73 | setSigningKey(...) | here |
|
|
||||||
| MissingJWTSignatureCheck.java:100:9:105:22 | parse(...) | A signing key is set $@, but the signature is not verified. | MissingJWTSignatureCheck.java:26:16:26:75 | setSigningKey(...) | here |
|
|
||||||
| MissingJWTSignatureCheck.java:127:9:129:33 | parse(...) | A signing key is set $@, but the signature is not verified. | MissingJWTSignatureCheck.java:127:9:128:58 | setSigningKey(...) | here |
|
|
||||||
| MissingJWTSignatureCheck.java:133:9:140:22 | parse(...) | A signing key is set $@, but the signature is not verified. | MissingJWTSignatureCheck.java:133:9:134:58 | setSigningKey(...) | here |
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
experimental/Security/CWE/CWE-347/MissingJWTSignatureCheck.ql
|
|
||||||
@@ -1,18 +1,13 @@
|
|||||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/jwtk-jjwt-0.11.2
|
|
||||||
|
|
||||||
import io.jsonwebtoken.Jwts;
|
|
||||||
import io.jsonwebtoken.JwtParser;
|
|
||||||
import io.jsonwebtoken.Jwt;
|
|
||||||
import io.jsonwebtoken.Jws;
|
|
||||||
import io.jsonwebtoken.Header;
|
import io.jsonwebtoken.Header;
|
||||||
import io.jsonwebtoken.JwtParserBuilder;
|
import io.jsonwebtoken.Jws;
|
||||||
|
import io.jsonwebtoken.Jwt;
|
||||||
import io.jsonwebtoken.JwtHandlerAdapter;
|
import io.jsonwebtoken.JwtHandlerAdapter;
|
||||||
|
import io.jsonwebtoken.JwtParser;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
import io.jsonwebtoken.impl.DefaultJwtParser;
|
import io.jsonwebtoken.impl.DefaultJwtParser;
|
||||||
|
import io.jsonwebtoken.impl.DefaultJwtParserBuilder;
|
||||||
|
|
||||||
public class MissingJWTSignatureCheck {
|
public class MissingJWTSignatureCheckTest {
|
||||||
|
|
||||||
|
|
||||||
// SIGNED
|
|
||||||
|
|
||||||
private JwtParser getASignedParser() {
|
private JwtParser getASignedParser() {
|
||||||
return Jwts.parser().setSigningKey("someBase64EncodedKey");
|
return Jwts.parser().setSigningKey("someBase64EncodedKey");
|
||||||
@@ -46,10 +41,6 @@ public class MissingJWTSignatureCheck {
|
|||||||
goodJwtHandler(parser3, "");
|
goodJwtHandler(parser3, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// SIGNED END
|
|
||||||
|
|
||||||
// UNSIGNED
|
|
||||||
|
|
||||||
private JwtParser getAnUnsignedParser() {
|
private JwtParser getAnUnsignedParser() {
|
||||||
return Jwts.parser();
|
return Jwts.parser();
|
||||||
}
|
}
|
||||||
@@ -84,20 +75,16 @@ public class MissingJWTSignatureCheck {
|
|||||||
|
|
||||||
private void signParserAfterParseCall() {
|
private void signParserAfterParseCall() {
|
||||||
JwtParser parser = getAnUnsignedParser();
|
JwtParser parser = getAnUnsignedParser();
|
||||||
parser.parse(""); // Should not be detected
|
parser.parse(""); // Safe
|
||||||
parser.setSigningKey("someBase64EncodedKey");
|
parser.setSigningKey("someBase64EncodedKey");
|
||||||
}
|
}
|
||||||
|
|
||||||
// UNSIGNED END
|
|
||||||
|
|
||||||
// INDIRECT
|
|
||||||
|
|
||||||
private void badJwtOnParserBuilder(JwtParser parser, String token) {
|
private void badJwtOnParserBuilder(JwtParser parser, String token) {
|
||||||
parser.parse(token); // BAD: Does not verify the signature
|
parser.parse(token); // $hasMissingJwtSignatureCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
private void badJwtHandlerOnParserBuilder(JwtParser parser, String token) {
|
private void badJwtHandlerOnParserBuilder(JwtParser parser, String token) {
|
||||||
parser.parse(token, new JwtHandlerAdapter<Jwt<Header, String>>() { // BAD: The handler is called on an unverified JWT
|
parser.parse(token, new JwtHandlerAdapter<Jwt<Header, String>>() { // $hasMissingJwtSignatureCheck
|
||||||
@Override
|
@Override
|
||||||
public Jwt<Header, String> onPlaintextJwt(Jwt<Header, String> jwt) {
|
public Jwt<Header, String> onPlaintextJwt(Jwt<Header, String> jwt) {
|
||||||
return jwt;
|
return jwt;
|
||||||
@@ -106,12 +93,12 @@ public class MissingJWTSignatureCheck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void goodJwtOnParserBuilder(JwtParser parser, String token) {
|
private void goodJwtOnParserBuilder(JwtParser parser, String token) {
|
||||||
parser.parseClaimsJws(token) // GOOD: Verify the signature
|
parser.parseClaimsJws(token) // Safe
|
||||||
.getBody();
|
.getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void goodJwtHandler(JwtParser parser, String token) {
|
private void goodJwtHandler(JwtParser parser, String token) {
|
||||||
parser.parse(token, new JwtHandlerAdapter<Jws<String>>() { // GOOD: The handler is called on a verified JWS
|
parser.parse(token, new JwtHandlerAdapter<Jws<String>>() { // Safe
|
||||||
@Override
|
@Override
|
||||||
public Jws<String> onPlaintextJws(Jws<String> jws) {
|
public Jws<String> onPlaintextJws(Jws<String> jws) {
|
||||||
return jws;
|
return jws;
|
||||||
@@ -119,20 +106,17 @@ public class MissingJWTSignatureCheck {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// INDIRECT END
|
|
||||||
|
|
||||||
// DIRECT
|
|
||||||
|
|
||||||
private void badJwtOnParserBuilder(String token) {
|
private void badJwtOnParserBuilder(String token) {
|
||||||
Jwts.parserBuilder()
|
Jwts.parserBuilder().setSigningKey("someBase64EncodedKey").build().parse(token); // $hasMissingJwtSignatureCheck
|
||||||
.setSigningKey("someBase64EncodedKey").build()
|
}
|
||||||
.parse(token); // BAD: Does not verify the signature
|
|
||||||
|
private void badJwtOnDefaultParserBuilder(String token) {
|
||||||
|
new DefaultJwtParserBuilder().setSigningKey("someBase64EncodedKey").build().parse(token); // $hasMissingJwtSignatureCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
private void badJwtHandlerOnParser(String token) {
|
private void badJwtHandlerOnParser(String token) {
|
||||||
Jwts.parser()
|
Jwts.parser().setSigningKey("someBase64EncodedKey").parse(token, // $hasMissingJwtSignatureCheck
|
||||||
.setSigningKey("someBase64EncodedKey")
|
new JwtHandlerAdapter<Jwt<Header, String>>() {
|
||||||
.parse(token, new JwtHandlerAdapter<Jwt<Header, String>>() { // BAD: The handler is called on an unverified JWT
|
|
||||||
@Override
|
@Override
|
||||||
public Jwt<Header, String> onPlaintextJwt(Jwt<Header, String> jwt) {
|
public Jwt<Header, String> onPlaintextJwt(Jwt<Header, String> jwt) {
|
||||||
return jwt;
|
return jwt;
|
||||||
@@ -141,24 +125,17 @@ public class MissingJWTSignatureCheck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void goodJwtOnParser(String token) {
|
private void goodJwtOnParser(String token) {
|
||||||
Jwts.parser()
|
Jwts.parser().setSigningKey("someBase64EncodedKey").parseClaimsJws(token) // Safe
|
||||||
.setSigningKey("someBase64EncodedKey")
|
|
||||||
.parseClaimsJws(token) // GOOD: Verify the signature
|
|
||||||
.getBody();
|
.getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void goodJwtHandlerOnParserBuilder(String token) {
|
private void goodJwtHandlerOnParserBuilder(String token) {
|
||||||
Jwts.parserBuilder()
|
Jwts.parserBuilder().setSigningKey("someBase64EncodedKey").build().parse(token, // Safe
|
||||||
.setSigningKey("someBase64EncodedKey").build()
|
new JwtHandlerAdapter<Jws<String>>() {
|
||||||
.parse(token, new JwtHandlerAdapter<Jws<String>>() { // GOOD: The handler is called on a verified JWS
|
|
||||||
@Override
|
@Override
|
||||||
public Jws<String> onPlaintextJws(Jws<String> jws) {
|
public Jws<String> onPlaintextJws(Jws<String> jws) {
|
||||||
return jws;
|
return jws;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// DIRECT END
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import java
|
||||||
|
import semmle.code.java.security.MissingJWTSignatureCheckQuery
|
||||||
|
import TestUtilities.InlineExpectationsTest
|
||||||
|
|
||||||
|
class HasMissingJwtSignatureCheckTest extends InlineExpectationsTest {
|
||||||
|
HasMissingJwtSignatureCheckTest() { this = "HasMissingJwtSignatureCheckTest" }
|
||||||
|
|
||||||
|
override string getARelevantTag() { result = "hasMissingJwtSignatureCheck" }
|
||||||
|
|
||||||
|
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||||
|
tag = "hasMissingJwtSignatureCheck" and
|
||||||
|
exists(DataFlow::Node source, DataFlow::Node sink, MissingJwtSignatureCheckConf conf |
|
||||||
|
conf.hasFlow(source, sink)
|
||||||
|
|
|
||||||
|
sink.getLocation() = location and
|
||||||
|
element = sink.toString() and
|
||||||
|
value = ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
1
java/ql/test/query-tests/security/CWE-347/options
Normal file
1
java/ql/test/query-tests/security/CWE-347/options
Normal file
@@ -0,0 +1 @@
|
|||||||
|
semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/jwtk-jjwt-0.11.2
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 jsonwebtoken.io
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package io.jsonwebtoken.impl;
|
||||||
|
|
||||||
|
import java.security.Key;
|
||||||
|
import io.jsonwebtoken.JwtParser;
|
||||||
|
import io.jsonwebtoken.JwtParserBuilder;
|
||||||
|
import io.jsonwebtoken.SigningKeyResolver;
|
||||||
|
|
||||||
|
|
||||||
|
public class DefaultJwtParserBuilder implements JwtParserBuilder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JwtParserBuilder setSigningKey(byte[] key) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JwtParserBuilder setSigningKey(String base64EncodedSecretKey) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JwtParserBuilder setSigningKey(Key key) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JwtParserBuilder setSigningKeyResolver(SigningKeyResolver signingKeyResolver) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JwtParser build() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user