Cookie-Flags
Teil des Abschnitts Authentifizierung und Sicherheit der Node.js-Journey von Coddy. Lektion 13 von 24.
Ein Cookie ohne Flags wird über einfaches HTTP übertragen, bei Anfragen mitgesendet, die andere Websites auslösen, und bleibt bestehen, bis der Browser geschlossen wird. Vier Optionen bei res.cookie schränken das ein:
httpOnly: Skripte auf der Seite können es nicht lesen.sameSite:'lax'verhindert, dass es bei Anfragen mitgesendet wird, die andere Websites senden, außer wenn der Benutzer einem Link folgt;'strict'verhindert auch das.secure: wird nur über HTTPS gesendet, niemals über einfaches HTTP, wo andere im Netzwerk es lesen können.maxAge: wie lange es gültig ist, in Millisekunden.
res.cookie('cart', cartId, {
httpOnly: true,
sameSite: 'strict',
secure: true,
maxAge: 30 * 60 * 1000, // 30 Minuten
});Aufgabe
EinfachSchränke das sid-Cookie so ein, dass Browser es nur dort senden, wo es hingehört, und es nach einem Tag vergessen.
- Gib in
server.jsunterPOST /logindemsid-Cookie diese vier Optionen:httpOnly, damit Skripte auf der Seite es nicht lesen können. Diese Option ist bereits gesetzt.- Setze
sameSiteauf'lax', damit der Browser es bei Anfragen, die von anderen Websites gestartet werden, nicht mitsendet, es sei denn, der Benutzer folgt einem Link zu deiner Website. secure, damit es nur über HTTPS gesendet wird.maxAgeauf einen Tag.maxAgewird in Millisekunden angegeben, und der Header zeigt dieselbe Zeit in Sekunden an. Ein Tag erscheint daher alsMax-Age=86400.
- Die Anmeldung muss weiterhin funktionieren: Nach
POST /logingibtGET /meden Benutzer zurück.
Probiere es aus: Klicke auf „Ausführen“. Sende POST /signup und dann POST /login, jeweils mit {"username": "ada", "password": "correct horse"}. Öffne den Tab „Kopfzeilen“ der Anmeldeantwort. Set-Cookie sieht etwa so aus: sid=oqjRxvxmXl_nRwAwOLxgZyI2sYq9hZtN; Max-Age=86400; Path=/; Expires=Sat, 31 Oct 2026 12:00:00 GMT; HttpOnly; Secure; SameSite=Lax. Sende GET /me: {"username": "ada"}.
Probier es selbst
import express from 'express';
import { randomBytes } from 'node:crypto';
import { parseCookies } from './cookies.js';
import { notes, sessions, users } from './db.js';
import { hashPassword, verifyPassword } from './passwords.js';
const app = express();
app.use(express.json());
app.post('/signup', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const password = String(req.body.password ?? '');
if (!username || password.length < 8) {
return res.status(400).json({ error: 'Pick a username and a password of at least 8 characters' });
}
if (await users.findOne({ username })) {
return res.status(409).json({ error: 'That username is taken' });
}
await users.insertOne({ username, passwordHash: await hashPassword(password) });
res.status(201).json({ username });
});
app.post('/login', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const user = await users.findOne({ username });
if (!user || !(await verifyPassword(String(req.body.password ?? ''), user.passwordHash))) {
return res.status(401).json({ error: 'Wrong username or password' });
}
const sid = randomBytes(24).toString('base64url');
await sessions.insertOne({ _id: sid, userId: user._id, createdAt: new Date() });
res.cookie('sid', sid, { httpOnly: true });
res.json({ username: user.username });
});
// Ermittelt den Benutzer anhand des Sitzungscookies oder antwortet mit 401.
async function requireUser(req, res, next) {
const { sid } = parseCookies(req.get('cookie'));
const session = sid && (await sessions.findOne({ _id: sid }));
const user = session && (await users.findOne({ _id: session.userId }));
if (!user) {
return res.status(401).json({ error: 'Log in first' });
}
req.user = { id: user._id.toHexString(), username: user.username };
next();
}
app.get('/me', requireUser, (req, res) => {
res.json({ username: req.user.username });
});
app.post('/logout', async (req, res) => {
const { sid } = parseCookies(req.get('cookie'));
if (sid) {
await sessions.deleteOne({ _id: sid });
}
res.clearCookie('sid');
res.status(204).end();
});
// Zählt, wie oft dieser Browser GET /visit aufgerufen hat.
app.get('/visit', (req, res) => {
const cookies = parseCookies(req.get('cookie'));
const visits = Number(cookies.visits ?? 0) + 1;
res.cookie('visits', String(visits));
res.json({ visits });
});
// Notizen: Jeder Benutzer liest und schreibt nur seine eigenen.
app.get('/notes', requireUser, async (req, res) => {
res.json(await notes.find({ owner: req.user.id }).toArray());
});
app.post('/notes', requireUser, async (req, res) => {
const note = { owner: req.user.id, text: String(req.body.text ?? '') };
await notes.insertOne(note);
res.status(201).json(note);
});
app.listen(3000, () => {
console.log('Vault is running on port 3000');
});
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Authentifizierung und Sicherheit
2Sitzungen und Cookies
Mit Cookies erinnernEine Sitzung nachschlagenAnmeldesitzungenRouten hinter einer AnmeldungÜbe selbstständig: Express-Playground