Aller au contenu principal

Sécurité des API : guide complet REST et GraphQL 2025

3 décembre 2025
17 min de lecture

Sécurité API complète : authentification OAuth/JWT, autorisation, validation, rate limiting, protection GraphQL. Guide pratique avec exemples pour sécuriser vos API REST et GraphQL.

Sécurité des API : le maillon faible du web moderne

> Les API sont devenues le cœur de l'architecture moderne des applications. Pourtant, elles représentent souvent un point d'entrée privilégié pour les attaquants. Sécuriser les API est devenu un enjeu critique.

Les API (Application Programming Interfaces) sont partout : applications mobiles, microservices, intégrations tierces, IoT. Cette omniprésence en fait une cible de choix pour les attaquants, d'autant plus que leur sécurité est souvent négligée.

Pourquoi les API sont vulnérables

Caractéristiques des API

1. Exposition directe

  • Accès direct depuis Internet
  • Pas d'interface utilisateur à sécuriser
  • Endpoints documentés (ou découvrables)
  • Faciles à tester et exploiter

2. Complexité de l'authentification

  • Multiples méthodes (API keys, OAuth, JWT)
  • Gestion des tokens
  • Refresh tokens
  • Scopes et permissions

3. Volume de trafic

  • Automatisable facilement
  • Attaques par force brute
  • DDoS facilité
  • Abus de ressources

4. Évolution rapide

  • Développement agile
  • Mises à jour fréquentes
  • Nouvelles fonctionnalités
  • Risque de régression

Statistiques alarmantes

Selon les études récentes :

  • 91% des organisations ont des problèmes de sécurité API
  • 54% des organisations ne testent pas leurs API
  • Temps moyen de découverte d'une API non documentée : 3 jours
  • 40% des incidents de sécurité impliquent des API

Vulnérabilités courantes des API

1. Authentification défaillante

Problèmes courants :

API Keys dans les URLs

# ❌ Mauvais : API key dans l'URL
GET /api/users?apikey=abc123xyz

# ✅ Bon : API key dans les headers
GET /api/users
Authorization: Bearer abc123xyz

Tokens non expirés

# ❌ Mauvais : Token sans expiration
token = jwt.encode({'user_id': user_id}, secret)

# ✅ Bon : Token avec expiration
token = jwt.encode({
    'user_id': user_id,
    'exp': datetime.utcnow() + timedelta(hours=1)
}, secret)

Absence de rate limiting sur l'authentification

  • Attaques par force brute possibles
  • Pas de protection contre les tentatives multiples
  • Compromission facilitée

2. Autorisation insuffisante

Problèmes courants :

IDOR (Insecure Direct Object Reference)

# ❌ Mauvais : Accès direct sans vérification
GET /api/users/123/profile
# L'utilisateur peut accéder à n'importe quel profil

# ✅ Bon : Vérification de l'autorisation
GET /api/users/123/profile
# Vérifier que l'utilisateur authentifié est le propriétaire

Bypass des contrôles d'accès

# ❌ Mauvais : Vérification côté client uniquement
if user.role == 'admin':
    # Logique admin

# ✅ Bon : Vérification côté serveur
@require_permission('admin')
def admin_function():
    # Logique admin

Escalade de privilèges

  • Modification des rôles dans les tokens
  • Bypass des scopes OAuth
  • Accès aux fonctionnalités admin

3. Validation insuffisante

Problèmes courants :

Injection SQL

# ❌ Mauvais : Concaténation de chaînes
query = f"SELECT * FROM users WHERE id = {user_id}"

# ✅ Bon : Requêtes paramétrées
query = "SELECT * FROM users WHERE id = ?"
cursor.execute(query, (user_id,))

Injection NoSQL

// ❌ Mauvais : Requête non validée
db.users.find({username: req.body.username})

// Injection
// username: {"$ne": null}
// Résultat : Retourne tous les utilisateurs

// ✅ Bon : Validation stricte
const username = validator.escape(req.body.username)
db.users.find({username: username})

Validation de schéma manquante

# ❌ Mauvais : Pas de validation
def create_user(data):
    user = User(name=data['name'], email=data['email'])
    user.save()

# ✅ Bon : Validation avec schéma
from marshmallow import Schema, fields, validate

class UserSchema(Schema):
    name = fields.Str(required=True, validate=validate.Length(min=1, max=100))
    email = fields.Email(required=True)

def create_user(data):
    schema = UserSchema()
    validated_data = schema.load(data)
    user = User(**validated_data)
    user.save()

4. Exposition excessive de données

Problèmes courants :

Réponses trop verbeuses

// ❌ Mauvais : Trop d'informations
{
  "id": 123,
  "username": "admin",
  "email": "admin@example.com",
  "password_hash": "$2b$12$...",
  "api_key": "abc123xyz",
  "internal_id": 456,
  "created_at": "2025-01-01T00:00:00Z",
  "updated_at": "2025-12-01T00:00:00Z"
}

// ✅ Bon : Données minimales nécessaires
{
  "id": 123,
  "username": "admin",
  "email": "admin@example.com"
}

Messages d'erreur détaillés

# ❌ Mauvais : Détails techniques exposés
try:
    user = User.get(id=user_id)
except Exception as e:
    return {"error": str(e)}  # Expose la stack trace

# ✅ Bon : Message générique
except Exception:
    return {"error": "User not found"}

Versioning manquant

  • Changements breaking
  • Rétrocompatibilité
  • Gestion des versions

5. Rate Limiting absent

Problèmes :

  • Attaques par force brute
  • DDoS facilité
  • Abus de ressources
  • Coûts élevés

Protection :

from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(
    app,
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)

@app.route('/api/login')
@limiter.limit("5 per minute")
def login():
    # Logique d'authentification
    pass

Méthodes d'authentification des API

API Keys

Avantages :

  • Simple à implémenter
  • Facile à utiliser
  • Bon pour les intégrations machine-to-machine

Inconvénients :

  • Pas de révocation facile
  • Risque d'exposition
  • Pas de granularité fine

Bonnes pratiques :

  • Stocker dans les headers, pas dans les URLs
  • Rotation régulière
  • Révocation possible
  • Limitation par clé

OAuth 2.0

Flux d'autorisation :

Authorization Code Flow :

1. Client redirige vers Authorization Server
2. Utilisateur s'authentifie
3. Authorization Server redirige avec code
4. Client échange code contre access token
5. Client utilise access token pour accéder à l'API

Client Credentials Flow (machine-to-machine) :

1. Client s'authentifie avec client_id et client_secret
2. Authorization Server retourne access token
3. Client utilise access token pour accéder à l'API

Bonnes pratiques :

  • Utiliser HTTPS partout
  • Valider les redirect_uri
  • Utiliser PKCE pour les clients publics
  • Scopes granulaires
  • Refresh tokens sécurisés

JWT (JSON Web Tokens)

Structure :

header.payload.signature

Avantages :

  • Stateless
  • Scalable
  • Portable

Inconvénients :

  • Difficile à révoquer
  • Risque si compromis
  • Taille des tokens

Bonnes pratiques :

import jwt
from datetime import datetime, timedelta

# Génération
token = jwt.encode({
    'user_id': user_id,
    'exp': datetime.utcnow() + timedelta(hours=1),
    'iat': datetime.utcnow()
}, secret, algorithm='HS256')

# Vérification
try:
    payload = jwt.decode(token, secret, algorithms=['HS256'])
except jwt.ExpiredSignatureError:
    # Token expiré
    pass
except jwt.InvalidTokenError:
    # Token invalide
    pass

Sécurisation :

  • Expiration courte
  • Refresh tokens sécurisés
  • Signature forte (HS256, RS256)
  • Validation stricte
  • Blacklist pour révocation

Sécurisation des API REST

Headers de sécurité

# Headers recommandés
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'

CORS (Cross-Origin Resource Sharing)

Configuration sécurisée :

from flask_cors import CORS

# ❌ Mauvais : CORS ouvert
CORS(app, resources={r"/api/*": {"origins": "*"}})

# ✅ Bon : CORS restreint
CORS(app, resources={
    r"/api/*": {
        "origins": ["https://example.com"],
        "methods": ["GET", "POST"],
        "allow_headers": ["Content-Type", "Authorization"]
    }
})

Validation des entrées

Schémas de validation :

from marshmallow import Schema, fields, validate, ValidationError

class UserSchema(Schema):
    name = fields.Str(required=True, validate=validate.Length(min=1, max=100))
    email = fields.Email(required=True)
    age = fields.Int(validate=validate.Range(min=0, max=120))

def create_user(data):
    schema = UserSchema()
    try:
        validated_data = schema.load(data)
    except ValidationError as err:
        return {"errors": err.messages}, 400

Pagination et limites

Protection contre l'énumération :

# Limiter les résultats
@app.route('/api/users')
def get_users():
    page = request.args.get('page', 1, type=int)
    per_page = min(request.args.get('per_page', 10, type=int), 100)
    
    users = User.query.paginate(page=page, per_page=per_page)
    return {
        'data': [user.to_dict() for user in users.items],
        'pagination': {
            'page': page,
            'per_page': per_page,
            'total': users.total,
            'pages': users.pages
        }
    }

Sécurisation des API GraphQL

Vulnérabilités spécifiques

1. Query Depth Attacks

# Requête récursive profonde
query {
  user {
    posts {
      author {
        posts {
          author {
            posts {
              # ... récursion infinie
            }
          }
        }
      }
    }
  }
}

Protection :

from graphql import validate, specified_rules
from graphql.validation.rules import QueryDepthLimiter

# Limiter la profondeur
rules = specified_rules + [QueryDepthLimiter(max_depth=10)]
errors = validate(schema, document, rules)

2. Query Complexity Attacks

# Requête complexe
query {
  users {
    posts {
      comments {
        author {
          posts {
            # ... complexité exponentielle
          }
        }
      }
    }
  }
}

Protection :

from graphql.validation.rules import QueryComplexityRule

# Limiter la complexité
rules = specified_rules + [QueryComplexityRule(max_complexity=1000)]

3. Introspection en production

# ❌ Mauvais : Introspection activée en production
query {
  __schema {
    types {
      name
      fields {
        name
        type {
          name
        }
      }
    }
  }
}

Protection :

# Désactiver l'introspection en production
if environment == 'production':
    schema = GraphQLSchema(
        query=QueryType,
        # Pas d'introspection
    )

Bonnes pratiques générales

Checklist de sécurité API

Authentification :

  • [ ] Méthode d'authentification appropriée (OAuth 2.0, JWT)
  • [ ] Tokens avec expiration
  • [ ] Refresh tokens sécurisés
  • [ ] Rate limiting sur l'authentification
  • [ ] Révocation possible

Autorisation :

  • [ ] Vérification des permissions à chaque requête
  • [ ] Principe du moindre privilège
  • [ ] Scopes granulaires (OAuth)
  • [ ] Pas d'IDOR
  • [ ] Journalisation des accès

Validation :

  • [ ] Validation de tous les inputs
  • [ ] Schémas de validation stricts
  • [ ] Sanitization des données
  • [ ] Protection contre les injections
  • [ ] Limites de taille

Protection :

  • [ ] HTTPS partout
  • [ ] Headers de sécurité
  • [ ] CORS configuré correctement
  • [ ] Rate limiting
  • [ ] Monitoring et alertes

Documentation :

  • [ ] Documentation à jour
  • [ ] Exemples sécurisés
  • [ ] Bonnes pratiques documentées
  • [ ] Versioning clair

Outils et tests

Tests de sécurité

Outils recommandés :

  • OWASP ZAP : Tests automatisés
  • Burp Suite : Tests manuels
  • Postman : Tests fonctionnels
  • REST Assured : Tests automatisés
  • GraphQL Security Scanner : Tests GraphQL

Monitoring

Métriques à suivre :

  • Taux d'erreur
  • Temps de réponse
  • Utilisation des endpoints
  • Tentatives d'authentification échouées
  • Patterns suspects

Conclusion

La sécurisation des API est un défi majeur du web moderne. Les API sont exposées, automatisables et souvent négligées, ce qui en fait des cibles privilégiées.

Points clés à retenir :

  • ✅ L'authentification et l'autorisation sont critiques
  • ✅ La validation des entrées est essentielle
  • ✅ Le rate limiting protège contre les abus
  • ✅ Le monitoring permet la détection
  • ✅ Les tests de sécurité sont indispensables

Action immédiate :

Auditez vos API. Vérifiez l'authentification, l'autorisation, la validation, le rate limiting et le monitoring. Identifiez les vulnérabilités et implémentez les corrections.


Pour tester la sécurité de vos API avec la méthodologie OWASP, découvrez notre **guide complet du pentest web** qui explique comment identifier les vulnérabilités d'authentification, d'autorisation et d'injection.

Besoin d'évaluer la sécurité de vos API ? Découvrez notre service de **pentest web** pour identifier et corriger les vulnérabilités de vos API avant qu'elles ne soient exploitées.

Votre site est-il sécurisé ?

Faites analyser la sécurité de votre site web par nos experts en cybersécurité.

Articles similaires

CVE-2025-55182 (React2Shell) : Vulnérabilité critique dans React Server Components

Vulnérabilité critique CVE-2025-55182 (React2Shell) permettant l'exécution de code arbitraire à distance dans React Server Components. Mise à jour urgente requise pour Next.js, Expo, React Router et autres frameworks.

Alternance et cybersécurité : réussir son entrée dans le métier

Guide complet pour réussir son alternance en cybersécurité : recherche, candidature, intégration, développement de compétences et conversion en CDI.

Multiples vulnérabilités critiques dans Cisco ASA et FTD - Exploitations actives

Alertes CERT-FR : Vulnérabilités critiques CVE-2025-20333 et CVE-2025-20362 dans Cisco ASA et FTD activement exploitées. Contournement d'authentification et exécution de code arbitraire à distance. Mise à jour urgente requise.