Merge pull request #14088 from am0o0/amammad-js-JWT

JS: decoding JWT without signature verification
This commit is contained in:
Erik Krogh Kristensen
2024-06-20 20:13:40 +02:00
committed by GitHub
18 changed files with 850 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
const express = require('express')
const app = express()
const jwtJsonwebtoken = require('jsonwebtoken');
const jwt_decode = require('jwt-decode');
const jwt_simple = require('jwt-simple');
const jose = require('jose')
const port = 3000
function getSecret() {
return "A Safe generated random key"
}
app.get('/jose', (req, res) => {
const UserToken = req.headers.authorization;
// BAD: no signature verification
jose.decodeJwt(UserToken)
})
app.get('/jwtDecode', (req, res) => {
const UserToken = req.headers.authorization;
// BAD: no signature verification
jwt_decode(UserToken)
})
app.get('/jwtSimple', (req, res) => {
const UserToken = req.headers.authorization;
// jwt.decode(token, key, noVerify, algorithm)
// BAD: no signature verification
jwt_simple.decode(UserToken, getSecret(), true);
})
app.get('/jwtJsonwebtoken', (req, res) => {
const UserToken = req.headers.authorization;
// BAD: no signature verification
jwtJsonwebtoken.decode(UserToken)
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})

View File

@@ -0,0 +1,56 @@
const express = require('express')
const app = express()
const jwtJsonwebtoken = require('jsonwebtoken');
const jwt_decode = require('jwt-decode');
const jwt_simple = require('jwt-simple');
const jose = require('jose')
const port = 3000
function getSecret() {
return "A Safe generated random key"
}
app.get('/jose1', async (req, res) => {
const UserToken = req.headers.authorization;
// GOOD: with signature verification
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret()))
})
app.get('/jose2', async (req, res) => {
const UserToken = req.headers.authorization;
// GOOD: first without signature verification then with signature verification for same UserToken
jose.decodeJwt(UserToken)
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret()))
})
app.get('/jwtSimple1', (req, res) => {
const UserToken = req.headers.authorization;
// GOOD: first without signature verification then with signature verification for same UserToken
jwt_simple.decode(UserToken, getSecret(), false);
jwt_simple.decode(UserToken, getSecret());
})
app.get('/jwtSimple2', (req, res) => {
const UserToken = req.headers.authorization;
// GOOD: with signature verification
jwt_simple.decode(UserToken, getSecret(), true);
jwt_simple.decode(UserToken, getSecret());
})
app.get('/jwtJsonwebtoken1', (req, res) => {
const UserToken = req.headers.authorization;
// GOOD: with signature verification
jwtJsonwebtoken.verify(UserToken, getSecret())
})
app.get('/jwtJsonwebtoken2', (req, res) => {
const UserToken = req.headers.authorization;
// GOOD: first without signature verification then with signature verification for same UserToken
jwtJsonwebtoken.decode(UserToken)
jwtJsonwebtoken.verify(UserToken, getSecret())
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})

View File

@@ -0,0 +1,54 @@
import javascript
DataFlow::Node unverifiedDecode() {
result = API::moduleImport("jsonwebtoken").getMember("decode").getParameter(0).asSink()
or
exists(API::Node verify | verify = API::moduleImport("jsonwebtoken").getMember("verify") |
verify
.getParameter(2)
.getMember("algorithms")
.getUnknownMember()
.asSink()
.mayHaveStringValue("none") and
result = verify.getParameter(0).asSink()
)
or
// jwt-simple
exists(API::Node n | n = API::moduleImport("jwt-simple").getMember("decode") |
n.getParameter(2).asSink().asExpr() = any(BoolLiteral b | b.getBoolValue() = true) and
result = n.getParameter(0).asSink()
)
or
// jwt-decode
result = API::moduleImport("jwt-decode").getParameter(0).asSink()
or
//jose
result = API::moduleImport("jose").getMember("decodeJwt").getParameter(0).asSink()
}
DataFlow::Node verifiedDecode() {
exists(API::Node verify | verify = API::moduleImport("jsonwebtoken").getMember("verify") |
(
not verify
.getParameter(2)
.getMember("algorithms")
.getUnknownMember()
.asSink()
.mayHaveStringValue("none") or
not exists(verify.getParameter(2).getMember("algorithms"))
) and
result = verify.getParameter(0).asSink()
)
or
// jwt-simple
exists(API::Node n | n = API::moduleImport("jwt-simple").getMember("decode") |
(
n.getParameter(2).asSink().asExpr() = any(BoolLiteral b | b.getBoolValue() = false) or
not exists(n.getParameter(2))
) and
result = n.getParameter(0).asSink()
or
//jose
result = API::moduleImport("jose").getMember("jwtVerify").getParameter(0).asSink()
)
}

View File

@@ -0,0 +1,37 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
A JSON Web Token (JWT) is used for authenticating and managing users in an application.
</p>
<p>
Only Decoding JWTs without checking if they have a valid signature or not can lead to security vulnerabilities.
</p>
</overview>
<recommendation>
<p>
Don't use methods that only decode JWT, Instead use methods that verify the signature of JWT.
</p>
</recommendation>
<example>
<p>
In the following code, you can see the proper usage of the most popular JWT libraries.
</p>
<sample src="Examples/Good.js" />
<p>
In the following code, you can see the improper usage of the most popular JWT libraries.
</p>
<sample src="Examples/Bad.js" />
</example>
<references>
<li>
<a href="https://www.ghostccamm.com/blog/multi_strapi_vulns/#cve-2023-22893-authentication-bypass-for-aws-cognito-login-provider-in-strapi-versions-456">JWT claim has not been verified</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,40 @@
/**
* @name JWT missing secret or public key verification
* @description The application does not verify the JWT payload with a cryptographic secret or public key.
* @kind path-problem
* @problem.severity error
* @security-severity 8.0
* @precision high
* @id js/decode-jwt-without-verification
* @tags security
* external/cwe/cwe-347
*/
import javascript
import DataFlow::PathGraph
import JWT
class ConfigurationUnverifiedDecode extends TaintTracking::Configuration {
ConfigurationUnverifiedDecode() { this = "jsonwebtoken without any signature verification" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink = unverifiedDecode() }
}
class ConfigurationVerifiedDecode extends TaintTracking::Configuration {
ConfigurationVerifiedDecode() { this = "jsonwebtoken with signature verification" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink = verifiedDecode() }
}
from ConfigurationUnverifiedDecode cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where
cfg.hasFlowPath(source, sink) and
not exists(ConfigurationVerifiedDecode cfg2 |
cfg2.hasFlowPath(any(DataFlow::PathNode p | p.getNode() = source.getNode()), _)
)
select source.getNode(), source, sink, "Decoding JWT $@.", sink.getNode(),
"without signature verification"

View File

@@ -0,0 +1,55 @@
/**
* @name JWT missing secret or public key verification
* @description The application does not verify the JWT payload with a cryptographic secret or public key.
* @kind path-problem
* @problem.severity error
* @security-severity 8.0
* @precision high
* @id js/decode-jwt-without-verification-local-source
* @tags security
* external/cwe/cwe-347
*/
import javascript
import DataFlow::PathGraph
import JWT
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "jsonwebtoken without any signature verification" }
override predicate isSource(DataFlow::Node source) {
source = [unverifiedDecode(), verifiedDecode()].getALocalSource()
}
override predicate isSink(DataFlow::Node sink) {
sink = unverifiedDecode()
or
sink = verifiedDecode()
}
}
/** Holds if `source` flows to the first parameter of jsonwebtoken.verify */
predicate isSafe(Configuration cfg, DataFlow::Node source) {
exists(DataFlow::Node sink |
cfg.hasFlow(source, sink) and
sink = verifiedDecode()
)
}
/**
* Holds if:
* - `source` does not flow to the first parameter of `jsonwebtoken.verify`, and
* - `source` flows to the first parameter of `jsonwebtoken.decode`
*/
predicate isVulnerable(Configuration cfg, DataFlow::Node source, DataFlow::Node sink) {
not isSafe(cfg, source) and // i.e., source does not flow to a verify call
cfg.hasFlow(source, sink) and // but it does flow to something else
sink = unverifiedDecode() // and that something else is a call to decode.
}
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where
cfg.hasFlowPath(source, sink) and
isVulnerable(cfg, source.getNode(), sink.getNode())
select source.getNode(), source, sink, "Decoding JWT $@.", sink.getNode(),
"without signature verification"

View File

@@ -0,0 +1,48 @@
const express = require('express')
const jwtJsonwebtoken = require('jsonwebtoken');
function getSecret() {
return "A Safe generated random key"
}
function aJWT() {
return "A JWT provided by user"
}
(function () {
const UserToken = aJwt()
// BAD: no signature verification
jwtJsonwebtoken.decode(UserToken) // NOT OK
})();
(function () {
const UserToken = aJwt()
// BAD: no signature verification
jwtJsonwebtoken.decode(UserToken) // NOT OK
jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256", "none"] }) // NOT OK
})();
(function () {
const UserToken = aJwt()
// GOOD: with signature verification
jwtJsonwebtoken.verify(UserToken, getSecret()) // OK
})();
(function () {
const UserToken = aJwt()
// GOOD: first without signature verification then with signature verification for same UserToken
jwtJsonwebtoken.decode(UserToken) // OK
jwtJsonwebtoken.verify(UserToken, getSecret()) // OK
})();
(function () {
const UserToken = aJwt()
// GOOD: first without signature verification then with signature verification for same UserToken
jwtJsonwebtoken.decode(UserToken) // OK
jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256"] }) // OK
})();

View File

@@ -0,0 +1,141 @@
nodes
| JsonWebToken.js:13:11:13:28 | UserToken |
| JsonWebToken.js:13:23:13:28 | aJwt() |
| JsonWebToken.js:13:23:13:28 | aJwt() |
| JsonWebToken.js:16:28:16:36 | UserToken |
| JsonWebToken.js:16:28:16:36 | UserToken |
| JsonWebToken.js:20:11:20:28 | UserToken |
| JsonWebToken.js:20:23:20:28 | aJwt() |
| JsonWebToken.js:20:23:20:28 | aJwt() |
| JsonWebToken.js:23:28:23:36 | UserToken |
| JsonWebToken.js:23:28:23:36 | UserToken |
| JsonWebToken.js:24:28:24:36 | UserToken |
| JsonWebToken.js:24:28:24:36 | UserToken |
| JsonWebToken.js:28:11:28:28 | UserToken |
| JsonWebToken.js:28:23:28:28 | aJwt() |
| JsonWebToken.js:28:23:28:28 | aJwt() |
| JsonWebToken.js:31:28:31:36 | UserToken |
| JsonWebToken.js:31:28:31:36 | UserToken |
| JsonWebToken.js:35:11:35:28 | UserToken |
| JsonWebToken.js:35:23:35:28 | aJwt() |
| JsonWebToken.js:35:23:35:28 | aJwt() |
| JsonWebToken.js:38:28:38:36 | UserToken |
| JsonWebToken.js:38:28:38:36 | UserToken |
| JsonWebToken.js:39:28:39:36 | UserToken |
| JsonWebToken.js:39:28:39:36 | UserToken |
| JsonWebToken.js:43:11:43:28 | UserToken |
| JsonWebToken.js:43:23:43:28 | aJwt() |
| JsonWebToken.js:43:23:43:28 | aJwt() |
| JsonWebToken.js:46:28:46:36 | UserToken |
| JsonWebToken.js:46:28:46:36 | UserToken |
| JsonWebToken.js:47:28:47:36 | UserToken |
| JsonWebToken.js:47:28:47:36 | UserToken |
| jose.js:12:11:12:28 | UserToken |
| jose.js:12:23:12:28 | aJwt() |
| jose.js:12:23:12:28 | aJwt() |
| jose.js:15:20:15:28 | UserToken |
| jose.js:15:20:15:28 | UserToken |
| jose.js:19:11:19:28 | UserToken |
| jose.js:19:23:19:28 | aJwt() |
| jose.js:19:23:19:28 | aJwt() |
| jose.js:22:20:22:28 | UserToken |
| jose.js:22:20:22:28 | UserToken |
| jose.js:23:26:23:34 | UserToken |
| jose.js:23:26:23:34 | UserToken |
| jose.js:27:11:27:28 | UserToken |
| jose.js:27:23:27:28 | aJwt() |
| jose.js:27:23:27:28 | aJwt() |
| jose.js:30:26:30:34 | UserToken |
| jose.js:30:26:30:34 | UserToken |
| jwtDecode.js:13:11:13:28 | UserToken |
| jwtDecode.js:13:23:13:28 | aJwt() |
| jwtDecode.js:13:23:13:28 | aJwt() |
| jwtDecode.js:17:16:17:24 | UserToken |
| jwtDecode.js:17:16:17:24 | UserToken |
| jwtSimple.js:13:11:13:28 | UserToken |
| jwtSimple.js:13:23:13:28 | aJwt() |
| jwtSimple.js:13:23:13:28 | aJwt() |
| jwtSimple.js:16:23:16:31 | UserToken |
| jwtSimple.js:16:23:16:31 | UserToken |
| jwtSimple.js:20:11:20:28 | UserToken |
| jwtSimple.js:20:23:20:28 | aJwt() |
| jwtSimple.js:20:23:20:28 | aJwt() |
| jwtSimple.js:23:23:23:31 | UserToken |
| jwtSimple.js:23:23:23:31 | UserToken |
| jwtSimple.js:24:23:24:31 | UserToken |
| jwtSimple.js:24:23:24:31 | UserToken |
| jwtSimple.js:28:11:28:28 | UserToken |
| jwtSimple.js:28:23:28:28 | aJwt() |
| jwtSimple.js:28:23:28:28 | aJwt() |
| jwtSimple.js:31:23:31:31 | UserToken |
| jwtSimple.js:31:23:31:31 | UserToken |
| jwtSimple.js:32:23:32:31 | UserToken |
| jwtSimple.js:32:23:32:31 | UserToken |
edges
| JsonWebToken.js:13:11:13:28 | UserToken | JsonWebToken.js:16:28:16:36 | UserToken |
| JsonWebToken.js:13:11:13:28 | UserToken | JsonWebToken.js:16:28:16:36 | UserToken |
| JsonWebToken.js:13:23:13:28 | aJwt() | JsonWebToken.js:13:11:13:28 | UserToken |
| JsonWebToken.js:13:23:13:28 | aJwt() | JsonWebToken.js:13:11:13:28 | UserToken |
| JsonWebToken.js:20:11:20:28 | UserToken | JsonWebToken.js:23:28:23:36 | UserToken |
| JsonWebToken.js:20:11:20:28 | UserToken | JsonWebToken.js:23:28:23:36 | UserToken |
| JsonWebToken.js:20:11:20:28 | UserToken | JsonWebToken.js:24:28:24:36 | UserToken |
| JsonWebToken.js:20:11:20:28 | UserToken | JsonWebToken.js:24:28:24:36 | UserToken |
| JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:20:11:20:28 | UserToken |
| JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:20:11:20:28 | UserToken |
| JsonWebToken.js:28:11:28:28 | UserToken | JsonWebToken.js:31:28:31:36 | UserToken |
| JsonWebToken.js:28:11:28:28 | UserToken | JsonWebToken.js:31:28:31:36 | UserToken |
| JsonWebToken.js:28:23:28:28 | aJwt() | JsonWebToken.js:28:11:28:28 | UserToken |
| JsonWebToken.js:28:23:28:28 | aJwt() | JsonWebToken.js:28:11:28:28 | UserToken |
| JsonWebToken.js:35:11:35:28 | UserToken | JsonWebToken.js:38:28:38:36 | UserToken |
| JsonWebToken.js:35:11:35:28 | UserToken | JsonWebToken.js:38:28:38:36 | UserToken |
| JsonWebToken.js:35:11:35:28 | UserToken | JsonWebToken.js:39:28:39:36 | UserToken |
| JsonWebToken.js:35:11:35:28 | UserToken | JsonWebToken.js:39:28:39:36 | UserToken |
| JsonWebToken.js:35:23:35:28 | aJwt() | JsonWebToken.js:35:11:35:28 | UserToken |
| JsonWebToken.js:35:23:35:28 | aJwt() | JsonWebToken.js:35:11:35:28 | UserToken |
| JsonWebToken.js:43:11:43:28 | UserToken | JsonWebToken.js:46:28:46:36 | UserToken |
| JsonWebToken.js:43:11:43:28 | UserToken | JsonWebToken.js:46:28:46:36 | UserToken |
| JsonWebToken.js:43:11:43:28 | UserToken | JsonWebToken.js:47:28:47:36 | UserToken |
| JsonWebToken.js:43:11:43:28 | UserToken | JsonWebToken.js:47:28:47:36 | UserToken |
| JsonWebToken.js:43:23:43:28 | aJwt() | JsonWebToken.js:43:11:43:28 | UserToken |
| JsonWebToken.js:43:23:43:28 | aJwt() | JsonWebToken.js:43:11:43:28 | UserToken |
| jose.js:12:11:12:28 | UserToken | jose.js:15:20:15:28 | UserToken |
| jose.js:12:11:12:28 | UserToken | jose.js:15:20:15:28 | UserToken |
| jose.js:12:23:12:28 | aJwt() | jose.js:12:11:12:28 | UserToken |
| jose.js:12:23:12:28 | aJwt() | jose.js:12:11:12:28 | UserToken |
| jose.js:19:11:19:28 | UserToken | jose.js:22:20:22:28 | UserToken |
| jose.js:19:11:19:28 | UserToken | jose.js:22:20:22:28 | UserToken |
| jose.js:19:11:19:28 | UserToken | jose.js:23:26:23:34 | UserToken |
| jose.js:19:11:19:28 | UserToken | jose.js:23:26:23:34 | UserToken |
| jose.js:19:23:19:28 | aJwt() | jose.js:19:11:19:28 | UserToken |
| jose.js:19:23:19:28 | aJwt() | jose.js:19:11:19:28 | UserToken |
| jose.js:27:11:27:28 | UserToken | jose.js:30:26:30:34 | UserToken |
| jose.js:27:11:27:28 | UserToken | jose.js:30:26:30:34 | UserToken |
| jose.js:27:23:27:28 | aJwt() | jose.js:27:11:27:28 | UserToken |
| jose.js:27:23:27:28 | aJwt() | jose.js:27:11:27:28 | UserToken |
| jwtDecode.js:13:11:13:28 | UserToken | jwtDecode.js:17:16:17:24 | UserToken |
| jwtDecode.js:13:11:13:28 | UserToken | jwtDecode.js:17:16:17:24 | UserToken |
| jwtDecode.js:13:23:13:28 | aJwt() | jwtDecode.js:13:11:13:28 | UserToken |
| jwtDecode.js:13:23:13:28 | aJwt() | jwtDecode.js:13:11:13:28 | UserToken |
| jwtSimple.js:13:11:13:28 | UserToken | jwtSimple.js:16:23:16:31 | UserToken |
| jwtSimple.js:13:11:13:28 | UserToken | jwtSimple.js:16:23:16:31 | UserToken |
| jwtSimple.js:13:23:13:28 | aJwt() | jwtSimple.js:13:11:13:28 | UserToken |
| jwtSimple.js:13:23:13:28 | aJwt() | jwtSimple.js:13:11:13:28 | UserToken |
| jwtSimple.js:20:11:20:28 | UserToken | jwtSimple.js:23:23:23:31 | UserToken |
| jwtSimple.js:20:11:20:28 | UserToken | jwtSimple.js:23:23:23:31 | UserToken |
| jwtSimple.js:20:11:20:28 | UserToken | jwtSimple.js:24:23:24:31 | UserToken |
| jwtSimple.js:20:11:20:28 | UserToken | jwtSimple.js:24:23:24:31 | UserToken |
| jwtSimple.js:20:23:20:28 | aJwt() | jwtSimple.js:20:11:20:28 | UserToken |
| jwtSimple.js:20:23:20:28 | aJwt() | jwtSimple.js:20:11:20:28 | UserToken |
| jwtSimple.js:28:11:28:28 | UserToken | jwtSimple.js:31:23:31:31 | UserToken |
| jwtSimple.js:28:11:28:28 | UserToken | jwtSimple.js:31:23:31:31 | UserToken |
| jwtSimple.js:28:11:28:28 | UserToken | jwtSimple.js:32:23:32:31 | UserToken |
| jwtSimple.js:28:11:28:28 | UserToken | jwtSimple.js:32:23:32:31 | UserToken |
| jwtSimple.js:28:23:28:28 | aJwt() | jwtSimple.js:28:11:28:28 | UserToken |
| jwtSimple.js:28:23:28:28 | aJwt() | jwtSimple.js:28:11:28:28 | UserToken |
#select
| JsonWebToken.js:13:23:13:28 | aJwt() | JsonWebToken.js:13:23:13:28 | aJwt() | JsonWebToken.js:16:28:16:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:16:28:16:36 | UserToken | without signature verification |
| JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:23:28:23:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:23:28:23:36 | UserToken | without signature verification |
| JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:20:23:20:28 | aJwt() | JsonWebToken.js:24:28:24:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:24:28:24:36 | UserToken | without signature verification |
| jose.js:12:23:12:28 | aJwt() | jose.js:12:23:12:28 | aJwt() | jose.js:15:20:15:28 | UserToken | Decoding JWT $@. | jose.js:15:20:15:28 | UserToken | without signature verification |
| jwtDecode.js:13:23:13:28 | aJwt() | jwtDecode.js:13:23:13:28 | aJwt() | jwtDecode.js:17:16:17:24 | UserToken | Decoding JWT $@. | jwtDecode.js:17:16:17:24 | UserToken | without signature verification |
| jwtSimple.js:13:23:13:28 | aJwt() | jwtSimple.js:13:23:13:28 | aJwt() | jwtSimple.js:16:23:16:31 | UserToken | Decoding JWT $@. | jwtSimple.js:16:23:16:31 | UserToken | without signature verification |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-347/decodeJwtWithoutVerificationLocalSource.ql

View File

@@ -0,0 +1,31 @@
const jose = require('jose')
function getSecret() {
return "A Safe generated random key"
}
function aJWT() {
return "A JWT provided by user"
}
(function () {
const UserToken = aJwt()
// no signature verification
jose.decodeJwt(UserToken) // NOT OK
})();
(async function () {
const UserToken = aJwt()
// first without signature verification then with signature verification for same UserToken
jose.decodeJwt(UserToken) // OK
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret())) // OK
})();
(async function () {
const UserToken = aJwt()
// with signature verification
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret())) // OK
})();

View File

@@ -0,0 +1,18 @@
const express = require('express')
const jwt_decode = require('jwt-decode');
function getSecret() {
return "A Safe generated random key"
}
function aJWT() {
return "A JWT provided by user"
}
(function () {
const UserToken = aJwt()
// jwt-decode
// no signature verification
jwt_decode(UserToken) // NOT OK
})();

View File

@@ -0,0 +1,33 @@
const express = require('express')
const jwt_simple = require('jwt-simple');
function getSecret() {
return "A Safe generated random key"
}
function aJWT() {
return "A JWT provided by user"
}
(function () {
const UserToken = aJwt()
// BAD: no signature verification
jwt_simple.decode(UserToken, getSecret(), true); // NOT OK
})();
(function () {
const UserToken = aJwt()
// GOOD: all with with signature verification
jwt_simple.decode(UserToken, getSecret(), false); // OK
jwt_simple.decode(UserToken, getSecret()); // OK
})();
(function () {
const UserToken = aJwt()
// GOOD: first without signature verification then with signature verification for same UserToken
jwt_simple.decode(UserToken, getSecret(), true); // OK
jwt_simple.decode(UserToken, getSecret()); // OK
})();

View File

@@ -0,0 +1,49 @@
const express = require('express')
const app = express()
const jwtJsonwebtoken = require('jsonwebtoken');
const port = 3000
function getSecret() {
return "A Safe generated random key"
}
app.get('/jwtJsonwebtoken1', (req, res) => {
const UserToken = req.headers.authorization;
// BAD: no signature verification
jwtJsonwebtoken.decode(UserToken) // NOT OK
})
app.get('/jwtJsonwebtoken2', (req, res) => {
const UserToken = req.headers.authorization;
// BAD: no signature verification
jwtJsonwebtoken.decode(UserToken) // NOT OK
jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256", "none"] }) // NOT OK
})
app.get('/jwtJsonwebtoken3', (req, res) => {
const UserToken = req.headers.authorization;
// GOOD: with signature verification
jwtJsonwebtoken.verify(UserToken, getSecret()) // OK
})
app.get('/jwtJsonwebtoken4', (req, res) => {
const UserToken = req.headers.authorization;
// GOOD: first without signature verification then with signature verification for same UserToken
jwtJsonwebtoken.decode(UserToken) // OK
jwtJsonwebtoken.verify(UserToken, getSecret()) // OK
})
app.get('/jwtJsonwebtoken5', (req, res) => {
const UserToken = req.headers.authorization;
// GOOD: first without signature verification then with signature verification for same UserToken
jwtJsonwebtoken.decode(UserToken) // OK
jwtJsonwebtoken.verify(UserToken, getSecret(), { algorithms: ["HS256"] }) // OK
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})

View File

@@ -0,0 +1,161 @@
nodes
| JsonWebToken.js:10:11:10:47 | UserToken |
| JsonWebToken.js:10:23:10:47 | req.hea ... ization |
| JsonWebToken.js:10:23:10:47 | req.hea ... ization |
| JsonWebToken.js:13:28:13:36 | UserToken |
| JsonWebToken.js:13:28:13:36 | UserToken |
| JsonWebToken.js:17:11:17:47 | UserToken |
| JsonWebToken.js:17:23:17:47 | req.hea ... ization |
| JsonWebToken.js:17:23:17:47 | req.hea ... ization |
| JsonWebToken.js:20:28:20:36 | UserToken |
| JsonWebToken.js:20:28:20:36 | UserToken |
| JsonWebToken.js:21:28:21:36 | UserToken |
| JsonWebToken.js:21:28:21:36 | UserToken |
| JsonWebToken.js:25:11:25:47 | UserToken |
| JsonWebToken.js:25:23:25:47 | req.hea ... ization |
| JsonWebToken.js:25:23:25:47 | req.hea ... ization |
| JsonWebToken.js:28:28:28:36 | UserToken |
| JsonWebToken.js:28:28:28:36 | UserToken |
| JsonWebToken.js:32:11:32:47 | UserToken |
| JsonWebToken.js:32:11:32:47 | UserToken |
| JsonWebToken.js:32:23:32:47 | req.hea ... ization |
| JsonWebToken.js:32:23:32:47 | req.hea ... ization |
| JsonWebToken.js:32:23:32:47 | req.hea ... ization |
| JsonWebToken.js:32:23:32:47 | req.hea ... ization |
| JsonWebToken.js:35:28:35:36 | UserToken |
| JsonWebToken.js:35:28:35:36 | UserToken |
| JsonWebToken.js:36:28:36:36 | UserToken |
| JsonWebToken.js:36:28:36:36 | UserToken |
| JsonWebToken.js:40:11:40:47 | UserToken |
| JsonWebToken.js:40:11:40:47 | UserToken |
| JsonWebToken.js:40:23:40:47 | req.hea ... ization |
| JsonWebToken.js:40:23:40:47 | req.hea ... ization |
| JsonWebToken.js:40:23:40:47 | req.hea ... ization |
| JsonWebToken.js:40:23:40:47 | req.hea ... ization |
| JsonWebToken.js:43:28:43:36 | UserToken |
| JsonWebToken.js:43:28:43:36 | UserToken |
| JsonWebToken.js:44:28:44:36 | UserToken |
| JsonWebToken.js:44:28:44:36 | UserToken |
| jose.js:11:11:11:47 | UserToken |
| jose.js:11:23:11:47 | req.hea ... ization |
| jose.js:11:23:11:47 | req.hea ... ization |
| jose.js:13:20:13:28 | UserToken |
| jose.js:13:20:13:28 | UserToken |
| jose.js:18:11:18:47 | UserToken |
| jose.js:18:23:18:47 | req.hea ... ization |
| jose.js:18:23:18:47 | req.hea ... ization |
| jose.js:20:26:20:34 | UserToken |
| jose.js:20:26:20:34 | UserToken |
| jose.js:24:11:24:47 | UserToken |
| jose.js:24:11:24:47 | UserToken |
| jose.js:24:23:24:47 | req.hea ... ization |
| jose.js:24:23:24:47 | req.hea ... ization |
| jose.js:24:23:24:47 | req.hea ... ization |
| jose.js:24:23:24:47 | req.hea ... ization |
| jose.js:26:20:26:28 | UserToken |
| jose.js:26:20:26:28 | UserToken |
| jose.js:27:26:27:34 | UserToken |
| jose.js:27:26:27:34 | UserToken |
| jwtDecode.js:11:11:11:47 | UserToken |
| jwtDecode.js:11:23:11:47 | req.hea ... ization |
| jwtDecode.js:11:23:11:47 | req.hea ... ization |
| jwtDecode.js:15:16:15:24 | UserToken |
| jwtDecode.js:15:16:15:24 | UserToken |
| jwtSimple.js:10:11:10:47 | UserToken |
| jwtSimple.js:10:23:10:47 | req.hea ... ization |
| jwtSimple.js:10:23:10:47 | req.hea ... ization |
| jwtSimple.js:13:23:13:31 | UserToken |
| jwtSimple.js:13:23:13:31 | UserToken |
| jwtSimple.js:17:11:17:47 | UserToken |
| jwtSimple.js:17:23:17:47 | req.hea ... ization |
| jwtSimple.js:17:23:17:47 | req.hea ... ization |
| jwtSimple.js:20:23:20:31 | UserToken |
| jwtSimple.js:20:23:20:31 | UserToken |
| jwtSimple.js:21:23:21:31 | UserToken |
| jwtSimple.js:21:23:21:31 | UserToken |
| jwtSimple.js:25:11:25:47 | UserToken |
| jwtSimple.js:25:11:25:47 | UserToken |
| jwtSimple.js:25:23:25:47 | req.hea ... ization |
| jwtSimple.js:25:23:25:47 | req.hea ... ization |
| jwtSimple.js:25:23:25:47 | req.hea ... ization |
| jwtSimple.js:25:23:25:47 | req.hea ... ization |
| jwtSimple.js:28:23:28:31 | UserToken |
| jwtSimple.js:28:23:28:31 | UserToken |
| jwtSimple.js:29:23:29:31 | UserToken |
| jwtSimple.js:29:23:29:31 | UserToken |
edges
| JsonWebToken.js:10:11:10:47 | UserToken | JsonWebToken.js:13:28:13:36 | UserToken |
| JsonWebToken.js:10:11:10:47 | UserToken | JsonWebToken.js:13:28:13:36 | UserToken |
| JsonWebToken.js:10:23:10:47 | req.hea ... ization | JsonWebToken.js:10:11:10:47 | UserToken |
| JsonWebToken.js:10:23:10:47 | req.hea ... ization | JsonWebToken.js:10:11:10:47 | UserToken |
| JsonWebToken.js:17:11:17:47 | UserToken | JsonWebToken.js:20:28:20:36 | UserToken |
| JsonWebToken.js:17:11:17:47 | UserToken | JsonWebToken.js:20:28:20:36 | UserToken |
| JsonWebToken.js:17:11:17:47 | UserToken | JsonWebToken.js:21:28:21:36 | UserToken |
| JsonWebToken.js:17:11:17:47 | UserToken | JsonWebToken.js:21:28:21:36 | UserToken |
| JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:17:11:17:47 | UserToken |
| JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:17:11:17:47 | UserToken |
| JsonWebToken.js:25:11:25:47 | UserToken | JsonWebToken.js:28:28:28:36 | UserToken |
| JsonWebToken.js:25:11:25:47 | UserToken | JsonWebToken.js:28:28:28:36 | UserToken |
| JsonWebToken.js:25:23:25:47 | req.hea ... ization | JsonWebToken.js:25:11:25:47 | UserToken |
| JsonWebToken.js:25:23:25:47 | req.hea ... ization | JsonWebToken.js:25:11:25:47 | UserToken |
| JsonWebToken.js:32:11:32:47 | UserToken | JsonWebToken.js:35:28:35:36 | UserToken |
| JsonWebToken.js:32:11:32:47 | UserToken | JsonWebToken.js:35:28:35:36 | UserToken |
| JsonWebToken.js:32:11:32:47 | UserToken | JsonWebToken.js:36:28:36:36 | UserToken |
| JsonWebToken.js:32:11:32:47 | UserToken | JsonWebToken.js:36:28:36:36 | UserToken |
| JsonWebToken.js:32:23:32:47 | req.hea ... ization | JsonWebToken.js:32:11:32:47 | UserToken |
| JsonWebToken.js:32:23:32:47 | req.hea ... ization | JsonWebToken.js:32:11:32:47 | UserToken |
| JsonWebToken.js:32:23:32:47 | req.hea ... ization | JsonWebToken.js:32:11:32:47 | UserToken |
| JsonWebToken.js:32:23:32:47 | req.hea ... ization | JsonWebToken.js:32:11:32:47 | UserToken |
| JsonWebToken.js:40:11:40:47 | UserToken | JsonWebToken.js:43:28:43:36 | UserToken |
| JsonWebToken.js:40:11:40:47 | UserToken | JsonWebToken.js:43:28:43:36 | UserToken |
| JsonWebToken.js:40:11:40:47 | UserToken | JsonWebToken.js:44:28:44:36 | UserToken |
| JsonWebToken.js:40:11:40:47 | UserToken | JsonWebToken.js:44:28:44:36 | UserToken |
| JsonWebToken.js:40:23:40:47 | req.hea ... ization | JsonWebToken.js:40:11:40:47 | UserToken |
| JsonWebToken.js:40:23:40:47 | req.hea ... ization | JsonWebToken.js:40:11:40:47 | UserToken |
| JsonWebToken.js:40:23:40:47 | req.hea ... ization | JsonWebToken.js:40:11:40:47 | UserToken |
| JsonWebToken.js:40:23:40:47 | req.hea ... ization | JsonWebToken.js:40:11:40:47 | UserToken |
| jose.js:11:11:11:47 | UserToken | jose.js:13:20:13:28 | UserToken |
| jose.js:11:11:11:47 | UserToken | jose.js:13:20:13:28 | UserToken |
| jose.js:11:23:11:47 | req.hea ... ization | jose.js:11:11:11:47 | UserToken |
| jose.js:11:23:11:47 | req.hea ... ization | jose.js:11:11:11:47 | UserToken |
| jose.js:18:11:18:47 | UserToken | jose.js:20:26:20:34 | UserToken |
| jose.js:18:11:18:47 | UserToken | jose.js:20:26:20:34 | UserToken |
| jose.js:18:23:18:47 | req.hea ... ization | jose.js:18:11:18:47 | UserToken |
| jose.js:18:23:18:47 | req.hea ... ization | jose.js:18:11:18:47 | UserToken |
| jose.js:24:11:24:47 | UserToken | jose.js:26:20:26:28 | UserToken |
| jose.js:24:11:24:47 | UserToken | jose.js:26:20:26:28 | UserToken |
| jose.js:24:11:24:47 | UserToken | jose.js:27:26:27:34 | UserToken |
| jose.js:24:11:24:47 | UserToken | jose.js:27:26:27:34 | UserToken |
| jose.js:24:23:24:47 | req.hea ... ization | jose.js:24:11:24:47 | UserToken |
| jose.js:24:23:24:47 | req.hea ... ization | jose.js:24:11:24:47 | UserToken |
| jose.js:24:23:24:47 | req.hea ... ization | jose.js:24:11:24:47 | UserToken |
| jose.js:24:23:24:47 | req.hea ... ization | jose.js:24:11:24:47 | UserToken |
| jwtDecode.js:11:11:11:47 | UserToken | jwtDecode.js:15:16:15:24 | UserToken |
| jwtDecode.js:11:11:11:47 | UserToken | jwtDecode.js:15:16:15:24 | UserToken |
| jwtDecode.js:11:23:11:47 | req.hea ... ization | jwtDecode.js:11:11:11:47 | UserToken |
| jwtDecode.js:11:23:11:47 | req.hea ... ization | jwtDecode.js:11:11:11:47 | UserToken |
| jwtSimple.js:10:11:10:47 | UserToken | jwtSimple.js:13:23:13:31 | UserToken |
| jwtSimple.js:10:11:10:47 | UserToken | jwtSimple.js:13:23:13:31 | UserToken |
| jwtSimple.js:10:23:10:47 | req.hea ... ization | jwtSimple.js:10:11:10:47 | UserToken |
| jwtSimple.js:10:23:10:47 | req.hea ... ization | jwtSimple.js:10:11:10:47 | UserToken |
| jwtSimple.js:17:11:17:47 | UserToken | jwtSimple.js:20:23:20:31 | UserToken |
| jwtSimple.js:17:11:17:47 | UserToken | jwtSimple.js:20:23:20:31 | UserToken |
| jwtSimple.js:17:11:17:47 | UserToken | jwtSimple.js:21:23:21:31 | UserToken |
| jwtSimple.js:17:11:17:47 | UserToken | jwtSimple.js:21:23:21:31 | UserToken |
| jwtSimple.js:17:23:17:47 | req.hea ... ization | jwtSimple.js:17:11:17:47 | UserToken |
| jwtSimple.js:17:23:17:47 | req.hea ... ization | jwtSimple.js:17:11:17:47 | UserToken |
| jwtSimple.js:25:11:25:47 | UserToken | jwtSimple.js:28:23:28:31 | UserToken |
| jwtSimple.js:25:11:25:47 | UserToken | jwtSimple.js:28:23:28:31 | UserToken |
| jwtSimple.js:25:11:25:47 | UserToken | jwtSimple.js:29:23:29:31 | UserToken |
| jwtSimple.js:25:11:25:47 | UserToken | jwtSimple.js:29:23:29:31 | UserToken |
| jwtSimple.js:25:23:25:47 | req.hea ... ization | jwtSimple.js:25:11:25:47 | UserToken |
| jwtSimple.js:25:23:25:47 | req.hea ... ization | jwtSimple.js:25:11:25:47 | UserToken |
| jwtSimple.js:25:23:25:47 | req.hea ... ization | jwtSimple.js:25:11:25:47 | UserToken |
| jwtSimple.js:25:23:25:47 | req.hea ... ization | jwtSimple.js:25:11:25:47 | UserToken |
#select
| JsonWebToken.js:10:23:10:47 | req.hea ... ization | JsonWebToken.js:10:23:10:47 | req.hea ... ization | JsonWebToken.js:13:28:13:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:13:28:13:36 | UserToken | without signature verification |
| JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:20:28:20:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:20:28:20:36 | UserToken | without signature verification |
| JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:17:23:17:47 | req.hea ... ization | JsonWebToken.js:21:28:21:36 | UserToken | Decoding JWT $@. | JsonWebToken.js:21:28:21:36 | UserToken | without signature verification |
| jose.js:11:23:11:47 | req.hea ... ization | jose.js:11:23:11:47 | req.hea ... ization | jose.js:13:20:13:28 | UserToken | Decoding JWT $@. | jose.js:13:20:13:28 | UserToken | without signature verification |
| jwtDecode.js:11:23:11:47 | req.hea ... ization | jwtDecode.js:11:23:11:47 | req.hea ... ization | jwtDecode.js:15:16:15:24 | UserToken | Decoding JWT $@. | jwtDecode.js:15:16:15:24 | UserToken | without signature verification |
| jwtSimple.js:10:23:10:47 | req.hea ... ization | jwtSimple.js:10:23:10:47 | req.hea ... ization | jwtSimple.js:13:23:13:31 | UserToken | Decoding JWT $@. | jwtSimple.js:13:23:13:31 | UserToken | without signature verification |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-347/decodeJwtWithoutVerification.ql

View File

@@ -0,0 +1,32 @@
const express = require('express')
const app = express()
const jose = require('jose')
const port = 3000
function getSecret() {
return "A Safe generated random key"
}
app.get('/jose1', (req, res) => {
const UserToken = req.headers.authorization;
// no signature verification
jose.decodeJwt(UserToken) // NOT OK
})
app.get('/jose2', async (req, res) => {
const UserToken = req.headers.authorization;
// with signature verification
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret())) // OK
})
app.get('/jose3', async (req, res) => {
const UserToken = req.headers.authorization;
// first without signature verification then with signature verification for same UserToken
jose.decodeJwt(UserToken) // OK
await jose.jwtVerify(UserToken, new TextEncoder().encode(getSecret())) // OK
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})

View File

@@ -0,0 +1,20 @@
const express = require('express')
const app = express()
const jwt_decode = require('jwt-decode');
const port = 3000
function getSecret() {
return "A Safe generated random key"
}
app.get('/jwtDecode', (req, res) => {
const UserToken = req.headers.authorization;
// jwt-decode
// no signature verification
jwt_decode(UserToken) // NOT OK
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})

View File

@@ -0,0 +1,34 @@
const express = require('express')
const app = express()
const jwt_simple = require('jwt-simple');
const port = 3000
function getSecret() {
return "A Safe generated random key"
}
app.get('/jwtSimple1', (req, res) => {
const UserToken = req.headers.authorization;
// no signature verification
jwt_simple.decode(UserToken, getSecret(), true); // NOT OK
})
app.get('/jwtSimple2', (req, res) => {
const UserToken = req.headers.authorization;
// GOOD: all with with signature verification
jwt_simple.decode(UserToken, getSecret(), false); // OK
jwt_simple.decode(UserToken, getSecret()); // OK
})
app.get('/jwtSimple3', (req, res) => {
const UserToken = req.headers.authorization;
// GOOD: first without signature verification then with signature verification for same UserToken
jwt_simple.decode(UserToken, getSecret(), true); // OK
jwt_simple.decode(UserToken, getSecret()); // OK
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})