Flask is famous for its simplicity. A minimal application is just a few lines of code, and you can have a web server running in minutes. But that simplicity can mask a critical security detail that's easy to overlook: the secret key.
Flask's secret key is the cryptographic foundation of its session management. Getting it wrong can expose your users to session forgery, data tampering, and account takeover. Getting it right is straightforward once you understand what it does.
What Flask's Secret Key Does
When you set app.secret_key in Flask, you're enabling the session framework. Flask uses this key to sign session cookies, which is how it maintains state across requests in an otherwise stateless protocol.
Here's how it works. When a user visits your Flask app, Flask creates a session dictionary. You can store data in it, like a user ID or a shopping cart. At the end of the request, Flask serializes the session data, signs it with your secret key, and sends it to the user's browser as a cookie.
On the next request, the browser sends the cookie back. Flask verifies the signature using the secret key. If the signature matches, Flask knows the data hasn't been tampered with and deserializes it. If the signature doesn't match, Flask rejects the session.
This is important: the data is signed, not encrypted. The session contents are readable by anyone who has the cookie. The secret key prevents tampering, not viewing. If you're storing sensitive information in sessions, that's a problem you need to address separately.
The Danger of a Weak or Missing Secret Key
If you don't set a secret key at all, Flask will refuse to use sessions. You'll get a RuntimeError telling you the secret key is not set. This is actually good behavior, it prevents you from accidentally running without session protection.
But many developers, eager to get things working, set a weak key just to make the error go away:
python app.secret_key = 'secret'
This is catastrophically bad. An attacker who guesses or discovers this key can forge any session cookie they want. They can set the session to claim they're any user ID, including administrators. They can inject arbitrary data into the session, potentially exploiting application logic that trusts session values.
The same risk applies to hardcoded keys in source code. If your key is in your Git repository, it's effectively public. Every developer who has touched the codebase has it. Every CI system, every deployment tool, every backup. If the repo is public, automated bots will find it within hours.
How to Set the Secret Key Properly
The correct approach is to load the key from an environment variable:
python import os from flask import Flask
app = Flask(__name__) app.secret_key = os.environ.get('SECRET_KEY')
Or equivalently:
python app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
For local development, use a .env file with python-dotenv:
python from dotenv import load_dotenv load_dotenv()
app = Flask(__name__) app.secret_key = os.environ.get('SECRET_KEY')
Create a .env file (and add it to .gitignore):
SECRET_KEY=your-generated-key-here
For production, use your hosting platform's environment variable system. Heroku config vars, Docker environment variables, Kubernetes secrets, or a dedicated secrets manager. The key should never appear in your codebase.
Generating a Strong Flask Secret Key
A strong secret key should be long, random, and generated by a cryptographically secure random number generator. Python's secrets module is the right tool:
python import secrets print(secrets.token_hex(32))
This generates 32 bytes of randomness, rendered as a 64-character hexadecimal string. That's 256 bits of entropy, which is more than sufficient.
You can also use token_urlsafe for a shorter, URL-safe string:
python import secrets print(secrets.token_urlsafe(32))
Both methods use the operating system's CSPRNG, which is the same source Django uses for its secret key generation. For a comparison of how Django and Flask handle secret keys differently, read our post on Django's SECRET_KEY explained.
You can also generate a key using our Django Key Generator, which works equally well for Flask since both frameworks need the same type of cryptographically random string.
Session Security: Signing vs Encryption
One of the most common misconceptions about Flask sessions is that the data is encrypted. It's not.
Flask uses itsdangerous, a signing library, to create and verify session cookies. The process is:
1. Serialize the session data to JSON 2. Base64-encode the serialized data 3. Sign it with HMAC using your secret key 4. Send the signed payload as a cookie
The result is a cookie that looks like gibberish, but it's actually just encoded and signed. Anyone who intercepts the cookie can base64-decode it and read the contents. They can't modify it without invalidating the signature, but they can see what's inside.
This means you should never store sensitive data in Flask sessions without additional protection. Passwords, API keys, personal information, and other secrets should not go in the session cookie directly.
If you need to store sensitive data, you have options:
**Server-side sessions.** Use Flask-Session to store session data on the server (in Redis, a database, or the filesystem) instead of in the cookie. The cookie only contains a session ID, and the actual data lives on your server.
**Encrypt before storing.** Encrypt sensitive values before putting them in the session, and decrypt them when you read them back. This adds a layer of protection on top of the signing.
**Don't store it at all.** If you don't need to persist sensitive data across requests, don't put it in the session. Store a reference (like a user ID) and fetch the sensitive data from the database when needed.
Common Mistakes to Avoid
**Hardcoding the key.** app.secret_key = 'my-secret-key' in your source code is the most common mistake. The key should always come from an environment variable.
**Using a weak key.** 'secret', 'password', 'changeme', or any dictionary word is trivially guessable. The key should be a random string of at least 32 bytes.
**Committing the key to Git.** Even if your repo is private, this is a bad practice. Developers come and go, repos get forked, and accidents happen. Use environment variables and .gitignore.
**Using the same key across environments.** Development, staging, and production should have different keys. If your development key leaks, your production sessions should still be secure.
**Never rotating the key.** If a key might have been exposed, rotate it. Yes, users will need to log in again. That's a minor inconvenience compared to a session forgery attack.
Flask-Session for Enhanced Security
For applications that need stronger session security, Flask-Session is worth considering. It moves session storage from the client-side cookie to the server side.
python from flask import Flask from flask_session import Session
app = Flask(__name__) app.secret_key = os.environ.get('SECRET_KEY') app.config['SESSION_TYPE'] = 'redis' Session(app)
With server-side sessions, the cookie only contains a session ID. The actual data lives in your Redis instance, database, or filesystem. This means:
- Session data can be any size, not limited by cookie size constraints - Sensitive data isn't sent to the client at all - You can invalidate sessions server-side by deleting them - The secret key still matters for signing the session ID cookie, but the data itself is protected
The tradeoff is complexity. You need to manage the session store, handle cleanup, and deal with the infrastructure. For many applications, signed cookies are sufficient. For those handling sensitive data, server-side sessions are the better choice.
The Bottom Line
Flask's secret key is simple to set but critical to get right. It's the foundation of your session security, and treating it casually puts your users at risk.
Load it from an environment variable. Generate it with a CSPRNG. Never commit it. Rotate it if it might be exposed. And understand that Flask sessions are signed, not encrypted, so don't store sensitive data in them without additional protection.
For more on the broader topic of cryptographic key generation and why CSPRNGs matter, read our explanation of CSPRNGs and crypto.getRandomValues().