diff --git a/javascript/ql/src/Security/CWE-347-HardCodedKey/Example.js b/javascript/ql/src/Security/CWE-347-HardCodedKey/Example.js deleted file mode 100644 index 8d5486d8a21..00000000000 --- a/javascript/ql/src/Security/CWE-347-HardCodedKey/Example.js +++ /dev/null @@ -1,49 +0,0 @@ -const express = require('express') -const jwtJsonwebtoken = require("jsonwebtoken"); -const jose = require("jose"); -const jwt_simple = require("jwt-simple"); -const app = express() -const port = 3000 - -function getSecret() { - return "secret" -} - -async function startSymmetric(token) { - const {payload, protectedHeader} = await jose.jwtVerify(token, new TextEncoder().encode(getSecret())) - return { - payload, protectedHeader - } -} - -async function startRSA(token) { - const spki = `-----BEGIN PUBLIC KEY----- - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwhYOFK2Ocbbpb/zVypi9 - SeKiNUqKQH0zTKN1+6fpCTu6ZalGI82s7XK3tan4dJt90ptUPKD2zvxqTzFNfx4H - HHsrYCf2+FMLn1VTJfQazA2BvJqAwcpW1bqRUEty8tS/Yv4hRvWfQPcc2Gc3+/fQ - OOW57zVy+rNoJc744kb30NjQxdGp03J2S3GLQu7oKtSDDPooQHD38PEMNnITf0pj - +KgDPjymkMGoJlO3aKppsjfbt/AH6GGdRghYRLOUwQU+h+ofWHR3lbYiKtXPn5dN - 24kiHy61e3VAQ9/YAZlwXC/99GGtw/NpghFAuM4P1JDn0DppJldy3PGFC0GfBCZA - SwIDAQAB - -----END PUBLIC KEY-----` - const publicKey = await jose.importSPKI(spki, 'RS256') - const {payload, protectedHeader} = await jose.jwtVerify(token, publicKey, { - issuer: 'urn:example:issuer', - audience: 'urn:example:audience', - }) - console.log(protectedHeader) - console.log(payload) -} - -app.get('/', (req, res) => { - const UserToken = req.headers.authorization; - startSymmetric(UserToken).then() - startRSA(UserToken).then() - jwt_simple.decode(UserToken, getSecret()); - jwtJsonwebtoken.verify(UserToken, getSecret()) - res.send('Hello World!') -}) - -app.listen(port, () => { - console.log(`Example app listening on port ${port}`) -}) diff --git a/javascript/ql/src/Security/CWE-347-HardCodedKey/jwtConstantKey.qhelp b/javascript/ql/src/Security/CWE-347-HardCodedKey/jwtConstantKey.qhelp deleted file mode 100644 index 7ce51daabeb..00000000000 --- a/javascript/ql/src/Security/CWE-347-HardCodedKey/jwtConstantKey.qhelp +++ /dev/null @@ -1,29 +0,0 @@ - - - -

- A JSON Web Token (JWT) is used for authenticating and managing users in an application. -

-

- Decoding JWTs with a Constant hardcoded secret key can lead to security vulnerabilities. -

- -
- - -

- Generate seceret key in application startup with secure randome generators. -

- -
- - -

- The following code you can see an Example from a popular Library. -

- - - -
- -
\ No newline at end of file diff --git a/javascript/ql/src/Security/CWE-347-HardCodedKey/jwtConstantKey.ql b/javascript/ql/src/Security/CWE-347-HardCodedKey/jwtConstantKey.ql deleted file mode 100644 index d5f594ba0dd..00000000000 --- a/javascript/ql/src/Security/CWE-347-HardCodedKey/jwtConstantKey.ql +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @name Usage of Constant Secret key for decoding JWT - * @description Hardcoded Secrets leakage can lead to authentication or authorization bypass - * @kind path-problem - * @problem.severity error - * @precision high - * @id javascript/jwt-hardcoded-key - * @tags security - * experimental - * external/cwe/CWE-347 - */ - -import javascript -import DataFlow::PathGraph -import DataFlow - -class JWTDecodeConfig extends TaintTracking::Configuration { - JWTDecodeConfig() { this = "JWTConfig" } - - override predicate isSource(DataFlow::Node source) { - source.asExpr() instanceof ConstantString and - // following prevent custom secret key generators that exist in source code - not source.asExpr().mayHaveStringValue(["", " ", any(string s | s.length() = 1)]) - } - - override predicate isSanitizer(DataFlow::Node node) { - node.getFile() - .getLocation() - .hasLocationInfo(any(string s | s.matches(["%test%", "%demo%", "%example%", "%sample%"])), - _, _, _, _) - } - - override predicate isSink(DataFlow::Node sink) { - sink = API::moduleImport("jsonwebtoken").getMember(["sign", "verify"]).getParameter(1).asSink() or - sink = API::moduleImport("jose").getMember("jwtVerify").getParameter(1).asSink() or - sink = API::moduleImport("jwt-simple").getMember("decode").getParameter(1).asSink() or - sink = API::moduleImport("next-auth").getParameter(0).getMember("secret").asSink() or - sink = API::moduleImport("koa-jwt").getParameter(0).getMember("secret").asSink() or - sink = - API::moduleImport("express-jwt") - .getMember("expressjwt") - .getParameter(0) - .getMember("secret") - .asSink() or - sink = - API::moduleImport("passport-jwt") - .getMember("Strategy") - .getParameter(0) - .getMember("secretOrKey") - .asSink() - } - - override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { - exists(DataFlow::CallNode n, DataFlow::NewNode nn | - n.getCalleeName() = "encode" and - nn.flowsTo(n.getReceiver()) and - nn.getCalleeName() = "TextEncoder" - | - pred = n.getArgument(0) and - succ = n - ) - or - exists(API::Node n | n = API::moduleImport("jose").getMember("importSPKI") | - pred = n.getACall().getArgument(0) and - succ = n.getReturn().getPromised().asSource() - ) - or - exists(API::Node n | n = API::moduleImport("jose").getMember("base64url").getMember("decode") | - pred = n.getACall().getArgument(0) and - succ = n.getACall() - ) - or - exists(DataFlow::CallNode n | n = DataFlow::globalVarRef("Buffer").getAMemberCall("from") | - pred = n.getArgument(0) and - succ = [n, n.getAChainedMethodCall(["toString", "toJSON"])] - ) - } -} - -from JWTDecodeConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink -where cfg.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "this $@. is used as a secret key", source.getNode(), - "Constant" diff --git a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/Config.js b/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/Config.js deleted file mode 100644 index 9d32caad4fa..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/Config.js +++ /dev/null @@ -1,6 +0,0 @@ -function getSecret() { - return "secret" -} - -const DoValidate = false -module.exports = {getSecret, DoValidate} diff --git a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/ExpressJWT.js b/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/ExpressJWT.js deleted file mode 100644 index b3a09924bab..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/ExpressJWT.js +++ /dev/null @@ -1,14 +0,0 @@ -const expressjwt = require("express-jwt"); -var {getSecret} = require('./Config.js'); -app.get( - "/protected", - expressjwt.expressjwt({secret: getSecret(), algorithms: ["HS256"]}), - function (req, res) { - if (!req.auth.admin) return res.sendStatus(401); - res.sendStatus(200); - } -); -expressjwt.expressjwt({ - secret: Buffer.from(getSecret(), "base64"), - algorithms: ["RS256"], -}); diff --git a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/jwtConstantKey.expected b/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/jwtConstantKey.expected deleted file mode 100644 index ac992895b9c..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/jwtConstantKey.expected +++ /dev/null @@ -1,3 +0,0 @@ -nodes -edges -#select diff --git a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/jwtConstantKey.js b/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/jwtConstantKey.js deleted file mode 100644 index 1c6de23f65e..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/jwtConstantKey.js +++ /dev/null @@ -1,52 +0,0 @@ -// jsonwebtoken -const jwtJsonwebtoken = require('jsonwebtoken'); -const {getSecret} = require('./Config.js'); -const payloads = {foo: 'bar'} -const token = jwtJsonwebtoken.sign(payloads, getSecret()); -console.log(jwtJsonwebtoken.verify(token, getSecret())) - -// jose -const jose = require('jose') - -async function startSymmetric() { - const {payload, protectedHeader} = await jose.jwtVerify(token, new TextEncoder().encode(getSecret())) - return { - payload, protectedHeader - } -} - -startSymmetric().then(result => console.log(result)) - -const alg = 'RS256' -const spki = `-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwhYOFK2Ocbbpb/zVypi9 -SeKiNUqKQH0zTKN1+6fpCTu6ZalGI82s7XK3tan4dJt90ptUPKD2zvxqTzFNfx4H -HHsrYCf2+FMLn1VTJfQazA2BvJqAwcpW1bqRUEty8tS/Yv4hRvWfQPcc2Gc3+/fQ -OOW57zVy+rNoJc744kb30NjQxdGp03J2S3GLQu7oKtSDDPooQHD38PEMNnITf0pj -+KgDPjymkMGoJlO3aKppsjfbt/AH6GGdRghYRLOUwQU+h+ofWHR3lbYiKtXPn5dN -24kiHy61e3VAQ9/YAZlwXC/99GGtw/NpghFAuM4P1JDn0DppJldy3PGFC0GfBCZA -SwIDAQAB ------END PUBLIC KEY-----` -const jwt2 = - 'eyJhbGciOiJSUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZSwiaWF0IjoxNjY5MDU2NDg4LCJpc3MiOiJ1cm46ZXhhbXBsZTppc3N1ZXIiLCJhdWQiOiJ1cm46ZXhhbXBsZTphdWRpZW5jZSJ9.gXrPZ3yM_60dMXGE69dusbpzYASNA-XIOwsb5D5xYnSxyj6_D6OR_uR_1vqhUm4AxZxcrH1_-XJAve9HCw8az_QzHcN-nETt-v6stCsYrn6Bv1YOc-mSJRZ8ll57KVqLbCIbjKwerNX5r2_Qg2TwmJzQdRs-AQDhy-s_DlJd8ql6wR4n-kDZpar-pwIvz4fFIN0Fj57SXpAbLrV6Eo4Byzl0xFD8qEYEpBwjrMMfxCZXTlAVhAq6KCoGlDTwWuExps342-0UErEtyIqDnDGcrfNWiUsoo8j-29IpKd-w9-C388u-ChCxoHz--H8WmMSZzx3zTXsZ5lXLZ9IKfanDKg' - -async function startRSA() { - var publicKey = await jose.importSPKI(spki, alg) - var {payload, protectedHeader} = await jose.jwtVerify(jwt2, publicKey, { - issuer: 'urn:example:issuer', - audience: 'urn:example:audience', - }) - console.log(protectedHeader) - console.log(payload) -} - -startRSA() -// additional step -console.log("jose.base64url.decode()", Buffer.from(jose.base64url.decode(token), "base64").toString()) - -// jwt-simple -const jwt_simple = require('jwt-simple'); -// var secret = Buffer.from('fe1a1915a379f3be5394b64d14794932', 'hex') -// decode -const decoded = jwt_simple.decode(token, getSecret() + "fj"); -console.log("jwt_simple:", decoded); //=> { foo: 'bar' } diff --git a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/jwtConstantKey.qlref b/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/jwtConstantKey.qlref deleted file mode 100644 index 50490c02175..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/jwtConstantKey.qlref +++ /dev/null @@ -1 +0,0 @@ -Security/CWE-347-HardCodedKey/jwtConstantKey.ql \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/koaJWT.js b/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/koaJWT.js deleted file mode 100644 index e25811a0de4..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/koaJWT.js +++ /dev/null @@ -1,38 +0,0 @@ -var Koa = require('koa'); -var jwt = require('koa-jwt'); -var {getSecret} = require('./Config.js'); - -var app = new Koa(); - -// Custom 401 handling if you don't want to expose koa-jwt errors to users -app.use(function (ctx, next) { - return next().catch((err) => { - if (401 === err.status) { - ctx.status = 401; - ctx.body = 'Protected resource, use Authorization header to get access\n'; - } else { - throw err; - } - }); -}); - -// Unprotected middleware -app.use(function (ctx, next) { - if (ctx.url.match(/^\/public/)) { - ctx.body = 'unprotected\n'; - } else { - return next(); - } -}); - -// Middleware below this line is only reached if JWT token is valid -app.use(jwt({secret: getSecret()})); - -// Protected middleware -app.use(function (ctx) { - if (ctx.url.match(/^\/api/)) { - ctx.body = 'protected\n'; - } -}); - -app.listen(3000); \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/netxAuth.js b/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/netxAuth.js deleted file mode 100644 index 3425f1b59e0..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/netxAuth.js +++ /dev/null @@ -1,26 +0,0 @@ -// pages/api/auth/[...nextauth].js -import NextAuth from "next-auth" -import AppleProvider from "next-auth/providers/apple" -import GoogleProvider from "next-auth/providers/google" -import EmailProvider from "next-auth/providers/email" - -var {getSecret} = require('./Config.js'); - -NextAuth({ - secret: getSecret(), - providers: [ - AppleProvider({ - clientId: process.env.APPLE_ID, - clientSecret: process.env.APPLE_SECRET, - }), - GoogleProvider({ - clientId: process.env.GOOGLE_ID, - clientSecret: process.env.GOOGLE_SECRET, - }), - // Sign in with passwordless email link - EmailProvider({ - server: process.env.MAIL_SERVER, - from: "", - }), - ], -}) \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/passportJWT.js b/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/passportJWT.js deleted file mode 100644 index df3f0351891..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-347-HardCodedKey/passportJWT.js +++ /dev/null @@ -1,13 +0,0 @@ -var JwtStrategy = require('passport-jwt').Strategy, - ExtractJwt = require('passport-jwt').ExtractJwt; -var {getSecret} = require('./Config.js'); -var opts = {} -opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken(); -opts.secretOrKey = getSecret(); -opts.issuer = 'accounts.examplesoft.com'; -opts.audience = 'yoursite.net'; -const newPassportUse = new JwtStrategy(opts, function (jwt_payload, done) { - - return done(null, false); - -})