Skip to main content

Flask: HTTPS and SSL Configuration

Flask: HTTPS and SSL Configuration

Enabling HTTPS in Flask applications is critical for securing data in transit, protecting user privacy, and ensuring compliance with modern web standards. HTTPS encrypts communication between clients and servers using SSL/TLS protocols, preventing eavesdropping and tampering. This tutorial explores HTTPS and SSL configuration in Flask, covering setup, certificate management, and best practices for secure deployment.


01. Why Use HTTPS in Flask?

HTTPS ensures data confidentiality, integrity, and authenticity, mitigating risks like man-in-the-middle attacks. Flask, a lightweight Python web framework, requires explicit configuration to enable HTTPS, as it does not include built-in SSL/TLS support. Using HTTPS is essential for user trust, SEO benefits, and compliance with standards like GDPR and PCI-DSS.

Example: Basic HTTPS with Ad-Hoc SSL

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Secure Flask Application"

if __name__ == '__main__':
    app.run(ssl_context='adhoc', debug=True)

Output:

* Running on https://127.0.0.1:5000
(Browser shows self-signed certificate warning; accepts for testing)

Explanation:

  • ssl_context='adhoc' - Generates a temporary self-signed certificate for local testing.
  • Suitable for development but not production due to browser warnings.

02. Key HTTPS and SSL Configuration Techniques

Configuring HTTPS in Flask involves setting up SSL certificates, enabling secure server configurations, and integrating with production-ready servers. These techniques ensure robust encryption and scalability. The table below summarizes key techniques and their applications:

Technique Description Use Case
Self-Signed Certificates Use temporary certificates Local development and testing
Production Certificates Use trusted CA certificates Public-facing applications
Reverse Proxy with SSL Offload SSL to Nginx/Apache Scalable production deployments
Secure Cookies Restrict cookies to HTTPS Protect session data
HSTS Headers Enforce HTTPS connections Prevent HTTP downgrade attacks


2.1 Self-Signed Certificates for Development

Example: Custom Self-Signed Certificate

from flask import Flask
import ssl

app = Flask(__name__)

@app.route('/')
def index():
    return "Secure Flask Application"

if __name__ == '__main__':
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    context.load_cert_chain(certfile='cert.pem', keyfile='key.pem')
    app.run(ssl_context=context, debug=True)

Generate Certificate (Bash):

openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365

Output:

* Running on https://127.0.0.1:5000
(Browser shows self-signed certificate warning)

Explanation:

  • openssl - Generates a self-signed certificate and key.
  • Useful for testing HTTPS locally; not trusted by browsers.

2.2 Production Certificates with Let’s Encrypt

Example: Flask with Let’s Encrypt Certificate

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Secure Flask Application"

if __name__ == '__main__':
    app.run(ssl_context=('/path/to/fullchain.pem', '/path/to/privkey.pem'), debug=False)

Obtain Certificate (Bash):

sudo certbot certonly --standalone -d example.com

Output:

* Running on https://example.com:5000
(Browser trusts certificate, no warnings)

Explanation:

  • certbot - Obtains free, trusted certificates from Let’s Encrypt.
  • Certificates stored in /etc/letsencrypt/live/example.com/ for use in Flask.

2.3 Reverse Proxy with Nginx for SSL

Example: Nginx SSL Termination

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Secure Flask Application"

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000, debug=False)

Nginx Configuration (/etc/nginx/sites-available/flask_app):

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

Output:

* Flask running on http://127.0.0.1:8000
* Nginx serves https://example.com
(HTTP requests redirect to HTTPS)

Explanation:

  • Nginx handles SSL termination, forwarding requests to Flask over HTTP.
  • Redirects HTTP to HTTPS for consistent security.

2.4 Secure Cookies for HTTPS

Example: Configuring Secure Cookies

from flask import Flask, session, redirect, url_for

app = Flask(__name__)
app.config.update(
    SECRET_KEY='your-secret-key',
    SESSION_COOKIE_SECURE=True,  # Cookies only over HTTPS
    SESSION_COOKIE_HTTPONLY=True,  # Prevent JavaScript access
    SESSION_COOKIE_SAMESITE='Lax'  # Mitigate CSRF
)

@app.route('/login')
def login():
    session['user_id'] = '123'
    return redirect(url_for('dashboard'))

@app.route('/dashboard')
def dashboard():
    return f"Welcome, user {session.get('user_id')}"

if __name__ == '__main__':
    app.run(ssl_context='adhoc', debug=True)

Output:

* Running on https://127.0.0.1:5000
(Request to /login: Sets secure cookie, redirects to /dashboard)
(Cookie only sent over HTTPS)

Explanation:

  • SESSION_COOKIE_SECURE=True - Ensures cookies are only transmitted over HTTPS.
  • Protects session data from interception.

2.5 Enforcing HTTPS with HSTS

Example: Adding HSTS Headers

from flask import Flask

app = Flask(__name__)

@app.after_request
def add_hsts_header(response):
    response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
    return response

@app.route('/')
def index():
    return "Secure Flask Application"

if __name__ == '__main__':
    app.run(ssl_context='adhoc', debug=True)

Output:

* Running on https://127.0.0.1:5000
(Browser enforces HTTPS for 1 year due to HSTS)

Explanation:

  • Strict-Transport-Security - Instructs browsers to use HTTPS only.
  • max-age=31536000 - Enforces policy for one year.

2.6 Insecure Configuration Example

Example: Running Flask Without SSL

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Insecure Flask Application"

if __name__ == '__main__':
    app.run(debug=True)

Output:

* Running on http://127.0.0.1:5000
(Data transmitted in plaintext, vulnerable to interception)

Explanation:

  • Running over HTTP exposes sensitive data like passwords or cookies.
  • Solution: Enable HTTPS with a trusted certificate or reverse proxy.

03. Effective Usage

3.1 Recommended Practices

  • Use trusted certificates (e.g., Let’s Encrypt) in production.

Example: Comprehensive HTTPS Setup with Nginx

from flask import Flask, session

app = Flask(__name__)
app.config.update(
    SECRET_KEY='your-secret-key',
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE='Lax'
)

@app.after_request
def add_security_headers(response):
    response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
    return response

@app.route('/login')
def login():
    session['user_id'] = '123'
    return "Logged in securely"

@app.route('/')
def index():
    return f"Secure Flask Application, user: {session.get('user_id', 'Guest')}"

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000, debug=False)

Nginx Configuration (/etc/nginx/sites-available/flask_app):

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

Output:

* Flask running on http://127.0.0.1:8000
* Nginx serves https://example.com
(HTTP redirects to HTTPS; secure cookies and HSTS enforced)
  • Uses Let’s Encrypt for trusted certificates.
  • Nginx offloads SSL, redirects HTTP, and enforces modern TLS protocols.
  • Secure cookies and HSTS enhance security.

3.2 Practices to Avoid

  • Avoid using self-signed certificates or HTTP in production.

Example: Insecure Production Setup

from flask import Flask

app = Flask(__name__)
app.config['SESSION_COOKIE_SECURE'] = False  # Allows cookies over HTTP

@app.route('/')
def index():
    return "Insecure Flask Application"

if __name__ == '__main__':
    app.run(ssl_context='adhoc', debug=True, host='0.0.0.0')

Output:

* Running on https://0.0.0.0:5000
(Browser shows self-signed certificate warning; cookies vulnerable over HTTP)
  • Self-signed certificates cause trust issues; SESSION_COOKIE_SECURE=False risks session hijacking.
  • Solution: Use trusted certificates and enforce secure cookies.

04. Common Use Cases

4.1 Secure User Authentication

Protect login sessions with HTTPS and secure cookies.

Example: Secure Login System

from flask import Flask, session, request, redirect, url_for

app = Flask(__name__)
app.config.update(
    SECRET_KEY='your-secret-key',
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE='Lax'
)

@app.after_request
def add_hsts_header(response):
    response.headers['Strict-Transport-Security'] = 'max-age=31536000'
    return response

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['user_id'] = '123'
        return redirect(url_for('dashboard'))
    return '''
        <form method="post">
            <input type="submit" value="Login">
        </form>
    '''

@app.route('/dashboard')
def dashboard():
    return f"Welcome, user {session.get('user_id', 'Guest')}"

if __name__ == '__main__':
    app.run(ssl_context='adhoc', debug=True)

Output:

* Running on https://127.0.0.1:5000
(POST to /login: Sets secure cookie, redirects to /dashboard)

Explanation:

  • HTTPS ensures secure transmission of credentials and session data.
  • HSTS and secure cookies protect against downgrade attacks.

4.2 Secure API Endpoints

Serve APIs over HTTPS to protect sensitive data.

Example: Secure API with Nginx

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/api/data')
def data():
    return jsonify({'message': 'Secure API data'})

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000, debug=False)

Nginx Configuration:

server {
    listen 443 ssl;
    server_name api.example.com;

    ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
    }
}

Output:

* Flask running on http://127.0.0.1:8000
* Nginx serves https://api.example.com
(Request to /api/data: Returns {"message": "Secure API data"} over HTTPS)

Explanation:

  • Nginx handles SSL, ensuring encrypted API communication.
  • Scalable for high-traffic APIs.

Conclusion

Configuring HTTPS and SSL in Flask is essential for secure web applications. Key takeaways:

  • Use self-signed certificates for development and trusted certificates (e.g., Let’s Encrypt) for production.
  • Leverage reverse proxies like Nginx for scalable SSL termination.
  • Enable secure cookies and HSTS to protect sessions and enforce HTTPS.
  • Avoid HTTP or self-signed certificates in production to maintain trust and security.

With these practices, you can deploy Flask applications that safeguard user data and meet modern security standards!

Comments