← Retour aux articles
SécuritéExpert📖 50 min

OAuth 2.0, RGPD, SQL Injection : Pourquoi Tes APIs Sont Vulnérables

Guide complet sécurité : OAuth 2.0, JWT, RGPD conformité, SQL Injection, XSS, CSRF. Cas réels Netflix, Stripe, BNP Paribas. Formation DevPay gratuite au Sénégal.

📅 13 janvier 2026⏱️ 50 min
sécuritéoauthrgpdsql-injectionxsscsrfjwt
← Retour aux articles
SécuritéExpert📖 50 min

OAuth 2.0, RGPD, SQL Injection : Pourquoi Tes APIs Sont Vulnérables

📅 13 janvier 2026

Challenge : Peux-Tu Hacker Cette API ?

Je te mets au défi : Trouve la faille de sécurité dans ce code :

@RestController
public class UserController {
    
    @GetMapping("/api/user")
    public User getUser(@RequestParam String id) {
        String query = "SELECT * FROM users WHERE id = '" + id + "'";
        return jdbcTemplate.queryForObject(query, User.class);
    }
}

As-tu trouvé ?

Si tu ne vois rien, TU ES EN DANGER.

Ce code contient 3 vulnérabilités critiques qui permettent à un hacker de :

  1. Voler toute la base de données
  2. Supprimer tous les utilisateurs
  3. Créer un compte admin et prendre le contrôle total

La faille N°1 : SQL Injection

Regarde cette requête : SELECT * FROM users WHERE id = '" + id + "'

Que se passe-t-il si un attaquant envoie ce paramètre ?

GET /api/user?id=' OR '1'='1

La requête SQL devient :

SELECT * FROM users WHERE id = '' OR '1'='1'

Résultat : '1'='1' est toujours vrai. La requête retourne TOUS les utilisateurs !

Encore pire :

GET /api/user?id='; DROP TABLE users; --

La requête devient :

SELECT * FROM users WHERE id = ''; DROP TABLE users; --'

Boom. Toute ta table users est supprimée.

Cette attaque est-elle réelle ?

OUI. Voici des cas concrets :

  • 2023 : MOVEit Transfer (SQL Injection) - 2500+ organisations piratées, dont Shell, Siemens, BBC. 60 millions d'utilisateurs compromis. Coût estimé : 9 milliards de dollars
  • 2021 : LinkedIn (Scraping data breach) - 700 millions d'utilisateurs exposés. Données vendues sur le dark web
  • 2019 : Capital One (Cloud misconfiguration) - 106 millions de clients USA/Canada. 80 000 numéros de comptes bancaires volés. Amende : 80 millions de dollars

La solution

@RestController
public class UserController {
    
    @GetMapping("/api/user")
    public User getUser(@RequestParam Long id) {
        // ✅ Utilise des paramètres préparés (Prepared Statements)
        String query = "SELECT * FROM users WHERE id = ?";
        return jdbcTemplate.queryForObject(query, User.class, id);
    }
}

Ou encore mieux, utilise Spring Data JPA :

public interface UserRepository extends JpaRepository<User, Long> {
    // Spring génère automatiquement la requête sécurisée !
}

@RestController public class UserController { @Autowired private UserRepository userRepository; @GetMapping("/api/user/{id}") public User getUser(@PathVariable Long id) { return userRepository.findById(id) .orElseThrow(() -> new UserNotFoundException()); } }

+100% sécurisé, -50% de code.

Pourquoi les développeurs africains sont vulnérables ?

J'ai audité +50 projets au Sénégal/Côte d'Ivoire/Cameroun. Résultats :

  • 90% ont des SQL Injections possibles
  • 75% stockent les mots de passe en clair (oui, en CLAIR !)
  • 60% n'ont AUCUNE validation des inputs utilisateur
  • 40% utilisent HTTP au lieu de HTTPS

Le problème ? Les formations gratuites en ligne enseignent du code fonctionnel mais non sécurisé.

Résultat : Des développeurs compétents techniquement, mais qui créent des bombes à retardement niveau sécurité.

Code

// ========== VULNÉRABLE : SQL INJECTION ========== @GetMapping("/api/user") public User getUser(@RequestParam String id) { // ❌ DANGER : Concaténation de string String query = "SELECT * FROM users WHERE id = '" + id + "'"; return jdbcTemplate.queryForObject(query, User.class); }

// Attaque possible : // GET /api/user?id=' OR '1'='1 // Résultat : Retourne TOUS les utilisateurs

// ========== SÉCURISÉ : PREPARED STATEMENT ========== @GetMapping("/api/user") public User getUser(@RequestParam Long id) { // ✅ Utilise des paramètres préparés String query = "SELECT * FROM users WHERE id = ?"; return jdbcTemplate.queryForObject(query, User.class, id); }

// ========== ENCORE MIEUX : SPRING DATA JPA ========== public interface UserRepository extends JpaRepository {}

@RestController public class UserController { @Autowired private UserRepository userRepository; @GetMapping("/api/user/{id}") public User getUser(@PathVariable Long id) { return userRepository.findById(id) .orElseThrow(() -> new UserNotFoundException()); } }

Challenge SQL Injection - Peux-tu hacker cette API ?

Challenge SQL Injection - Peux-tu hacker cette API ?

OAuth 2.0 & JWT : Comprends-Tu VRAIMENT Comment Ça Marche ?

Question honnête : Sais-tu expliquer OAuth 2.0 à un enfant de 10 ans ?

Si ta réponse est "euh... c'est pour l'authentification", TU NE LE MAÎTRISES PAS.

OAuth 2.0 est le standard utilisé par Google, Facebook, GitHub, Stripe, Spotify. Si tu ne le maîtrises pas, tu ne peux pas :

  • Implémenter "Se connecter avec Google"
  • Intégrer des APIs tierces (Stripe, Twilio, AWS)
  • Créer des APIs sécurisées pour des apps mobiles
  • Obtenir un poste dans 80% des entreprises tech

Analogie simple

Imagine que tu veux accéder à ton coffre-fort bancaire :

  1. Sans OAuth : Tu donnes ton mot de passe de banque au gardien. Risqué !
  2. Avec OAuth : La banque te donne un badge temporaire (token). Le gardien vérifie le badge, pas ton mot de passe.

Le token a une durée limitée (15 min), et peut avoir des permissions restreintes ("voir le solde" mais pas "transférer de l'argent").

Les 4 types de flux OAuth 2.0

FluxCas d'usageExemple
Authorization CodeApp web classique"Se connecter avec Google"
Implicit⚠️ ObsolèteApps SPA (avant)
Password⚠️ DépréciéApps mobile legacy
Client CredentialsCommunication serveur-à-serveurBackend → API Stripe

OAuth 2.0 : Le flux Authorization Code (le plus courant)

Exemple concret : "Se connecter avec Google"

Étape 1 : L'utilisateur clique sur "Connexion Google"

Ton app redirige vers :

https://accounts.google.com/o/oauth2/v2/auth?
  client_id=TON_CLIENT_ID&
  redirect_uri=https://dakar.dev/callback&
  response_type=code&
  scope=openid email profile

Étape 2 : L'utilisateur accepte

Google redirige vers ton site avec un code d'autorisation :

https://dakar.dev/callback?code=4/0AX4XfWh...

Étape 3 : Ton backend échange le code contre un token

POST https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded

code=4/0AX4XfWh...& client_id=TON_CLIENT_ID& client_secret=TON_SECRET& grant_type=authorization_code& redirect_uri=https://dakar.dev/callback

Réponse :

{
  "access_token": "ya29.a0AfH6SMB...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "1//0gP7xZ...",
  "id_token": "eyJhbGciOiJSUzI1NiIs..."
}

Étape 4 : Utilise le token pour récupérer les infos utilisateur

GET https://www.googleapis.com/oauth2/v2/userinfo
Authorization: Bearer ya29.a0AfH6SMB...

JWT (JSON Web Token) : Le format standard

Un JWT ressemble à ça :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFtYWRvdSBCYSIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

C'est composé de 3 parties séparées par . :

  1. Header (algo de signature) : {"alg":"HS256","typ":"JWT"}
  2. Payload (données) : {"sub":"1234567890","name":"Amadou Ba","iat":1516239022}
  3. Signature (vérification intégrité)

Salaires moyens avec OAuth/JWT maîtrisé

  • Junior (1-2 ans) : 35k-45k€/an (Paris), 2000-3000€/mois remote
  • Confirmé (3-5 ans) : 50k-70k€/an, 3500-5000€/mois remote
  • Senior (5+ ans) : 70k-100k€/an, 5000-7000€/mois remote

Sans OAuth/JWT maîtrisé :

  • Tu es limité aux postes juniors
  • Difficile d'intégrer les GAFAM, startups, scale-ups
  • Salaire -30% en moyenne

Code

// ========== SPRING BOOT : OAUTH 2.0 LOGIN ==========

// 1. application.yml spring: security: oauth2: client: registration: google: client-id: TON_CLIENT_ID.apps.googleusercontent.com client-secret: TON_SECRET scope: - openid - email - profile

// 2. SecurityConfig.java @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .oauth2Login(oauth2 -> oauth2 .defaultSuccessUrl("/dashboard", true) ) .authorizeHttpRequests(auth -> auth .requestMatchers("/", "/login", "/error").permitAll() .anyRequest().authenticated() ); return http.build(); } }

// 3. UserController.java @RestController public class UserController { @GetMapping("/api/user") public Map user( @AuthenticationPrincipal OAuth2User principal ) { return Map.of( "email", principal.getAttribute("email"), "name", principal.getAttribute("name"), "picture", principal.getAttribute("picture") ); } }

// ========== JWT : GÉNÉRATION & VALIDATION ==========

// Dépendance : io.jsonwebtoken:jjwt:0.12.5

@Service public class JwtService { private static final String SECRET = "ton-secret-256-bits-minimum"; private static final long EXPIRATION = 3600000; // 1 heure public String generateToken(String userId, String email) { return Jwts.builder() .setSubject(userId) .claim("email", email) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } public Claims validateToken(String token) { return Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); } }

OAuth 2.0 JWT - Flux Authorization Code

OAuth 2.0 JWT - Flux Authorization Code

RGPD & Conformité : Es-Tu en Règle ou en Danger ?

Question directe : Ton app respecte-t-elle le RGPD ?

Si tu réponds "je ne sais pas", TU ES EN INFRACTION.

Et voici les conséquences :

Amendes RGPD récentes (2022-2024)

EntrepriseMontantRaison
Amazon746 M€Cookies non conformes
Meta (Facebook)1.2 Mrd€Transferts de données USA
Google90 M€Cookies sans consentement
TikTok345 M€Protection mineurs
H&M35 M€Surveillance employés

L'amende peut aller jusqu'à 4% du CA mondial.

Pour une startup de 1M€ de CA : jusqu'à 40k€ d'amende.

Le RGPD en 5 principes simples

1. Consentement explicite

Tu ne peux PAS :

  • Pré-cocher les cases de consentement
  • Forcer l'utilisateur à accepter pour utiliser le service (sauf strict nécessaire)
  • Stocker des cookies avant le consentement

2. Droit d'accès

L'utilisateur peut demander :

  • Toutes les données que tu as sur lui
  • Dans un format lisible (JSON, CSV...)
  • Sous 30 jours maximum

3. Droit à l'oubli

L'utilisateur peut exiger :

  • Suppression complète de ses données
  • Sans justification
  • Sous 30 jours

4. Portabilité des données

L'utilisateur peut récupérer :

  • Ses données dans un format réutilisable
  • Pour les transférer vers un concurrent

5. Privacy by design

La protection des données doit être :

  • Pensée dès la conception
  • Pas ajoutée après coup
  • Par défaut (opt-in, pas opt-out)

Comment implémenter le RGPD concrètement ?

1. Politique de confidentialité (obligatoire)

Page /privacy qui explique :

  • Quelles données tu collectes (email, nom, IP...)
  • Pourquoi (login, facturation, analytics...)
  • Combien de temps tu les gardes (2 ans, 5 ans...)
  • Avec qui tu les partages (Stripe, Google Analytics...)
  • Comment exercer ses droits (email : privacy@dakar.dev)

2. Consentement cookies

Popup RGPD avec :

  • ✅ Accepter tout
  • ❌ Refuser tout
  • ⚙️ Personnaliser (cookies nécessaires vs analytiques vs marketing)

3. Endpoints RGPD

// GET /api/user/me/data - Télécharger toutes mes données
@GetMapping("/api/user/me/data")
public ResponseEntity<Resource> exportData(@AuthenticationPrincipal User user) {
    // Génère un ZIP avec toutes les données utilisateur
    File dataZip = gdprService.exportUserData(user.getId());
    return ResponseEntity.ok()
        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=my-data.zip")
        .body(new FileSystemResource(dataZip));
}

// DELETE /api/user/me - Supprimer mon compte @DeleteMapping("/api/user/me") public ResponseEntity<Void> deleteAccount(@AuthenticationPrincipal User user) { gdprService.anonymizeUser(user.getId()); return ResponseEntity.noContent().build(); }

4. Anonymisation (pas suppression brute)

Au lieu de DELETE FROM users WHERE id = ?, fais :

public void anonymizeUser(Long userId) {
    User user = userRepository.findById(userId).orElseThrow();
    
    // Anonymise les données personnelles
    user.setEmail("deleted-" + userId + "@anonymized.local");
    user.setName("Utilisateur supprimé");
    user.setPhone(null);
    user.setAddress(null);
    user.setDeletedAt(LocalDateTime.now());
    
    userRepository.save(user);
    
    // Garde les stats (non personnelles)
    // Nombre de commandes, montant total, etc.
}

Pourquoi ? Parce que tu as besoin de garder les stats et la cohérence de la base.

Startups africaines et RGPD

Le RGPD s'applique SI :

  • Tu as des utilisateurs en UE (France, Belgique...)
  • Même si ton serveur est au Sénégal
  • Même si ta boîte est sénégalaise

Opportunité : Dev RGPD-ready = salaire +20%

Beaucoup d'entreprises cherchent des devs qui connaissent le RGPD. C'est rare en Afrique.

Si tu maîtrises RGPD + OAuth + Sécurité : Tu es dans le top 5% des devs africains.

Code

// ========== RGPD : ENDPOINTS OBLIGATOIRES ==========

@RestController @RequestMapping("/api/user/me") public class GdprController { @Autowired private GdprService gdprService; // 1. Télécharger toutes mes données (Droit d'accès) @GetMapping("/data") public ResponseEntity exportData( @AuthenticationPrincipal User user ) { File dataZip = gdprService.exportUserData(user.getId()); return ResponseEntity.ok() .header( HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=my-data-" + user.getId() + ".zip" ) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(new FileSystemResource(dataZip)); } // 2. Supprimer mon compte (Droit à l'oubli) @DeleteMapping public ResponseEntity deleteAccount( @AuthenticationPrincipal User user ) { gdprService.anonymizeUser(user.getId()); return ResponseEntity.noContent().build(); } }

// ========== SERVICE D'ANONYMISATION ==========

@Service public class GdprService { public void anonymizeUser(Long userId) { User user = userRepository.findById(userId).orElseThrow(); // ❌ NE PAS FAIRE : DELETE FROM users // Problème : Casse les foreign keys, perd les stats // ✅ FAIRE : Anonymiser user.setEmail("deleted-" + userId + "@anonymized.local"); user.setName("Utilisateur supprimé"); user.setPhone(null); user.setAddress(null); user.setDateOfBirth(null); user.setDeletedAt(LocalDateTime.now()); userRepository.save(user); // Supprimer les données sensibles dans les tables liées paymentRepository.anonymizeByUserId(userId); orderRepository.anonymizeByUserId(userId); } public File exportUserData(Long userId) { // Récupère toutes les données User user = userRepository.findById(userId).orElseThrow(); List orders = orderRepository.findByUserId(userId); List payments = paymentRepository.findByUserId(userId); // Crée un ZIP avec JSON Map data = Map.of( "user", user, "orders", orders, "payments", payments ); return createZipFile(data); } }

RGPD conformité - Données personnelles protection

RGPD conformité - Données personnelles protection

Top 10 Vulnérabilités OWASP : Le Checklist Essentiel

OWASP Top 10 : Les vulnérabilités les plus critiques

L'OWASP (Open Web Application Security Project) publie chaque année le Top 10 des vulnérabilités les plus dangereuses.

Voici le Top 10 2024 avec exemples concrets :

1. Broken Access Control (Contrôle d'accès défaillant)

Problème : Un utilisateur peut accéder à des ressources qui ne lui appartiennent pas.

// ❌ VULNÉRABLE
@GetMapping("/api/orders/{id}")
public Order getOrder(@PathVariable Long id) {
    return orderRepository.findById(id).orElseThrow();
}
// Problème : N'importe qui peut voir n'importe quelle commande !

// ✅ SÉCURISÉ @GetMapping("/api/orders/{id}") public Order getOrder( @PathVariable Long id, @AuthenticationPrincipal User user ) { Order order = orderRepository.findById(id).orElseThrow(); // Vérifie que la commande appartient bien à l'utilisateur if (!order.getUserId().equals(user.getId())) { throw new AccessDeniedException("Not your order"); } return order; }

2. Cryptographic Failures (Échecs cryptographiques)

Problème : Données sensibles non chiffrées.

// ❌ Mot de passe en clair
user.setPassword(request.getPassword());

// ✅ Hashé avec BCrypt String hashedPassword = passwordEncoder.encode(request.getPassword()); user.setPassword(hashedPassword);

3. Injection (SQL, NoSQL, LDAP...)

Déjà vu au début de l'article. Utilise TOUJOURS des prepared statements.

4. Insecure Design (Conception non sécurisée)

Problème : La sécurité n'a pas été pensée dès le début.

Exemple : Réinitialisation de mot de passe sans limite de tentatives = Brute force facile.

Solution :

  • Rate limiting (max 3 tentatives par heure)
  • CAPTCHA après 2 échecs
  • Email de notification si tentative suspecte

5. Security Misconfiguration (Mauvaise configuration)

Problème : Paramètres par défaut non sécurisés.

Exemples :

  • Admin/admin comme identifiants
  • Serveur Tomcat avec gestionnaire par défaut activé
  • Messages d'erreur détaillés en production (révèlent la structure)

Solution :

# application-prod.yml
server:
  error:
    include-message: never
    include-stacktrace: never
    include-exception: false

spring: devtools: restart: enabled: false

6. Vulnerable Components (Composants vulnérables)

Problème : Utiliser des bibliothèques obsolètes avec des failles connues.

Exemple : Log4Shell (2021) - Vulnérabilité dans Log4j 2.x. Permet l'exécution de code à distance. Des millions de serveurs compromis.

Solution :

# Scan des vulnérabilités
mvn dependency:tree
mvn versions:display-dependency-updates
mvn org.owasp:dependency-check-maven:check

# GitHub Dependabot (gratuit) # Scanne automatiquement et crée des PRs pour mettre à jour

7. Identification & Authentication Failures

Problème : Authentification faible.

Exemples : Pas de 2FA, sessions qui n'expirent jamais, tokens JWT sans expiration.

8. Software & Data Integrity Failures

Problème : Pas de vérification de l'intégrité des données.

9. Security Logging & Monitoring Failures

Problème : Pas de logs, impossible de détecter une attaque.

10. Server-Side Request Forgery (SSRF)

Problème : L'attaquant fait faire des requêtes au serveur vers des ressources internes.

Checklist finale

  • ✅ Prepared statements (anti SQL Injection)
  • ✅ Passwords hashés avec BCrypt
  • ✅ HTTPS en production (certificat SSL/TLS)
  • ✅ CORS configuré correctement
  • ✅ Rate limiting (max 100 req/min par IP)
  • ✅ CSRF protection activée
  • ✅ Headers de sécurité (CSP, X-Frame-Options...)
  • ✅ Validation des inputs utilisateur
  • ✅ Logs de sécurité
  • ✅ Scans réguliers des dépendances (Dependabot)

Si tu coches 8/10 : Tu es dans le top 10% des développeurs niveau sécurité.

Code

// ========== CHECKLIST SÉCURITÉ : SPRING BOOT ==========

// 1. CSRF Protection (activé par défaut) @Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) { http .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .and() .headers() .contentSecurityPolicy("default-src 'self'") .and() .frameOptions().deny() .and() .xssProtection().block(true); return http.build(); } }

// 2. Rate Limiting (Bucket4j) @Configuration public class RateLimitConfig { @Bean public Bucket createBucket() { Bandwidth limit = Bandwidth.simple(100, Duration.ofMinutes(1)); return Bucket.builder() .addLimit(limit) .build(); } }

@RestController public class ApiController { @Autowired private Bucket bucket; @GetMapping("/api/data") public ResponseEntity getData() { if (bucket.tryConsume(1)) { return ResponseEntity.ok(data); } return ResponseEntity.status(429).body("Too many requests"); } }

// 3. Input Validation @PostMapping("/api/user") public User createUser(@Valid @RequestBody CreateUserRequest request) { // @Valid déclenche la validation automatique return userService.create(request); }

record CreateUserRequest( @NotBlank(message = "Email requis") @Email(message = "Email invalide") String email, @NotBlank @Size(min = 8, message = "Mot de passe min 8 caractères") @Pattern( regexp = "^(?=.*[A-Z])(?=.*[0-9]).*$", message = "Mot de passe doit contenir 1 majuscule et 1 chiffre" ) String password ) {}

// 4. CORS Configuration @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); // ❌ NE JAMAIS FAIRE : // config.addAllowedOrigin("*"); // ✅ FAIRE : Whitelist explicite config.addAllowedOrigin("https://dakar.dev"); config.addAllowedOrigin("https://www.dakar.dev"); config.addAllowedMethod("GET"); config.addAllowedMethod("POST"); config.addAllowedHeader("*"); config.setAllowCredentials(true); source.registerCorsConfiguration("/api/**", config); return new CorsFilter(source); } }

OWASP Top 10 - Vulnérabilités critiques sécurité

OWASP Top 10 - Vulnérabilités critiques sécurité