Sécurité applicative — Patterns de prévention opérationnels
Guides pratiques pour coder des endpoints sécurisés : validation, échappement, CSRF, injection, hashing.
Ce document couvre les patterns de prévention concrets à appliquer par endpoint. Pour les vulnérabilités OWASP dans leur ensemble, voir OWASP Top 10. Pour les headers HTTP, voir Headers HTTP sécurisés.
Validation d’entrée
Toujours valider côté serveur, même si le front-end valide déjà. Privilégier les listes blanches (typage strict, regex bornées) aux listes noires (blocage de motifs malveillants).
// ✅ Java / Spring Boot — validation par annotations
@Valid
public record CreateUserRequest(
@NotBlank @Size(max = 100) String email,
@Pattern(regexp = "^[A-Z]{2}\\d{6}$") String countrycode,
@Min(0) @Max(150) int age
) {}# ✅ Python / Flask — validation par fonction ou dépendance
import re
def validate_email(email: str) -> str:
if not re.fullmatch(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", email):
raise ValueError("Email invalide")
return email.strip().lower()Pièges courants
- Ne pas faire confiance à
Content-Type: l’attaquant peut forcer n’importe quel type. trim()etstrip()seul ne protège pas contre les données valides mais malveillantes (ex.+1dans un champ booléen).- Toujours invalider, jamais ignorer silencieusement.
Échappement de sortie (XSS)
L’échappement dépend du contexte dans lequel la donnée est insérée : HTML body, attribut, JavaScript, URL.
// ✅ Java — échappement HTML systématique
import org.springframework.util.HtmlUtils;
String safe = HtmlUtils.htmlEscape(userInput);
// ↛ Ne jamais retourner userInput brut dans une vue HTML# ✅ Python — escaping Jinja2 (défaut) ou html.escape
from markupsafe import escape
return escape(user_input)// ✅ Échappement URL pour un redirect contrôlé
import java.net.URLEncoder;
String url = URLEncoder.encode(rawRedirect, StandardCharsets.UTF_8);
// ↛ Ne jamais utiliser rawRedirect directement dans un redirectContextes XSS
| Contexte | Type d’échappement | Risque si omis |
|---|---|---|
| HTML body | htmlEscape / escape | XSS stocké |
| Attribut HTML | htmlEscape + quotes | XSS dans onclick, href |
| JavaScript (innerHTML) | Sanitisation JS stricte | XSS DOM-based |
URL (attribut href) | URLEncoder | Redirection malveillante |
Règle d’or : échapper à la sortie (contexte), pas à l’entrée (données multiples).
CSRF — Cross-Site Request Forgery
Protéger les actions modifiant un état via un token de liaison à la session.
// ✅ Spring Security — CSRF activé par défaut
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
return http.build();
}# ✅ Python / Flask — CSRFProtect (WTForms)
from flask_wtf.csrf import CSRFProtect
app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
CSRFProtect(app)
# ↛ Par défaut, exige un token `_csrf_token` dans tous les formulaires POSTAlternatives / complémentaires
- SameSite=Strict sur les cookies de session : le navigateur refuse d’envoyer le cookie dans un contexte cross-site.
- Double-submit cookie : le serveur exige que le token CSRF soit identique dans le cookie et le header.
Règle : utiliser SameSite
StrictouLaxet un token CSRF pour les APIs soumises à des attaques forgées depuis d’autres origines.
Injection (SQL, LFI, commande)
Le remède universel est le paramétrage : envoyer les données comme paramètres, jamais comme fragments de code.
// ✅ SQL — PreparedStatement
String sql = "SELECT * FROM users WHERE email = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, userInput);// ✅ Commande — whitelist d'exécutables, jamais Runtime.exec(userInput)
private static final Set<String> VALID_COMMANDS = Set.of("git", "ping", "ls");
public String run(String cmd, String... args) throws IOException {
if (!VALID_COMMANDS.contains(cmd)) {
throw new SecurityException("Commande interdite : " + cmd);
}
ProcessBuilder pb = new ProcessBuilder(cmd, args);
return new String(pb.start().getInputStream().readAllBytes());
}# ✅ Python — paramétrage SQLAlchemy
from sqlalchemy import select, text
stmt = text("SELECT * FROM users WHERE email = :email")
result = session.execute(stmt, {"email": user_input})Pièges LFI
// ❌ Jamais — concaténation de chemin utilisateur
new File("/uploads/" + userInput).read();
// ✅ Whitelist de répertoires autorisés
Set<Path> ALLOWED = Set.of(Paths.get("/uploads"), Paths.get("/templates"));
if (!path.startsWith(ALLOWED.stream().toList())) {
throw new SecurityException("Chemin invalide");
}Hashing de mots de passe
Toujours utiliser un algorithme à coût ajustable (bcrypt, argon2id). Jamais MD5, SHA-1, SHA-256 ou tout hasher sans sel.
// ✅ Java / Spring Security — BCrypt (défaut)
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}# ✅ Python — argon2 via passlib
from passlib.context import Argon2Context
argon2 = Argon2Context(time_cost=2, memory_cost=65536, parallelism=4)
hash_ = argon2.hash(password)
argon2.verify(password, hash_)Pourquoi pas SHA-256 ?
SHA-256 est rapide (des milliards de hashes/seconde sur un GPU moderne) et sans sel par défaut. Un dictionnaire de 10^9 mots courants tient en ~20 Mo et peut être parcouru en quelques heures.
Checklist de revue de sécurité — Un endpoint API
Vérifier chaque nouvel endpoint de contrôleur avec cette liste :
- Entrées : tous les champs
@RequestParam/@RequestBodysont typés et validés (@Valid, regex, bornes) - Sorties : aucune donnée sensible (mot de passe, token, champ PII) n’est incluse dans la réponse
- Injection : aucune requête SQL / commande système n’est construite par concaténation
- CSRF : l’endpoint n’est pas accessible via une requête cross-site forgée (token ou SameSite)
- Auth :
@PreAuthorizeou filtre équivalent protège l’accès selon le rôle de l’utilisateur - Logs : l’endpoint logge les événements de sécurité sans jamais journaliser de secret ou de PII
- Rate limit : l’endpoint est couvert par le rate limiting global
Pages associées
- Authentification API — JWT, OAuth2, sessions, API keys
- Headers HTTP sécurisés — CSP, CORS, cookies
- OWASP Top 10 — Vulnérabilités critiques et patterns de prévention
- Gestion des secrets — Rotation, chiffrement, pipelines