Sous-solA API dans NuxtPièce 6/7Avancé

Sécurité et limites

Protéger une API : ne jamais faire confiance aux entrées, contrôler l'accès, régler CORS, mettre en cache et limiter les abus.

Ce que tu vas apprendre

  • Pourquoi une API publique est attaquée par n'importe qui.
  • Contrôler l'accès avec un middleware serveur et le bon code d'erreur.
  • Régler CORS, mettre en cache les lectures et limiter les abus.

Une API est ouverte à tous

Ta route /api/recettes est une adresse publique. Les outils du navigateur, curl ou un script peuvent l'appeler sans passer par ton site. Ton interface n'est donc pas une protection : un champ désactivé dans un formulaire n'empêche pas d'envoyer la requête à la main.

Règle d'or : le serveur vérifie tout, toujours. Le chapitre 5 en pose la base (valider le corps et l'identifiant, ne reprendre que les champs attendus). Ajoute :

  • limiter les tailles : max(100) sur un texte, plafond sur un nombre ;
  • ne pas renvoyer plus que nécessaire : pas de champ interne, pas de trace d'erreur ;
  • donner des messages d'erreur neutres, pas le détail de ta base de données.
Ne construis jamais une requête de base de données en collant le texte reçu : utilise les requêtes paramétrées de ta bibliothèque.

Contrôler l'accès

Certaines routes (créer, modifier, supprimer) ne doivent pas être ouvertes à n'importe qui. Un middleware serveur, un fichier de server/middleware/, s'exécute avant chaque requête. D'après la documentation, il ne doit rien renvoyer : il inspecte la requête ou lève une erreur.

server/middleware/auth.ts
export default defineEventHandler((event) => {
  const modifie = ['POST', 'PATCH', 'DELETE'].includes(event.method)
  if (!event.path.startsWith('/api/recettes') || !modifie) return

  const config = useRuntimeConfig(event)
  const jeton = getHeader(event, 'authorization')

  if (jeton !== `Bearer ${config.cleAdmin}`) {
    throw createError({ status: 401, statusText: 'Non autorise' })
  }
})

Pour déclarer cleAdmin, procède comme au chapitre 4, dans runtimeConfig et .env.

Retiens la distinction : authentification (qui es-tu ? 401 si absente) et autorisation (as-tu le droit ? 403 si refusée). Pour de vrais comptes, utilise une solution éprouvée plutôt que de la réécrire.

Les routeRules ne sont pas une mesure de sécurité, rappelle la documentation de Nuxt : ne t'en sers pas pour protéger une route.

CORS : qui peut t'appeler depuis un navigateur

Un navigateur applique la politique de même origine : un script chargé depuis autre-site.fr ne peut pas lire une réponse de monsite.fr sans accord. Le serveur donne cet accord avec l'en-tête Access-Control-Allow-Origin. C'est le mécanisme CORS.

Par exemple, MDN montre qu'une origine précise s'écrit ainsi :

Access-Control-Allow-Origin: https://monsite.fr

Le joker * autorise toutes les origines, mais seulement pour les requêtes sans identifiants. Dans Nuxt, la règle de route cors: true ajoute des en-têtes CORS :

nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/api/**': { cors: true }
  }
})

Deux idées reçues à corriger :

  • CORS ne protège pas ton serveur : il commande seulement ce que le navigateur laisse lire. curl ignore CORS.
  • Tu n'as pas besoin de CORS pour que ton propre site appelle ta propre API : même origine.

N'ouvre CORS que si un autre site doit vraiment appeler ton API, et à l'origine voulue.

Le cache : répondre plus vite

Si la liste des recettes change rarement, inutile de la recalculer à chaque visite. La règle swr de routeRules met la réponse en cache pour une durée en secondes :

nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/api/recettes': { swr: 60 }
  }
})

Selon la documentation, swr ajoute des en-têtes de cache et met la réponse en cache sur le serveur ou un serveur relais pendant la durée donnée. MDN explique l'en-tête Cache-Control, avec max-age pour la durée de fraîcheur.

Ne mets jamais en cache une réponse propre à un utilisateur (son profil, ses favoris) : un autre visiteur pourrait la recevoir. Garde le cache pour les données identiques pour tous. Et sache que, avec un POST ou un DELETE, la liste en cache peut rester ancienne jusqu'à la fin de sa durée.

Limiter les abus

Un script peut envoyer mille requêtes à la seconde. Le code 429 Too Many Requests signale « trop de requêtes en peu de temps ». Cette limitation de débit n'est pas réglée pour toi : tu la mets en place devant ton application (hébergeur, pare-feu) ou avec un module. Garde au moins ces réflexes :

  • plafonne la taille des listes (pagination) au lieu de tout renvoyer ;
  • protège les routes qui coûtent cher, comme l'appel d'une API payante du chapitre 4.

À retenir

  • Une API est publique : le serveur valide tout et ne se fie jamais à l'interface.
  • Un middleware serveur peut exiger une authentification : 401 si absente, 403 si refusée.
  • CORS décide ce qu'un navigateur laisse lire d'un autre site ; il ne protège pas le serveur.
  • routeRules avec swr met les lectures communes en cache ; jamais de données personnelles.
  • Contre les abus, plafonne les tailles et prévois une limitation de débit (429) en amont.

Pour aller plus loin

Sources officielles

Ensuite

Projet
Lignes de code
Niveau
Sous-sol
Appartement
API dans Nuxt
Pièce
6/7
Plan n°
api/06.securite-et-limites
Lignes de code · fiches rédigées à partir des documentations officielles, sources citées en bas de chaque page.