Summary

In the lab we’re going to build today, we’ll talk about JWTs and how they can affect the security of your website. This is a lab that’s been requested, and I could not be happier that the community is actually suggesting things for me to build. It’s a privilege to do this. JWT As Security Measure JSON Web Tokens are used a lot by developers. They provide a way to send ‘data’ with them (e.g., a role from a user), to check for authorization. A JWT consists of 3 parts, separated by a . . Let’s take this JWT as an example. eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30 The first part eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 is called ‘the header’. It holds information on the type of the JWT. The second part eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0 is the payload, which holds the claims. This is where that ‘data’ lives. If you decode this with Base64url you’ll learn that this token belongs to John Doe and that he is an administrator. The last part is the important part. This is the signature. Each JWT should be signed with a strong password. Ideally that password is saved in a .env file on the server and not in the code. So now you might wonder, well if it’s signed, nothing can go wrong right? Well, if that were true, I wouldn’t spend my evenings writing these kinds of blog posts now would I 😅. Let’s Build The Lab Already You’re right to think this by now. Enough theory, let’s do some hands on keyboard. As always in this series, I’ll provide you with the lab structure and some code to get us started. Lab Tree /mediumLabs └── /JWT ├── server.js ├── login.html ├── index.html ├── admin.html ├── /node_modules ├── /package-lock.json └── /package.json Code server.js const express = require(‘express’); const jwt = require(‘jsonwebtoken’); const path = require(‘path’); const Database = require(‘better-sqlite3’); const app = express(); const JWT_SECRET = ‘secret’; const db = new Database(‘:memory:’); db.exec(CREATE TABLE users ( id INTEGER PRIMARY KEY, username TEXT, password TEXT, role TEXT )); db.prepare(“INSERT INTO users (username, password, role) VALUES (‘user’, ‘password’, ‘user’)“).run(); app.use(express.urlencoded({ extended: false })); app.get(’/’, (req, res) res.sendFile(path.join(__dirname, ‘login.html’))); function getToken(req) { const match = (req.headers.cookie || ”).match(/token=([^;]+)/); return match ? match[1] : null; } app.post(‘/login’, (req, res) { const { username, password } = req.body; const user = db.prepare(‘SELECT * FROM users WHERE username = ? AND password = ?‘).get(username, password); if (!user) return res.redirect(’/?error=Invalid+credentials’); const token = jwt.sign({ username: user.username, role: user.role }, JWT_SECRET); res.setHeader(‘Set-Cookie’, token=${token}; Path=/); res.redirect(‘/index.html’); }); app.get(‘/index.html’, (req, res) { try { res.sendFile(path.join(__dirname, ‘index.html’)); } catch { res.redirect(’/’); } }); app.get(‘/admin’, (req, res) { }); app.listen(3000, () console.log(‘Listening on http://localhost:3000’)); login.html

Login

Login

index.html Home

Welcome!

You are logged in as .

Go to Admin Panel


admin.html Admin Panel

Admin Panel

Welcome, admin. Here is the secret flag:

FLAG{JWT}


Back to Home Let’s Add Some Vulnerabilities Ok, so now we have our base code, and we need to implement the functionality that will make sure only people with the role admin can navigate to /admin . First off, run npm run dev to see your code actually start. In a very, very wrong way we already tried implementing security in admin.html . When you look inside the