Flag Cookie
Termasuk bagian Autentikasi dan Keamanan dalam perjalanan Node.js di Coddy. Pelajaran 13 dari 24.
Cookie tanpa flag dikirim melalui HTTP biasa, ikut dikirim bersama permintaan yang dipicu situs lain, dan bertahan hingga browser ditutup. Empat opsi pada res.cookie memperketatnya:
httpOnly: skrip di halaman tidak dapat membacanya.sameSite:'lax'mencegah cookie dikirim bersama permintaan yang dikirim situs lain, kecuali saat pengguna mengikuti tautan;'strict'juga mencegah cookie dikirim saat itu.secure: cookie hanya dikirim melalui HTTPS, tidak pernah melalui HTTP biasa yang memungkinkan orang lain di jaringan membacanya.maxAge: berapa lama cookie bertahan, dalam milidetik.
res.cookie('cart', cartId, {
httpOnly: true,
sameSite: 'strict',
secure: true,
maxAge: 30 * 60 * 1000, // 30 menit
});Tantangan
MudahPerketat cookie sid, agar browser hanya mengirimkannya ke tempat yang semestinya dan melupakannya setelah satu hari.
- Di
server.js, dalamPOST /login, berikan empat opsi ini pada cookiesid:httpOnly, agar skrip di halaman tidak dapat membacanya. Opsi ini sudah ada.- Atur
sameSiteke'lax', agar browser tidak menyertakannya dalam permintaan yang dimulai oleh situs lain, kecuali jika pengguna mengikuti tautan ke situsmu. secure, agar cookie hanya dikirim melalui HTTPS.maxAgeselama satu hari. NilaimaxAgemenggunakan milidetik, sedangkan header menampilkan waktu yang sama dalam detik, jadi satu hari ditampilkan sebagaiMax-Age=86400.
- Proses login harus tetap berfungsi: setelah
POST /login,GET /memengembalikan pengguna.
Coba: Tekan tombol Jalankan. Kirim POST /signup, lalu POST /login, keduanya dengan {"username": "ada", "password": "correct horse"}. Buka tab Headers pada respons login. Set-Cookie akan terlihat seperti sid=oqjRxvxmXl_nRwAwOLxgZyI2sYq9hZtN; Max-Age=86400; Path=/; Expires=Sat, 31 Oct 2026 12:00:00 GMT; HttpOnly; Secure; SameSite=Lax. Kirim GET /me: {"username": "ada"}.
Coba sendiri
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 });
});
// Mencari pengguna dari cookie sesi, atau mengembalikan 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();
});
// Menghitung berapa kali browser ini memanggil GET /visit.
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 });
});
// Catatan: setiap pengguna hanya membaca dan menulis catatannya sendiri.
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');
});
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Autentikasi dan Keamanan
Latihan sendiri: Playground Express