La sécurité n’est pas un module à ajouter à la fin — c’est une pratique à intégrer dès le début. En 2026, les développeurs juniors qui ne connaissent pas les vulnérabilités de base (SQL injection, XSS, secrets exposés) ont du mal à passer les entretiens techniques des équipes sérieuses. Ce guide couvre ce que tout développeur doit savoir, sans être expert en sécurité.
L’OWASP Top 10 — les vulnérabilités les plus courantes
L’OWASP (Open Web Application Security Project) publie le Top 10 des vulnérabilités web les plus répandues. En 2026, les plus pertinentes pour un développeur :
1. Injection (SQL, NoSQL, commandes)
// ❌ SQL injection — l'entrée utilisateur est directement dans la requête
const query = `SELECT * FROM users WHERE email = '${userInput}'`
// Si userInput = "'; DROP TABLE users; --" → désastre
// ✅ Requêtes paramétrées — toujours
const user = await db.query('SELECT * FROM users WHERE email = $1', [userInput])
// Avec Prisma — automatiquement protégé
const user = await prisma.user.findUnique({
where: { email: userInput },
})
// ❌ Injection de commande shell
exec(`convert ${filename}`) // filename = "img.jpg; rm -rf /"
// ✅ Valider et assainir les entrées
const safeFilename = path.basename(filename).replace(/[^a-z0-9.-]/gi, '')
execFile('convert', [safeFilename])
2. Cross-Site Scripting (XSS)
// ❌ Injecter du HTML non sanitisé dans le DOM
element.innerHTML = userInput // si input = ""
// ✅ React échappe automatiquement — utilisez JSX
{userInput} // sécurisé par défaut
// ⚠️ dangerouslySetInnerHTML — uniquement avec un sanitizer
import DOMPurify from 'dompurify'
3. Contrôle d’accès défaillant
// ❌ Vérifier l'identité mais pas les autorisations
app.get('/api/orders/:id', authenticate, async (req, res) => {
const order = await Order.findById(req.params.id) // n'importe quel user peut voir n'importe quelle commande
res.json(order)
})
// ✅ Vérifier que l'utilisateur a accès à cette ressource
app.get('/api/orders/:id', authenticate, async (req, res) => {
const order = await Order.findOne({
_id: req.params.id,
userId: req.user.id, // s'assurer que la commande appartient à cet utilisateur
})
if (!order) return res.status(404).json({ error: 'Not found' })
res.json(order)
})
La gestion des secrets — la faute la plus commune
# ❌ Secrets dans le code source — commité sur GitHub
const API_KEY = 'sk-prod-abc123...'
DATABASE_URL = 'postgresql://user:password@prod-server'
# ✅ Variables d'environnement — jamais dans le code
const API_KEY = process.env.OPENAI_API_KEY
# .env (développement local — ne jamais commiter)
# .env.example (valeurs vides — commiter pour documenter)
OPENAI_API_KEY=
DATABASE_URL=
JWT_SECRET=
# .gitignore — toujours présent
.env
.env.local
.env.production
# Si vous avez commité un secret par erreur :
# 1. Révoquer immédiatement la clé compromise
# 2. Utiliser git-filter-repo pour supprimer de l'historique
# 3. Forcer le push (coordonner avec l'équipe)
HTTPS et headers de sécurité
// Next.js — headers de sécurité dans next.config.ts
const securityHeaders = [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' 'unsafe-eval'; img-src 'self' data: https:"
},
]
// Node.js/Express — avec helmet
import helmet from 'helmet'
app.use(helmet()) // applique une dizaine de headers de sécurité automatiquement
L’authentification — les pièges classiques
// ❌ Stocker des mots de passe en clair ou avec MD5
user.password = password // catastrophique si la DB est compromise
// ✅ bcrypt ou argon2 — hachage avec salt
import bcrypt from 'bcrypt'
const hash = await bcrypt.hash(password, 12) // 12 = cost factor
const isValid = await bcrypt.compare(plainPassword, hash)
// JWT — ne pas stocker de données sensibles dans le payload
// Le payload est encodé en base64, pas chiffré — lisible par n'importe qui
const token = jwt.sign(
{ userId: user.id, role: user.role }, // ✅ ok — pas de mot de passe ou données sensibles
process.env.JWT_SECRET,
{ expiresIn: '7d' }
)
// Rate limiting — prévenir le brute force
import rateLimit from 'express-rate-limit'
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 tentatives max
message: 'Trop de tentatives, réessayez dans 15 minutes',
})
Offres développeur back-end et fullstack
Des postes pour développeurs qui codent de façon sécurisée — les équipes sérieuses testent les connaissances en sécurité dès l’entretien technique.
À lire aussi : Déploiement sur VPS — Monitoring — DevOps pour débutant