RSA signing private key hardcoded in source
The RSA private key used by `authorize()` to mint session JWTs is a hardcoded string literal in `lib/insecurity.ts`, so anyone with read access to the repo (or a deployed source map / package) can sign arbitrary tokens.
const privateKey = '-----BEGIN RSA PRIVATE KEY-----\r\nMIICXAIBAAKBgQDNwqLEe9wgTXCbC7+RPdDb...\r\n-----END RSA PRIVATE KEY-----'
...
export const authorize = (user = {}) => jwt.sign(user, privateKey, { expiresIn: '6h', algorithm: 'RS256' })
The private key is committed to the repository in plain text. authorize() and deluxeToken() both use this key (the latter as an HMAC secret), so token forgery is trivial for anyone with access to the public source — every Juice Shop deployment shares the same signing key.
There is also no issuer/audience claim on the issued tokens, so a forged token from one deployment is accepted by every other deployment that uses the same key.
const jwt = require('jsonwebtoken');
const privateKey = `-----BEGIN RSA PRIVATE KEY-----\nMIICXAIB...same as repo...\n-----END RSA PRIVATE KEY-----`;
const token = jwt.sign(
{ status: 'success', data: { id: 1, email: 'admin@juice-sh.op', role: 'admin' } },
privateKey,
{ algorithm: 'RS256', expiresIn: '6h' }
);
// POST /api/Users/ with Cookie: token=<token> — passes verificationUniversal authentication bypass on every Juice Shop instance using the upstream key material. An attacker with no prior access can mint admin-role tokens and impersonate any user.
Line 24 contains a literal RSA private key string, and authorize = (user = {}) => jwt.sign(user, privateKey, { expiresIn: '6h', algorithm: 'RS256' }) uses it directly; the corresponding publicKey from encryptionkeys/jwt.pub is the upstream-repo counterpart, so any attacker can sign a token offline that passes verify()/expressJwt({ secret: publicKey }). deluxeToken() additionally uses the same private key as an HMAC secret, compounding the issue. Per scope, the training-app nature does not excuse this. Exploit chain is reachable from an unauthenticated attacker by submitting the forged JWT as a cookie/header.
The privateKey string literal at line 24 is the RS256 signing key used by authorize() and is paired with the publicly readable encryptionkeys/jwt.pub consumed by isAuthorized()/verify(). Because the key is hardcoded in the open-source repo, any unauthenticated remote attacker can mint a valid token with {data: {role: 'admin', id: 1, ...}}, send it as the token cookie/Authorization header, and pass jwt.verify/jws.verify — no auth, no UI, no preconditions. This yields full admin impersonation across every deployment using the upstream key, so confidentiality, integrity, and availability of all application data and admin functions are fully compromised; scope stays Unchanged since the impact is within the application's own authority.
- CWE-798
- CWE-321