Modern Python Engineering
Chapitre 12
12 - Securite Python
> **Duree :** 3 semaines > **Objectif :** Maitriser la securite des applications Python : OWASP, auth, crypto, devsecops.
Cours 12 : Securite Python
1. OWASP Top 10 Python
- Injection - SQL, NoSQL, OS command
- Broken Authentication - Mots de passe faibles
- Sensitive Data Exposure - Donnees non chiffrees
- XXE - XML External Entities
- Broken Access Control - Privilege escalation
- Security Misconfiguration - Default credentials
- XSS - Cross-Site Scripting
- Insecure Deserialization - Pickle, JSON
- Known Vulnerabilities - Dependances obsoletes
- Insufficient Logging - Manque de tracabilite
2. SQL Injection
2.1 Prevention
# MAUVAIS - concatenation
cursor.execute(f"SELECT * FROM users WHERE email = {email}")
# BON - parametres prepares
cursor.execute("SELECT * FROM users WHERE email = ?", (email,))
# Avec SQLAlchemy 2.0
session.exec(select(User).where(User.email == email))
2.2 Raw SQL securise
from sqlalchemy import text
# Utiliser :param avec text()
result = session.execute(
text("SELECT * FROM users WHERE email = :email"),
{"email": email}
)
3. XSS (Cross-Site Scripting)
# Jinja2 auto-escaping (par defaut)
return render_template("hello.html", name=name)
# Si name = "<script>alert('xss')</script>" -> safe
# Si HTML volontaire
from markupsafe import Markup
content = Markup("<b>safe</b>")
4. CSRF (Cross-Site Request Forgery)
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from starlette.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://app.example.com"],
allow_credentials=True,
allow_methods=["GET", "POST"],
allow_headers=["*"],
)
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["example.com", "*.example.com"]
)
5. Authentification JWT
from jose import jwt, JWTError
from datetime import datetime, timedelta
import os
SECRET = os.getenv("JWT_SECRET")
ALGORITHM = "HS256"
def create_token(user_id: str) -> str:
payload = {
"sub": user_id,
"exp": datetime.utcnow() + timedelta(hours=1),
"iat": datetime.utcnow(),
}
return jwt.encode(payload, SECRET, algorithm=ALGORITHM)
def verify_token(token: str) -> dict:
try:
return jwt.decode(token, SECRET, algorithms=[ALGORITHM])
except JWTError:
raise HTTPException(status_code=401, detail="Token invalide")
6. OAuth2 / OIDC
from authlib.integrations.starlette_client import OAuth
oauth = OAuth()
oauth.register(
name="google",
client_id=os.getenv("GOOGLE_CLIENT_ID"),
client_secret=os.getenv("GOOGLE_CLIENT_SECRET"),
server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
client_kwargs={"scope": "openid email profile"},
)
7. Password Hashing
from passlib.hash import argon2, bcrypt
# Argon2 (recommande OWASP)
hashed = argon2.hash("mon_password")
argon2.verify("mon_password", hashed) # True
# bcrypt (alternative)
hashed = bcrypt.hash("mon_password")
bcrypt.verify("mon_password", hashed) # True
8. Cryptography
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
# Symetrique (Fernet)
key = Fernet.generate_key()
cipher = Fernet(key)
token = cipher.encrypt(b"donnees sensibles")
data = cipher.decrypt(token)
# Asymetrique (RSA)
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
9. Secrets Management
# .env file
# DATABASE_URL=postgresql://user:pass@localhost/db
# API_KEY=sk-1234567890
from dotenv import load_dotenv
import os
load_dotenv()
DB_URL = os.environ["DATABASE_URL"]
API_KEY = os.environ["API_KEY"]
# Production: HashiCorp Vault
import hvac
client = hvac.Client(url="https://vault.example.com", token=os.getenv("VAULT_TOKEN"))
secret = client.secrets.kv.v2.read_secret_version(path="api-keys")
10. Dependency Scanning
# Safety
pip install safety
safety check --full-report
# pip-audit
pip install pip-audit
pip-audit
# Automatisation CI/CD
# .github/workflows/security.yml
name: Security Scan
on: [push]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install safety
- run: safety check --policy-file .safety-policy.yml
11. Bandit (Static Analysis)
pip install bandit
bandit -r src/ -f json -o bandit-report.json
bandit -r src/ -f html -o bandit-report.html
# bandit detects:
# - Hardcoded passwords
# - SQL injection
# - eval() usage
# - Pickle loading
# - Request without timeout
12. Secure Headers
from secure import SecureHeaders
secure_headers = SecureHeaders()
secure_headers.csp("default-src 'self'")
secure_headers.hsts("max-age=31536000")
secure_headers.xfo("DENY")
@app.middleware("http")
async def headers_middleware(request, call_next):
response = await call_next(request)
secure_headers.framework.fastapi(response)
return response
13. Diagramme
Diagramme en cours de génération...
14. Bonnes pratiques
- Never trust user input
- Use parameterized queries for all DB access
- Hash passwords with Argon2id
- Use HTTPS everywhere
- Rotate secrets regularly
- Scan dependencies in CI/CD
- Apply least privilege principle
- Log security events