Builders Academy
OWASP Top 10 — Vulnérabilités Web Critiques
3 of 3

Chapitre 1 · Leçon 3

Broken Access Control

IDOR, escalade de privilèges et contrôle d'accès

Section 1

IDOR — Insecure Direct Object Reference

Step 1

La vulnérabilité IDOR

IDOR : accéder à une ressource qui appartient à un autre utilisateur en changeant simplement l'ID dans l'URL. C'est la vulnérabilité #1 selon OWASP 2021.

snippet.typescript

typescript
1// ❌ Endpoint vulnérable à l'IDOR
2app.get('/api/invoices/:id', authenticate, async (req, res) => {
3  const invoice = await prisma.invoice.findUnique({
4    where: { id: req.params.id },
5  });
6  res.json(invoice);
7  // Si je suis l'utilisateur 42 et que je demande /api/invoices/99,
8  // je reçois la facture de l'utilisateur 99 !
9});
10
11// ✅ Correction : toujours filtrer par l'utilisateur authentifié
12app.get('/api/invoices/:id', authenticate, async (req, res) => {
13  const invoice = await prisma.invoice.findFirst({
14    where: {
15      id: req.params.id,
16      userId: req.user.id,  // ← LA LIGNE ESSENTIELLE
17    },
18  });
19
20  if (!invoice) {
21    // Ne pas révéler si la ressource existe (404 même si 403)
22    return res.status(404).json({ error: "Facture introuvable" });
23  }
24
25  res.json(invoice);
26});
27
28// ✅ Middleware de vérification réutilisable
29async function requireOwnership(
30  req: Request,
31  res: Response,
32  next: NextFunction
33) {
34  const resource = await prisma.invoice.findUnique({
35    where: { id: req.params.id },
36    select: { userId: true },
37  });
38
39  if (!resource || resource.userId !== req.user.id) {
40    return res.status(404).json({ error: "Introuvable" });
41  }
42
43  next();
44}
45
46app.delete('/api/invoices/:id', authenticate, requireOwnership, async (req, res) => {
47  await prisma.invoice.delete({ where: { id: req.params.id } });
48  res.status(204).send();
49});

snippet.typescript

typescript
1// ❌ Endpoint vulnérable à l'IDOR
2app.get('/api/invoices/:id', authenticate, async (req, res) => {
3  const invoice = await prisma.invoice.findUnique({
4    where: { id: req.params.id },
5  });
6  res.json(invoice);
7  // Si je suis l'utilisateur 42 et que je demande /api/invoices/99,
8  // je reçois la facture de l'utilisateur 99 !
9});
10
11// ✅ Correction : toujours filtrer par l'utilisateur authentifié
12app.get('/api/invoices/:id', authenticate, async (req, res) => {
13  const invoice = await prisma.invoice.findFirst({
14    where: {
15      id: req.params.id,
16      userId: req.user.id,  // ← LA LIGNE ESSENTIELLE
17    },
18  });
19
20  if (!invoice) {
21    // Ne pas révéler si la ressource existe (404 même si 403)
22    return res.status(404).json({ error: "Facture introuvable" });
23  }
24
25  res.json(invoice);
26});
27
28// ✅ Middleware de vérification réutilisable
29async function requireOwnership(
30  req: Request,
31  res: Response,
32  next: NextFunction
33) {
34  const resource = await prisma.invoice.findUnique({
35    where: { id: req.params.id },
36    select: { userId: true },
37  });
38
39  if (!resource || resource.userId !== req.user.id) {
40    return res.status(404).json({ error: "Introuvable" });
41  }
42
43  next();
44}
45
46app.delete('/api/invoices/:id', authenticate, requireOwnership, async (req, res) => {
47  await prisma.invoice.delete({ where: { id: req.params.id } });
48  res.status(204).send();
49});

Topics and skills covered

  • Sécurité
  • IDOR
  • Access Control
  • Authorization