🔑 OAuth 2.0

OAuth 2.0 est le cadre d'autorisation le plus largement utilisé pour l'accès aux API. Il est essentiel de sélectionner le type de subvention approprié.

Choisir un type de subvention

Type de subvention Cas d'utilisation Niveau de sécurité
Authorization Code + PKCE SPA et applications mobiles (recommandé) Le plus élevé
Authorization Code Applications web côté serveur Haut
Client Credentials Communication de serveur à serveur (M2M) Moyen
Implicite (obsolète) Anciennes ZPS Faible
Mot de passe du propriétaire de la ressource (obsolète) Uniquement des tiers de confiance Faible
Ne pas utiliser de subvention implicite

L'octroi implicite est désormais obsolète car le jeton est exposé dans le fragment d'URL. Utilisez le code d'autorisation + PKCE pour les SPA.

Code d'autorisation + flux PKCE

JavaScript Génération de défis PKCE
// Génération aléatoire d'un vérificateur de code
function generateCodeVerifier() {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return btoa(String.fromCharCode(...array))
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

// Générer le code_challenge avec SHA-256
async function generateCodeChallenge(verifier) {
  const encoder = new TextEncoder();
  const data = encoder.encode(verifier);
  const digest = await crypto.subtle.digest('SHA-256', data);
  return btoa(String.fromCharCode(...new Uint8Array(digest)))
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

// Demande d'autorisation
const verifier = generateCodeVerifier();
const challenge = await generateCodeChallenge(verifier);

const authUrl = `https://auth.example.com/authorize?` +
  `response_type=code&` +
  `client_id=${clientId}&` +
  `redirect_uri=${redirectUri}&` +
  `code_challenge=${challenge}&` +
  `code_challenge_method=S256&` +
  `scope=read write`;

🎫 JWT (JSON Web Token)

Le JWT est largement utilisé comme jeton d'authentification sans état, mais les erreurs de mise en œuvre entraînent directement des vulnérabilités en matière de sécurité.

Considérations sur la sécurité des JWT

✅ Faire

  • Spécifier explicitement l'algorithme (HS256 / RS256)
  • Définir un délai d'expiration court (15 minutes ou moins)
  • Valider l'émetteur (iss) et le public (aud)
  • Générer des clés secrètes d'une longueur suffisante (256 bits ou plus)
  • Mise en œuvre de la rotation des jetons de rafraîchissement

❌ Ne sait pas

  • Stocker des informations sensibles dans la charge utile JWT (Base64 n'est pas un cryptage)
  • Autoriser l'alg : "aucun"
  • Stocker les jetons dans localStorage (vulnérable aux XSS)
  • Négliger la gestion de la révocation des jetons
  • Utiliser une clé publique RS256 comme clé secrète HS256

Mise en œuvre sécurisée des JWT

JavaScript (Node.js) Émission et vérification des jetons
const jwt = require('jsonwebtoken');

// --- Émission de jetons ---
function issueTokens(user) {
  const accessToken = jwt.sign(
    { sub: user.id, role: user.role },
    process.env.JWT_SECRET,
    {
      algorithm: 'HS256',
      expiresIn: '15m',
      issuer: 'api.example.com',
      audience: 'app.example.com',
    }
  );

  const refreshToken = jwt.sign(
    { sub: user.id, type: 'refresh' },
    process.env.REFRESH_SECRET,
    { algorithm: 'HS256', expiresIn: '7d' }
  );

  return { accessToken, refreshToken };
}

// --- Token Verification ---
function verifyAccessToken(token) {
  return jwt.verify(token, process.env.JWT_SECRET, {
    algorithms: ['HS256'],   ← Toujours préciser !
    issuer: 'api.example.com',
    audience: 'app.example.com',
  });
}

Comparaison des lieux de stockage de Token

Lieu de stockage Résistance aux XSS Résistance au CSRF Recommandation
HttpOnly Cookie △ (nécessite le paramètre SameSite) Recommandé
Mémoire (variable) Perte de la navigation sur la page
localStorage ✕ (peut être volé via XSS) Non recommandé
sessionStorage ✕ (peut être volé via XSS) Utilisation limitée
Recommandé : HttpOnly + Secure + SameSite Cookie

L'approche la plus sûre consiste à stocker le jeton d'accès dans un cookie avec les attributs HttpOnly, Secure et SameSite=Strict. Cela permet d'éviter le vol du jeton par des attaques XSS.

🗝 Gestion des clés API

Les clés API constituent un mécanisme d'authentification simple, mais une mauvaise gestion peut entraîner des risques graves.

Meilleures pratiques en matière de clés API

Production et stockage

Générer avec une entropie suffisante (256 bits ou plus). Stocker le texte haché ; ne jamais conserver de texte en clair dans la base de données.

Restriction du champ d'application

Attribuer des portées minimales par clé (lecture seule, ressources spécifiques uniquement, etc.). Évitez les autorisations de type "joker".

Rotation

Procéder à une rotation régulière des clés (dans un délai de 90 jours). Fixer un délai de grâce pour les anciennes clés. Révoquer immédiatement les clés en cas de fuite.

Méthode de transmission

Envoyer via un en-tête (X-API-Key ou Authorization). Ne pas inclure dans les paramètres de requête de l'URL (ils apparaissent dans les journaux).

Gestion des variables d'environnement

Ne pas coder les clés en dur dans le code source. Gérez-les à l'aide de variables d'environnement ou d'un gestionnaire de secrets.

Surveillance et enregistrement

Surveiller l'utilisation des clés API. Détecter les schémas anormaux (demandes massives, accès à partir d'IP inconnues).

Exemple de mise en œuvre de la validation de la clé API

JavaScript (Express) Validation de la clé d'API hachée
const crypto = require('crypto');

// Hacher la clé de l'API avec SHA-256 et comparer
async function validateApiKey(req, res, next) {
  const apiKey = req.headers['x-api-key'];
  if (!apiKey) {
    return res.status(401).json({ error: 'API key required' });
  }

  // Hachage et recherche dans la base de données (timingSafeEqual non nécessaire puisque nous comparons des hachages)
  const hashedKey = crypto
    .createHash('sha256')
    .update(apiKey)
    .digest('hex');

  const keyRecord = await db.findApiKey(hashedKey);

  if (!keyRecord || keyRecord.revokedAt) {
    return res.status(403).json({ error: 'Invalid API key' });
  }

  // Vérifier l'expiration
  if (keyRecord.expiresAt && new Date() > keyRecord.expiresAt) {
    return res.status(403).json({ error: 'API key expired' });
  }

  req.apiClient = keyRecord;
  next();
}

🤖 Securing LLM & AI Agent API Access

AI agents and LLM-powered applications require additional authentication and authorization patterns beyond traditional API security.

Model-Specific Scopes

Assign different access levels per model tier (e.g., GPT-4 requires elevated scope). Prevent unauthorized use of expensive or sensitive models.

Token Budget Enforcement

Embed token budgets in access tokens or API key metadata. Enforce per-request and per-day limits to prevent denial-of-wallet attacks.

Prompt-Level Authorization

Check user permissions before executing certain prompt types (e.g., code generation, data analysis). Map prompt categories to role-based permissions.

Multi-Tenant Isolation

Ensure conversation history, fine-tuned models, and RAG data are isolated per tenant. Use tenant-scoped API keys with namespace enforcement.

Agent Identity & Attestation

Assign cryptographic identities to AI agents. Use signed JWTs for inter-agent communication. Verify agent identity before granting tool access.

Credential Rotation for AI Services

Automate API key rotation for LLM providers. Use short-lived tokens for agent sessions. Revoke credentials immediately when agents are decommissioned.

Agent Tool Access Authorization

Scenario Auth Method Required Scope Approval
Agent reads public data API Key data:read Automatic
Agent modifies user data OAuth 2.0 (delegated) data:write User consent required
Agent calls external API OAuth 2.0 + mTLS external:invoke Human-in-the-loop
Agent executes code / shell Scoped JWT + Sandbox exec:sandbox Admin approval + audit log

Scoped OAuth Token for AI Agents (Python)

Python Agent Token Issuance with Scope Enforcement
import jwt
import time
from datetime import datetime, timedelta

class AgentTokenIssuer:
    """Issues scoped, short-lived tokens for AI agents."""

    ALLOWED_SCOPES = {
        "reader": ["data:read"],
        "writer": ["data:read", "data:write"],
        "executor": ["data:read", "data:write", "exec:sandbox"],
    }

    def __init__(self, secret_key: str):
        self.secret_key = secret_key

    def issue_agent_token(
        self,
        agent_id: str,
        role: str,
        tenant_id: str,
        token_budget: int = 10000,
        ttl_minutes: int = 15,
    ) -> str:
        # Validate role and resolve scopes
        if role not in self.ALLOWED_SCOPES:
            raise ValueError(f"Invalid role: {role}")

        payload = {
            "sub": agent_id,
            "tenant": tenant_id,
            "scopes": self.ALLOWED_SCOPES[role],
            "token_budget": token_budget,
            "iat": datetime.utcnow(),
            "exp": datetime.utcnow() + timedelta(minutes=ttl_minutes),
            "type": "agent",
        }
        return jwt.encode(payload, self.secret_key, algorithm="HS256")

    def verify_tool_access(self, token: str, required_scope: str) -> dict:
        # Decode and verify agent token
        payload = jwt.decode(
            token, self.secret_key, algorithms=["HS256"]
        )
        if required_scope not in payload["scopes"]:
            raise PermissionError(
                f"Agent {payload['sub']} lacks scope: {required_scope}"
            )
        return payload
OWASP References

Related: LLM06: Excessive Agency, ASI01: Excessive Agency, ASI03: Insecure Tool/Function Calling

Comparaison des méthodes d'authentification

Méthode Apatride Cas d'utilisation Considérations relatives à la sécurité
OAuth 2.0 + PKCE API nécessitant l'autorisation de l'utilisateur état/PKCE requis, gestion de l'expiration des jetons
JWT Bearer Communication inter-microservices Spécification de l'algorithme requise, pas de données sensibles dans la charge utile
Clé API Serveur à serveur, intégrations externes Rotation et restriction du champ d'application requises
mTLS Communication M2M de haute sécurité Coût opérationnel élevé pour la gestion des certificats
Cookie de session Applications web traditionnelles Protection CSRF requise, problèmes d'évolutivité