Aller au contenu principal

Protéger une API

Protection contre les attaques qui visent une API (Rate Limiting, CORS, etc.)

Notions théoriques​

1. Introduction​

Une API (Application Programming Interface) permet à des applications de communiquer entre elles.

Cependant, une API exposée sur Internet est vulnérable à plusieurs types d'attaques.

Il est donc essentiel de mettre en place des mécanismes de protection pour garantir sa sécurité et éviter les abus.

Les attaques courantes contre les API incluent :

  • Attaques par force brute : Tentatives répétées d’authentification pour deviner des identifiants.
  • Déni de service (DoS) : Envoi massif de requêtes pour surcharger le serveur.
  • Cross-Origin Resource Sharing (CORS) mal configuré : Permet à des sites malveillants d’accéder aux ressources de l’API.
  • Injection SQL/XSS : Tentatives d’exécution de code malveillant via les entrées utilisateur.

Pour protéger une API, plusieurs techniques peuvent être mises en place, notamment :

  • Rate Limiting : Limiter le nombre de requêtes par utilisateur.
  • CORS (Cross-Origin Resource Sharing) : Contrôler les domaines autorisés à accéder à l’API.
  • Validation des entrées : Vérifier et filtrer les données envoyées à l’API.
  • Authentification et autorisation : Restreindre l’accès aux ressources en fonction des utilisateurs.

Exemple pratique​

Il est possible de sécuriser une API avec Express.js en mettant en place Rate Limiting et en configurant CORS.

1. Installation des dépendances​

npm install express express-rate-limit cors helmet

2. Mise en place du serveur avec des protections​

const express = require("express");
const rateLimit = require("express-rate-limit");
const cors = require("cors");
const helmet = require("helmet");

const app = express();

// Sécurisation des en-têtes HTTP
app.use(helmet());

// Configuration du Rate Limiting (100 requêtes par 15 minutes par IP)
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: "Trop de requêtes, veuillez réessayer plus tard."
});
app.use(limiter);

// Configuration de CORS (autoriser uniquement un domaine spécifique)
app.use(cors({ origin: "http://mon-site.com" }));

app.get("/", (req, res) => {
res.json({ message: "API sécurisée" });
});

app.listen(3000, () => {
console.log("Serveur démarré sur http://localhost:3000");
});

3. Explication du code :​

  • Helmet : Ajoute des en-têtes HTTP de sécurité.
  • Rate Limiting : Limite le nombre de requêtes par IP pour éviter les abus.
  • CORS : Restreint l’accès à l’API aux requêtes provenant de http://mon-site.com.

Test de mémorisation/compréhension​


Quel est l'objectif principal du Rate Limiting ?


Que fait Helmet en Express.js ?


Quel est le rôle de CORS ?


Quelle est la durée définie dans le Rate Limiting de l'exemple ?


Que se passe-t-il si une API ne met pas en place de Rate Limiting ?


Quel package permet d'activer CORS en Express.js ?


Quelle est la conséquence d'un CORS mal configuré ?


Quel est le rôle de l'en-tête HTTP 'Access-Control-Allow-Origin' ?


Quelle est la conséquence d'une API sans protection ?


Quel est l'effet d'un Rate Limiting trop restrictif ?



TP pour réfléchir et résoudre des problèmes​

Sécurisation d'une API Express.js avec Rate Limiting et CORS

Ce TP a pour objectif de mettre en place une API sécurisée en utilisant :

  • Rate Limiting pour limiter les requêtes
  • et CORS pour restreindre les accès depuis des domaines spécifiques.

L’API sera développée avec Node.js et Express.js. Elle inclura :

  • Une route publique accessible à tous.
  • Une route protégée par un Rate Limiting strict.
  • Une configuration de CORS pour autoriser uniquement certains domaines.

Étape 1 : Initialisation du projet​

Avant d’écrire du code, il est nécessaire de préparer l’environnement de travail.

Instructions :​

  1. Ouvrir un terminal et naviguer vers un dossier de travail.
  2. Créer un nouveau dossier pour le projet :
mkdir secure-api
cd secure-api
  1. Initialiser un projet Node.js :
npm init -y
  1. Installer les dépendances nécessaires :
npm install express express-rate-limit cors helmet dotenv
  1. Vérifier que les fichiers suivants sont bien présents dans le dossier :
    • package.json (créé automatiquement)
    • node_modules/ (dossier contenant les bibliothèques installées)

Vérifier si :

  • Un projet Node.js est correctement initialisé.
  • Les modules Express, express-rate-limit, cors, helmet et dotenv sont installés pour gérer la sécurité de l’API.

Étape 2 : Configuration de l’environnement​

Pour éviter d’exposer des informations sensibles dans le code, il est recommandé d’utiliser un fichier .env pour stocker les variables d’environnement.

Instructions :​

  1. Créer un fichier .env à la racine du projet :
touch .env
  1. Ajouter les variables d’environnement suivantes :
PORT=4000
ALLOWED_ORIGIN=http://localhost:3000
RATE_LIMIT_WINDOW=10
RATE_LIMIT_MAX=50
  1. Modifier le fichier package.json pour ajouter un script de démarrage :
"scripts": {
"start": "node server.js"
}

Vérifier si :

  • Un fichier .env est créé pour stocker les variables d’environnement.
  • Le fichier package.json est mis à jour avec un script de démarrage.

Étape 3 : Création du serveur Express.js​

L’API doit être capable de répondre aux requêtes HTTP et d’appliquer des règles de sécurité.

Instructions :​

  1. Créer un fichier server.js et ajouter le code suivant :
require("dotenv").config();
const express = require("express");
const rateLimit = require("express-rate-limit");
const cors = require("cors");
const helmet = require("helmet");

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

// Configuration du Rate Limiting
const limiter = rateLimit({
windowMs: process.env.RATE_LIMIT_WINDOW * 60 * 1000,
max: process.env.RATE_LIMIT_MAX,
message: "Trop de requêtes, veuillez réessayer plus tard."
});
app.use(limiter);

// Configuration de CORS
app.use(cors({ origin: process.env.ALLOWED_ORIGIN }));

// Route publique
app.get("/", (req, res) => {
res.json({ message: "Bienvenue sur l'API sécurisée" });
});

// Route protégée
app.get("/protected", (req, res) => {
res.json({ message: "Vous avez accédé à une route protégée" });
});

// Démarrage du serveur
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Serveur démarré sur http://localhost:${PORT}`);
});
  1. Lancer le serveur :
npm start
  1. Vérifier que l’API fonctionne en envoyant une requête GET à http://localhost:4000/.

Vérifier si :

  • Un serveur Express.js est mis en place avec Rate Limiting et CORS.
  • Une route publique / et une route protégée /protected sont créées.
  • Le serveur démarre et écoute sur le port défini dans .env.

Étape 4 : Test du Rate Limiting​

Le Rate Limiting empêche un utilisateur d’envoyer trop de requêtes en un temps limité.

Instructions :​

  1. Ouvrir un terminal et exécuter la commande suivante plusieurs fois :
curl -X GET http://localhost:4000/protected
  1. Observer la réponse de l’API. Après 50 requêtes en 10 minutes, l’API doit renvoyer un message d’erreur :
{
"message": "Trop de requêtes, veuillez réessayer plus tard."
}

Vérifier si :

  • Le Rate Limiting fonctionne correctement et bloque les requêtes excessives.

Étape 5 : Test de CORS​

Le CORS contrôle quels domaines peuvent accéder à l’API.

Instructions :​

  1. Ouvrir la console du navigateur (F12) et exécuter le code suivant dans l’onglet Console :
fetch("http://localhost:4000/protected")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("Erreur :", error));
  1. Observer la réponse :
    • Si la requête est effectuée depuis http://localhost:3000, elle doit réussir.
    • Si la requête est effectuée depuis un autre domaine, une erreur CORS doit apparaître.

Vérifier si :

  • Le CORS est bien configuré et empêche les requêtes provenant de domaines non autorisés.

Étape 6 : Amélioration de la sécurité​

Pour renforcer la sécurité de l’API, il est possible d’ajouter :

  • Un middleware pour journaliser les requêtes suspectes.
  • Un blocage des adresses IP suspectes.

Instructions :​

  1. Installer un module de journalisation :
npm install morgan
  1. Modifier server.js pour ajouter un logger :
const morgan = require("morgan");
app.use(morgan("combined"));
  1. Relancer le serveur :
npm start
  1. Vérifier que toutes les requêtes sont enregistrées dans la console.

Vérifier si :

  • Le journal des requêtes permet de surveiller les accès à l’API.
  • Il est possible d’identifier les adresses IP suspectes.

Conclusion​

Ce TP permet de comprendre comment protéger une API contre les attaques courantes en utilisant :

  • Rate Limiting pour limiter les requêtes abusives.
  • CORS pour restreindre l’accès aux domaines autorisés.
  • Helmet pour renforcer la sécurité des en-têtes HTTP.
  • Morgan pour journaliser les requêtes.

Des améliorations possibles :

  • Ajouter une authentification par token JWT.
  • Utiliser une base de données pour stocker les utilisateurs bloqués.
  • Mettre en place un système de bannissement automatique pour les IP suspectes.

L’API est maintenant sécurisée et prête à être utilisée dans un projet plus avancé.