Sécuriser son code généré par IA : checklist développeur
Les assistants IA génèrent du code rapidement. Mais ce code est-il sécurisé ? Pas toujours. Les modèles sont entraînés sur du code existant, incluant des patterns vulnérables. Voici comment valider et sécuriser le code avant de le merger.
Pourquoi le code IA peut être vulnérable
Les modèles apprennent de tout
Les LLMs sont entraînés sur des milliards de lignes de code, incluant :
- Du code legacy avec des pratiques obsolètes
- Des exemples de tutoriels simplifiés (sans sécurité)
- Du code avec des vulnérabilités connues
- Des snippets Stack Overflow non validés
Patterns dangereux observés
Une étude de Stanford (2023) a montré que les développeurs utilisant Copilot produisaient 40% plus de code vulnérable que ceux qui n'utilisaient pas d'IA.
| Vulnérabilité | Fréquence | Exemple |
|---|---|---|
| Injection SQL | Très fréquent | Concaténation de requêtes |
| XSS | Fréquent | Pas d'échappement HTML |
| Path Traversal | Fréquent | Pas de validation de chemins |
| Hardcoded secrets | Très fréquent | Clés API en clair |
| Insecure deserialization | Modéré | Deserialize sans validation |
Les vulnérabilités les plus courantes
1. Injection SQL
Code généré dangereux :
// ❌ L'IA génère souvent ce pattern
public User findByEmail(String email) {
String query = "SELECT * FROM users WHERE email = '" + email + "'";
return jdbcTemplate.queryForObject(query, userRowMapper);
}Code sécurisé :
// ✅ Utiliser des prepared statements
public User findByEmail(String email) {
String query = "SELECT * FROM users WHERE email = ?";
return jdbcTemplate.queryForObject(query, userRowMapper, email);
}Détection automatique :
# Règle Semgrep
rules:
- id: sql-injection-java
patterns:
- pattern: |
$QUERY = "..." + $VAR + "...";
$JDBC.query($QUERY, ...)
message: "Potential SQL injection"
severity: ERROR2. Cross-Site Scripting (XSS)
Code généré dangereux :
// ❌ Injection HTML directe
function displayComment(comment) {
document.getElementById('comments').innerHTML +=
`<div class="comment">${comment.text}</div>`;
}Code sécurisé :
// ✅ Utiliser textContent ou une librairie d'échappement
function displayComment(comment) {
const div = document.createElement('div');
div.className = 'comment';
div.textContent = comment.text;
document.getElementById('comments').appendChild(div);
}
// Ou avec DOMPurify
function displayCommentSafe(comment) {
const sanitized = DOMPurify.sanitize(comment.text);
document.getElementById('comments').innerHTML +=
`<div class="comment">${sanitized}</div>`;
}3. Path Traversal
Code généré dangereux :
# ❌ Pas de validation du chemin
@app.route('/download/<filename>')
def download(filename):
return send_file(f'/uploads/{filename}')Code sécurisé :
# ✅ Valider et normaliser le chemin
import os
from werkzeug.utils import secure_filename
UPLOAD_DIR = '/uploads'
@app.route('/download/<filename>')
def download(filename):
# Sécuriser le nom de fichier
safe_filename = secure_filename(filename)
# Construire le chemin absolu
file_path = os.path.join(UPLOAD_DIR, safe_filename)
# Vérifier que le chemin reste dans le dossier autorisé
if not os.path.realpath(file_path).startswith(os.path.realpath(UPLOAD_DIR)):
abort(403)
if not os.path.exists(file_path):
abort(404)
return send_file(file_path)4. Secrets hardcodés
Code généré dangereux :
# ❌ L'IA copie souvent les exemples avec de fausses clés
import openai
openai.api_key = "sk-proj-xxxxxxxxxxxxxxxxxxxxx"
def generate_text(prompt):
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.contentCode sécurisé :
# ✅ Utiliser des variables d'environnement
import os
import openai
openai.api_key = os.environ.get("OPENAI_API_KEY")
if not openai.api_key:
raise ValueError("OPENAI_API_KEY environment variable not set")
def generate_text(prompt):
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content5. Authentification faible
Code généré dangereux :
// ❌ Vérification JWT côté client uniquement
function isAuthenticated() {
const token = localStorage.getItem('token');
if (token) {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.exp > Date.now() / 1000;
}
return false;
}Code sécurisé :
// ✅ Toujours vérifier côté serveur
// Client : envoyer le token
async function fetchProtectedResource() {
const token = localStorage.getItem('token');
const response = await fetch('/api/protected', {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (response.status === 401) {
// Token invalide ou expiré
logout();
return null;
}
return response.json();
}
// Serveur : valider le token
const jwt = require('jsonwebtoken');
function authMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.slice(7);
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
}Outils de détection automatique
SAST (Static Application Security Testing)
Semgrep - Open source, règles personnalisables :
# Installation
pip install semgrep
# Scan du projet
semgrep --config=auto .
# Avec règles OWASP
semgrep --config=p/owasp-top-ten .
# Rapport CI/CD
semgrep --config=auto --json -o results.json .Configuration CI :
# .github/workflows/security.yaml
name: Security Scan
on: [push, pull_request]
jobs:
semgrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/secretsCodeQL - Analyse sémantique GitHub :
# .github/workflows/codeql.yaml
name: CodeQL
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * 1'
jobs:
analyze:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript, python, java
- name: Build
run: npm run build
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3Détection de secrets
GitLeaks :
# Installation
brew install gitleaks
# Scan du repo
gitleaks detect --source . --verbose
# Pre-commit hook
gitleaks protect --stagedConfiguration pre-commit :
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
- repo: https://github.com/returntocorp/semgrep
rev: v1.50.0
hooks:
- id: semgrep
args: ['--config', 'auto', '--error']Scan des dépendances
Dependabot (GitHub natif) :
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"Snyk :
# Installation
npm install -g snyk
# Authentification
snyk auth
# Scan des vulnérabilités
snyk test
# Monitoring continu
snyk monitorChecklist de validation
Avant de merger du code généré par IA
Injection :
- ☐ Pas de concaténation SQL, utiliser des prepared statements
- ☐ Pas d'évaluation dynamique de code (
eval(),exec()) - ☐ Validation des entrées utilisateur
- ☐ Échappement des sorties HTML
Authentification / Autorisation :
- ☐ Vérification côté serveur (jamais côté client seul)
- ☐ Tokens avec expiration
- ☐ Validation des permissions par ressource
- ☐ Pas de credentials hardcodés
Gestion des fichiers :
- ☐ Validation des chemins (pas de traversal)
- ☐ Limitation des types de fichiers uploadés
- ☐ Scan antivirus sur les uploads
- ☐ Stockage hors du webroot
Cryptographie :
- ☐ Pas d'algorithmes obsolètes (MD5, SHA1 pour passwords)
- ☐ Utilisation de bcrypt/argon2 pour les mots de passe
- ☐ Clés de chiffrement en variables d'environnement
- ☐ TLS pour toutes les communications
Données sensibles :
- ☐ Pas de secrets dans le code ou les logs
- ☐ Masquage des données sensibles dans les erreurs
- ☐ RGPD : minimisation des données collectées
Prompts sécurisés pour l'IA
Demander explicitement la sécurité
Génère une fonction d'authentification en Node.js.
Contraintes de sécurité :
- Utiliser bcrypt pour les mots de passe
- Implémenter rate limiting
- Valider toutes les entrées
- Logger les tentatives échouées
- Retourner des erreurs génériques (pas de leak d'info)Demander une review sécurité
Review ce code pour les vulnérabilités de sécurité.
Focus sur :
- OWASP Top 10
- Injection (SQL, XSS, Command)
- Authentification/Autorisation
- Gestion des secrets
- Validation des entrées
Code à analyser :
[coller le code]Template de validation
Ce code a été généré par IA. Vérifie :
1. Y a-t-il des injections possibles ?
2. Les entrées utilisateur sont-elles validées ?
3. Y a-t-il des secrets hardcodés ?
4. L'authentification est-elle correcte ?
5. Les erreurs révèlent-elles des infos sensibles ?
Si des problèmes sont trouvés, propose une version corrigée.
Code :
[coller le code]Workflow sécurisé
Pipeline CI/CD recommandé
# .github/workflows/secure-pipeline.yaml
name: Secure Pipeline
on:
push:
branches: [main]
pull_request:
jobs:
# Étape 1 : Secrets detection
secrets-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Étape 2 : SAST
sast:
runs-on: ubuntu-latest
needs: secrets-scan
steps:
- uses: actions/checkout@v4
- name: Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: p/owasp-top-ten
# Étape 3 : Dependency check
deps:
runs-on: ubuntu-latest
needs: secrets-scan
steps:
- uses: actions/checkout@v4
- name: Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
# Étape 4 : Tests (incluant tests de sécurité)
test:
runs-on: ubuntu-latest
needs: [sast, deps]
steps:
- uses: actions/checkout@v4
- name: Run tests
run: npm test
- name: Run security tests
run: npm run test:security
# Étape 5 : Container scan
container-scan:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Trivy scan
uses: aquasecurity/trivy-action@master
with:
image-ref: app:${{ github.sha }}
severity: CRITICAL,HIGH
exit-code: 1IDE : activer les checks en temps réel
VS Code :
// settings.json
{
"sonarlint.rules": {
"javascript:S2068": { "level": "on" }, // Hardcoded credentials
"javascript:S5131": { "level": "on" }, // XSS
"java:S3649": { "level": "on" } // SQL injection
},
"semgrep.scan.onSave": true
}Formation continue
Ressources recommandées
| Ressource | Type | Focus |
|---|---|---|
| OWASP Top 10 | Guide | Vulnérabilités web |
| PortSwigger Web Security Academy | Labs | Pratique offensive |
| SANS Secure Coding | Cours | Best practices |
| HackTheBox | CTF | Pentest pratique |
Tests de sécurité à ajouter
// tests/security.test.js
describe('Security Tests', () => {
describe('Input Validation', () => {
it('should reject SQL injection attempts', async () => {
const maliciousInput = "'; DROP TABLE users; --";
const response = await request(app)
.get(`/users?search=${encodeURIComponent(maliciousInput)}`);
expect(response.status).toBe(400);
});
it('should sanitize XSS in user input', async () => {
const xssPayload = '<script>alert("xss")</script>';
const response = await request(app)
.post('/comments')
.send({ text: xssPayload });
expect(response.body.text).not.toContain('<script>');
});
});
describe('Authentication', () => {
it('should not expose user existence on login failure', async () => {
const response1 = await request(app)
.post('/login')
.send({ email: 'exists@test.com', password: 'wrong' });
const response2 = await request(app)
.post('/login')
.send({ email: 'notexists@test.com', password: 'wrong' });
// Same error message for both cases
expect(response1.body.error).toBe(response2.body.error);
});
});
});Conclusion
Le code généré par IA n'est pas intrinsèquement dangereux, mais il nécessite la même vigilance que n'importe quel code externe. Les outils automatisés (Semgrep, CodeQL, Snyk) détectent la majorité des vulnérabilités connues. Les pre-commit hooks empêchent les erreurs d'atteindre le repository.
Le plus important : ne jamais faire confiance aveuglément au code généré. Relisez, testez, scannez. L'IA accélère le développement, elle ne remplace pas la vigilance humaine.
Pour choisir votre assistant IA : Claude Code vs Cursor vs GitHub Copilot : comparatif 2026
Pour les failles spécifiques aux IDE IA : IDEsaster : les failles de sécurité des IA de coding