agentggagentgg
Back to all findings
CRITICALconfirmedjwt-handlinghardcoded-private-key3596b2b31048

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.

Filelib/insecurity.ts
Lines2424
Confidence
95%
File statusvalidated
Details
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.

Proof of concept
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 verification
Impact

Universal 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.

Validation
confirmed

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.

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Base score: 9.8 · CRITICAL

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.

References