secure-codingowaspinjection
MDstable
Secure Coding — Bonnes Pratiques de Développement
Pratiques de développement sécurisé : validation des entrées, cryptographie, gestion des secrets, authentification, prévention OWASP Top 10
checklistintermediate 2026-05-30 5 min read
secure-codingowaspinjectioncryptographievalidationdevsecops
Validation des entrées
Principe : Whitelist > Blacklist
python
# ❌ Mauvais — blacklist contournabledef validate_username(name):if "<script>" in name or "SELECT" in name:raise ValueError("Invalid")return name# ✅ Bon — whitelist stricteimport redef validate_username(name):if not re.match(r'^[a-zA-Z0-9_-]{3,32}$', name):raise ValueError("Username: 3-32 chars, alphanumeric only")return name
Validation côté serveur obligatoire
javascript
// ❌ Validation côté client uniquement — contournable via proxydocument.getElementById('form').onsubmit = () => {if (!email.includes('@')) return false;};// ✅ Validation côté serveur (Express.js)const { body, validationResult } = require('express-validator');app.post('/register', [body('email').isEmail().normalizeEmail(),body('age').isInt({ min: 18, max: 120 }),body('username').matches(/^[a-zA-Z0-9_]{3,32}$/),], (req, res) => {const errors = validationResult(req);if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });// Traitement sécurisé});
Prévention des injections
SQL — Requêtes paramétrées
python
# ❌ Injection SQL possiblequery = f"SELECT * FROM users WHERE email = '{email}'"cursor.execute(query)# ✅ Paramètre bindcursor.execute("SELECT * FROM users WHERE email = %s", (email,))# ✅ ORM (SQLAlchemy)user = db.session.query(User).filter_by(email=email).first()
java
// ❌ VulnérableString query = "SELECT * FROM users WHERE name = '" + name + "'";Statement stmt = conn.createStatement();// ✅ PreparedStatementPreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE name = ?");stmt.setString(1, name);
Command Injection
python
# ❌ Command injection possibleimport subprocesssubprocess.run(f"ping {ip}", shell=True)# ✅ Pas de shell=True, arguments séparésimport ipaddressdef ping_host(ip):try:ipaddress.ip_address(ip) # Validationexcept ValueError:raise ValueError("Invalid IP")subprocess.run(["ping", "-c", "4", ip], shell=False, check=True)
SSTI — Server-Side Template Injection
python
# ❌ Vulnérable (Jinja2)from jinja2 import TemplateTemplate(user_input).render()# ✅ Séparer template et donnéestemplate = Template("Hello {{ name }}!")template.render(name=user_input) # Autoescape activé
Cryptographie
Hachage des mots de passe
python
# ❌ MD5, SHA1, SHA256 — NON pour les passwordsimport hashlibhash = hashlib.md5(password.encode()).hexdigest()# ✅ bcrypt / argon2import bcrypt# Hachagehashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))# Vérificationbcrypt.checkpw(password.encode(), hashed)# ✅ argon2 (recommandé OWASP)from argon2 import PasswordHasherph = PasswordHasher(time_cost=3, memory_cost=65536, parallelism=1)hash = ph.hash(password)ph.verify(hash, password)
Chiffrement symétrique
python
# ❌ DES, 3DES, RC4 — dépréciés# ❌ AES-ECB — révèle les patterns# ✅ AES-256-GCM (authentifié)from cryptography.hazmat.primitives.ciphers.aead import AESGCMimport oskey = os.urandom(32) # 256 bitsnonce = os.urandom(12) # 96 bitsaad = b"associated data" # Données authentifiéesaesgcm = AESGCM(key)ciphertext = aesgcm.encrypt(nonce, plaintext, aad)plaintext = aesgcm.decrypt(nonce, ciphertext, aad)
Génération de tokens sécurisés
python
# ❌ Math.random() / random() — prévisibleimport randomtoken = str(random.randint(100000, 999999))# ✅ CSPRNG (Cryptographically Secure PRNG)import secretstoken = secrets.token_urlsafe(32) # 256 bits, URL-safeotp = secrets.randbelow(1000000) # OTP 6 chiffres
Gestion des secrets
python
# ❌ Secrets hardcodés dans le codeAPI_KEY = "sk-prod-abc123xyz789"DB_PASSWORD = "supersecret"# ✅ Variables d'environnementimport osAPI_KEY = os.environ.get("API_KEY")if not API_KEY:raise RuntimeError("API_KEY environment variable not set")# ✅ Coffre-fort (HashiCorp Vault)import hvacclient = hvac.Client(url='https://vault.company.com', token=os.environ['VAULT_TOKEN'])secret = client.secrets.kv.read_secret_version(path='myapp/database')DB_PASSWORD = secret['data']['data']['password']
bash
# .gitignore — ne JAMAIS commiterenvpemkeyconfig/secrets.ymlcredentialsjson# Vérification pré-commitpip install detect-secretsdetect-secrets scan > secretsbaseline
Authentification & Sessions
python
# Sessions sécurisées (Flask)from flask import Flask, sessionapp = Flask(__name__)app.config.update(SECRET_KEY=secrets.token_hex(32),SESSION_COOKIE_SECURE=True, # HTTPS uniquementSESSION_COOKIE_HTTPONLY=True, # Pas de JS accessSESSION_COOKIE_SAMESITE='Lax', # Protection CSRFPERMANENT_SESSION_LIFETIME=1800, # 30 min timeout)
javascript
// JWT — configuration sécurisée (Node.js)const jwt = require('jsonwebtoken');// ❌ Algorithme none accepté// jwt.verify(token, '', { algorithms: ['none'] })// ✅ Algorithme fixé, secret fortconst token = jwt.sign({ userId: user.id, role: user.role },process.env.JWT_SECRET,{ algorithm: 'HS256', expiresIn: '1h' });// Vérification strictejwt.verify(token, process.env.JWT_SECRET, {algorithms: ['HS256'], // Algorithme expliciteissuer: 'myapp.com',audience: 'myapp-client',});
Headers de sécurité HTTP
python
# Flask — headers sécuritéfrom flask import Flaskfrom flask_talisman import Talismanapp = Flask(__name__)Talisman(app,content_security_policy={'default-src': "'self'",'script-src': ["'self'", 'cdn.jsdelivr.net'],'style-src': ["'self'", "'unsafe-inline'"],},strict_transport_security=True,strict_transport_security_max_age=31536000,frame_options='DENY',content_type_options=True,referrer_policy='strict-origin-when-cross-origin',)
Gestion des erreurs
python
# ❌ Exposer les détails d'erreur@app.errorhandler(500)def error(e):return str(e), 500 # Stack trace exposée# ✅ Message générique + log interneimport logginglogger = logging.getLogger(__name__)@app.errorhandler(Exception)def handle_error(e):logger.error(f"Internal error: {e}", exc_info=True)return {"error": "An internal error occurred"}, 500# ✅ Messages d'auth non discriminants# ❌ "User not found" vs "Wrong password" → user enumeration# ✅ "Invalid credentials" (même message)
Checklist OWASP Secure Coding
A01 Broken Access ControlVrifier les droits ct serveur chaque requteRefuser par dfaut deny-by-default)Logs des accs refussA02 Cryptographic FailuresChiffrer les donnes sensibles au reposTLS partout pas de HTTP en clairAlgorithmes modernes pas MD5 DES RC4A03 InjectionRequtes paramtres pour SQLPas de shellTrue dans subprocesschappement HTML des outputsA05 Security MisconfigurationDsactiver les pages derreur dtailles en prodPas de comptes par dfaut admin/adminSupprimer les endpoints de debugA07 Auth & Session Failuresbcrypt/argon2 pour les passwordsCSPRNG pour les tokensExpiration et invalidation des sessionsA09 Logging & MonitoringLoguer les checs dauthentificationLoguer les changements de droitsNe pas loguer les donnes sensibles passwords tokens
OPS·BRAIN v1.092 notes · Securitylocal