Sécurité pour le web
Ce cours fait partiellement suite à celui de Node.
Validation body
La validation du body est le principe de vérifier le contenu du body, en vérifiant les clés qu'il contient ainsi que le type de chacun. On vérifiera aussi la taille.
Pour ça, on utilisera Zod. En installant le package zod.
Zod nous permettra de créer des schémas et de définir leur propriétés (types, longeur, regex, etc..)
Par exemple, voici l'implémentation du schéma pour la création d'une tâche :
const TaskSchema = z.object({
title: z.string().min(1).max(255),
description: z.string().min(1).max(1024),
status: z.string().min(1).max(64)
});
// Route qui crée une nouvelle tâche
app.post('/tasks', async (c) => {
// On récupère les données depuis le body
const body = await c.req.json();
const safeData = TaskSchema.safeParse(body);
if (!safeData.success) {
return c.json(
{
status: 'error',
message: safeData.error.message
},
400
);
}
const { title, description, status } = safeData.data;
// On insère la tache dans la BDD
const task = await db.insert(tasks).values({ title, description, status });
// On confirme que tout s'est bien passé et on renvoie la tache
return c.json({
status: 'success',
data: task
});
});
CSRF (Cross Site Request Forgery)
Le CSRF est une attaque où un attaquant peut exécuter des actions sur un site web en utilisant les identifiants d'un utilisateur authentifié. Par exemple, si un utilisateur est connecté sur un site web, et qu'il clique sur un lien de spam, l'attaquant peut exécuter des actions sur le site web en utilisant les identifiants de l'utilisateur.
Pour prévenir cette attaque, on peut utiliser des tokens CSRF.
En fait, à chaque fois qu'on fait une requête, on va générer un token CSRF et l'envoyer dans le header de la requête.
Lors de la prochaine requête, on va vérifier si le token CSRF est valide.
Souvent, le CSRF est déjà présent dans les frameworks front-end, et back-end. Voir hono pour plus d'informations, par exemple.
CORS (Cross-Origin Resource Sharing)
Le CORS est un mécanisme qui permet de partager des ressources entre différents origines. Par exemple, si un site web est hébergé sur https://example.com, et que vous avez une application web qui est hébergée sur https://app.example.com, vous pouvez partager des ressources entre ces deux sites. Par défaut, le CORS empêche cette partage.
Le CORS permet d'authoriser :
- L'origine
- Les méthodes
- Les headers
On a déjà vu comment configurer le CORS plus tôt. On va le répéter ici.
app.use(
'*',
cors({
origin: 'http://localhost:5173',
credentials: true,
allowMethods: ['POST', 'GET', 'OPTIONS', 'PUT']
})
);
Headers HTTP
Il est important de mettre certains headers HTTP pour sécuriser nos APIs. Voici une liste de headers importants :
Content-Security-Policy: Permet de contrôler les ressources que le navigateur peut charger. ->Content-Security-Policy: image-src 'self' https://example.com-> On autorise le chargement d'images uniquement depuis notre domaine et https://example.comStrict-Transport-Security: Permet de forcer le navigateur à utiliser HTTPS. ->Strict-Transport-Security: max-age=31536000; includeSubDomains; preload-> On force le navigateur à utiliser HTTPS pour toute la durée de la session.X-Frame-Options: Permet de contrôler l'iframeing. ->X-Frame-Options: DENY-> On interdit l'iframeing.X-Content-Type-Options: Permet de contrôler le type de contenu. ->X-Content-Type-Options: nosniff-> On interdit le sniffing de type de contenu.X-XSS-Protection: Permet de protéger contre les attaques XSS. ->X-XSS-Protection: 1; mode=block-> On active la protection XSS.Cross-Origin-Opener-Policy: Permet de contrôler l'ouverture de nouvelles fenêtres. ->Cross-Origin-Opener-Policy: same-site-> On autorise l'ouverture de nouvelles fenêtres uniquement depuis le même site.Cross-Origin-Resource-Policy: Permet de contrôler l'accès à des ressources depuis un autre domaine. ->Cross-Origin-Resource-Policy: same-origin-> On autorise l'accès à des ressources uniquement depuis le même domaine.
Voir cette page pour plus d'informations.
Rate limiting
Le rate limiting est un mécanisme qui permet de limiter le nombre de requêtes que peut faire un utilisateur. C'est utile pour éviter les attaques de déni de service (DoS).
Pour mettre en place le rate limiting, on peut stocker l'IP ou l'utilisateur, et on va limiter le nombre de requêtes que peut faire un utilisateur par minute, par heure, par jour, etc..
Attention, étant donné qu'on doit vérifier chaque requête, ça peut être couteux en performance. Pour stocker les requêtes, il est donc recommandé d'utiliser la RAM directement (Map) ou une BDD de type in-memory, comme Redis.