Flask is unopinionated by design. That freedom is powerful, but it also means security is your responsibility. This checklist covers the critical areas you need to harden before your app faces the public internet.

1. Use Environment Variables for Secrets

Never commit secrets to version control. Use .env files (loaded via python-dotenv) and ensure .env is in your .gitignore.

import os
from dotenv import load_dotenv
load_dotenv()

app.secret_key = os.environ.get('SECRET_KEY')

2. Enable Security Headers

Use flask-talisman to set strict transport security, content security policy, and other modern security headers.

from flask_talisman import Talisman
Talisman(app, force_https=True, content_security_policy={
    'default-src': "'self'",
    'script-src': ["'self'", "'unsafe-inline'"],
})

3. Protect Against SQL Injection

Always use parameterized queries. Never use Python string formatting or concatenation for SQL.

# WRONG
cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")

# RIGHT
cursor.execute("SELECT * FROM users WHERE email = ?", (email,))

4. Escape Output to Prevent XSS

Jinja2 auto-escapes by default, but using |safe bypasses this. Only mark content as safe if you generated it yourself or sanitized it with bleach.

5. Implement Rate Limiting

Protect login and sensitive endpoints from brute-force attacks.

from flask_limiter import Limiter

limiter = Limiter(app, key_func=lambda: request.remote_addr)

@app.route('/login', methods=['POST'])
@limiter.limit("5 per minute")
def login():
    ...

6. Use HTTPS in Production

Obtain a free certificate from Let's Encrypt and redirect all HTTP traffic to HTTPS. Set SESSION_COOKIE_SECURE = True so cookies are only sent over TLS.

7. Validate and Sanitize All Input

Use WTForms with validators. Never trust client-side validation alone. Validate file uploads for type and size.

8. Keep Dependencies Updated

pip list --outdated
pip install --upgrade flask werkzeug jinja2

Summary

AreaTool / Approach
Secretspython-dotenv
Headersflask-talisman
SQL InjectionParameterized queries
XSSJinja2 autoescape + bleach
Brute Forceflask-limiter
TransportLet's Encrypt + HTTPS redirect

Security is not a feature you add at the end. It is a habit you build into every line of code.