Express-based JWT security lab for learning secure token handling, attack techniques, and safe implementation patterns.
Start here: Open WALKTHROUGH.md — it gives you step-by-step instructions with copy-paste commands and tells you exactly what to look for at each stage.
Keywords: JWT, security, auth, Express, lab, vulnerability, learning
.
├── src/
│ ├── server.js ← The SAFE server (port 3010) — study this
│ ├── auth.js ← The SAFE token code — study this
│ ├── auth_rs256.js ← RS256 demo — read after auth.js
│ ├── server_vulnerable.js ← The BROKEN server (port 3011) — attack this
│ └── auth_vulnerable.js ← 8 broken patterns — read and attack these
├── WALKTHROUGH.md ← ⭐ Start here — full step-by-step lab guide
├── .env.example ← Copy this to .env before starting
├── .env.vulnerable.example ← Copy this to .env.vulnerable before starting
└── .gitignore
# 1. Install packages
npm install
# 2. Create your environment files
cp .env.example .env
cp .env.vulnerable.example .env.vulnerable
# 3. Add a strong secret to .env
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
# Copy the output and paste it as the JWT_SECRET value in .env
# 4. Start both servers (use two separate terminal windows)
node src/server.js # Terminal 1 — safe server on port 3010
node src/server_vulnerable.js # Terminal 2 — vulnerable server on port 3011This is how token auth should be written. Study the code here.
| Method | Route | What it does |
|---|---|---|
| POST | /auth/login |
Send username + password, get a token |
| GET | /api/me |
See your own user data |
| GET | /api/admin |
Admin only — bob cannot enter here |
Test accounts: alice / correct (admin) and bob / password (user)
This is your attack target. Do not copy this code.
| Method | Route | What to do here |
|---|---|---|
| POST | /login |
Get a token and decode the payload — notice the personal data |
| GET | /api/admin_data |
Try to forge your way in |
| GET | /api/me |
Confirm the personal data is really in the token |
| # | Problem | Where it is | What an attacker can do |
|---|---|---|---|
| 1 | alg:none attack | auth_vulnerable.js |
Create a fake token with no signature |
| 2 | Algorithm confusion | auth_vulnerable.js |
Use the public key to forge tokens |
| 3 | Weak secret | .env.vulnerable |
Crack the secret in seconds using a word list |
| 4 | Personal data in payload | auth_vulnerable.js |
Read private data from the token without any key |
| 5 | Token never expires | auth_vulnerable.js |
Use a stolen token for 99 years |
| 6 | No issuer or audience | auth_vulnerable.js |
Use a token on the wrong server |
| 7 | localStorage advice | server_vulnerable.js |
Steal tokens via XSS |
| 8 | KID injection | auth_vulnerable.js |
Trick the server into loading the wrong key |
HS256 uses one key to both create and check tokens. Good when one server does everything. Problem: every server that checks tokens also gets the ability to CREATE them.
RS256 uses two keys:
- Private key — creates tokens. Only the login server has this.
- Public key — checks tokens. Safe to give to any server.
Good for when you have many separate servers. Even if one server is compromised, the attacker cannot create fake tokens.
To generate the two keys:
openssl genrsa -out private.key 2048
openssl rsa -in private.key -pubout -out public.keyRead src/auth_rs256.js for the full explanation.
server_vulnerable.js and auth_vulnerable.js are broken on purpose for this lab.
Never use these patterns in a real application.
For better discoverability on GitHub,i added these repository topics:
jwtsecurityexpressnodejslabvulnerabilityauth
These tags help users find the project when searching for JWT, security, or Express labs.