MFormations
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

  1. Injection - SQL, NoSQL, OS command
  2. Broken Authentication - Mots de passe faibles
  3. Sensitive Data Exposure - Donnees non chiffrees
  4. XXE - XML External Entities
  5. Broken Access Control - Privilege escalation
  6. Security Misconfiguration - Default credentials
  7. XSS - Cross-Site Scripting
  8. Insecure Deserialization - Pickle, JSON
  9. Known Vulnerabilities - Dependances obsoletes
  10. 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

  1. Never trust user input
  2. Use parameterized queries for all DB access
  3. Hash passwords with Argon2id
  4. Use HTTPS everywhere
  5. Rotate secrets regularly
  6. Scan dependencies in CI/CD
  7. Apply least privilege principle
  8. Log security events