When developing with Flask, engineers must assume responsibility for securing all application inputs, session lifecycles, and state transitions, as the framework provides core routing and request handling without automatic protection against common vulnerabilities. Security-sensitive surfaces include authentication flows, blueprint routing boundaries, error responses, configuration loading, and file uploads. Developers must ensure that security assumptions fail closed by explicitly validating all incoming payloads, enforcing resource ownership, and hardening deployment boundaries.
Essential implementation rules
Enforce Resource Ownership Checks
Verify that the authenticated user matches the resource owner before permitting any state modifications. Query the database for the target resource and use abort(404) if the resource is missing or abort(403) if unauthorized to prevent Insecure Direct Object Reference vulnerabilities.
Configure Production Error Handling and Exception Behavior
Disable DEBUG, PROPAGATE_EXCEPTIONS, TRAP_BAD_REQUEST_ERRORS, and TRAP_HTTP_EXCEPTIONS in production. Register explicit error handlers to log unhandled exceptions internally while returning sanitized responses without stack traces.
Preserve Request Context in Background Tasks
Decorate functions dispatched to background threads or greenlets with @flask.copy_current_request_context so request and session remain safely accessible without raising runtime errors.
Secure Routing and Blueprint Registration
Specify explicit uppercase HTTP methods for routes, position @app.route as the outermost decorator, configure explicit URL prefixes for blueprints, use parameter converters like int or uuid, and set TRUSTED_HOSTS.
Validate Authentication Inputs
Check incoming username and password inputs on the server side to ensure they are present before querying databases or executing authentication routines.
Implement Secure WSGI Middleware and Context Boundaries
Isolate application components using DispatcherMiddleware, unwrap thread-local proxies via _get_current_object() across thread boundaries, validate HTTP_HOST headers, and wrap app.wsgi_app with ProxyFix when behind a trusted reverse proxy.
Respect Environment Variable Precedence for Configurations
Rely on process environment variables for critical runtime secrets and configuration values. Ensure load_dotenv() does not overwrite existing variables in os.environ.
Hash and Verify Passwords Securely
Use Werkzeug’s generate_password_hash to hash user passwords prior to storage and check_password_hash during login credential validation.
Implement CSRF Protection and SameSite Cookie Policies
Manually validate anti-CSRF tokens on state-changing requests like POST. Configure SESSION_COOKIE_SAMESITE to Lax or Strict and set SESSION_COOKIE_SECURE and SESSION_COOKIE_HTTPONLY to true.
Disable Debug Mode and Development Server in Production
Never enable debug mode or run the built-in development server in production environments. Deploy applications exclusively using dedicated production WSGI servers such as Gunicorn.
Restrict Jinja Safe Filter and Escape Untrusted Output
Apply the Jinja |safe filter exclusively to trusted HTML generated by WTForms field widgets. Explicitly escape untrusted user input using markupsafe.escape() or rely on Jinja’s automatic escaping, and use |tojson for embedded JavaScript.
Sanitize Filenames and Use Directory-Restricted File Serving
Sanitize untrusted filenames using werkzeug.utils.secure_filename before writing files to disk. Use send_from_directory instead of send_file to serve user-requested files safely within a designated base folder.
Use Parameterized Queries for Database Interactions
Pass dynamic parameters separately from SQL statements using placeholders like ? or named parameters to prevent SQL injection vulnerabilities.
Validate Form Data Against Explicit Input Constraints
Bind HTTP request data to form validation classes and execute validation methods like form.validate() before processing submitted payloads in business logic or database operations.
Configure Security Headers and Response Protocols
Set HTTP security headers such as Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options, and X-Frame-Options on outgoing responses or via after_request hooks, and ensure Vary: Cookie is set when session data is accessed.
Configure Request Size Limits and Resource Thresholds
Restrict total payload size globally or per-request using MAX_CONTENT_LENGTH or Request.max_content_length, and configure MAX_FORM_MEMORY_SIZE and MAX_FORM_PARTS to prevent resource exhaustion.
Manage Secure Sessions and State Lifecycles
Call session.clear() before writing authentication identifiers during login and entirely upon logout. Define PERMANENT_SESSION_LIFETIME and set session.modified = True when updating nested mutable structures.
flask: All Security Cards
Approximately 5,906 tokens
On this card
Category: access control
Enforce Resource Ownership Checks Before Modifying Application Resources
Use when
When building state-changing operations or resource modification routes in Flask where authenticated users perform updates or deletions on records.
Secure rules
Rule 1: Verify that the authenticated user matches the resource owner or author before permitting state modifications
Query the database for the target resource and compare its owner identifier or its access against the current user in g.user. If the resource does not exist, raise an HTTP 404 error using abort(404), and if the user does not own the resource, raise an HTTP 403 Forbidden error using abort(403) to prevent unauthorized access and Insecure Direct Object Reference vulnerabilities.
Category: api contract misuse
Configure Flask error handling and production exception behavior
Use when
Configuring application error handling, setting production flags, and registering custom error handlers or teardown hooks.
Secure rules
Rule 1: Keep HTTP exception trapping and exception propagation disabled in production
Ensure TRAP_BAD_REQUEST_ERRORS, TRAP_HTTP_EXCEPTIONS, and PROPAGATE_EXCEPTIONS remain set to False in production configurations so that unhandled exceptions and standard HTTP errors are properly caught by custom error handlers instead of crashing requests or surfacing internal tracebacks.
Rule 2: Sanitize internal server error responses and differentiate HTTP exceptions
Explicitly differentiate HTTPException instances within generic error handlers to preserve original HTTP status codes and headers, and sanitize 500 error handlers to log original exceptions internally while returning obscured client responses.
from flask import json, render_templatefrom werkzeug.exceptions import HTTPException@app.errorhandler(500)def handle_500(e): original = getattr(e, "original_exception", None) if original: app.logger.error(f"Unhandled exception: {original}") return render_template("500.html"), 500
Rule 3: Register explicit application error handlers for HTTP codes and exception classes
Register explicit application error handlers using Flask’s @app.errorhandler decorator for integer HTTP status codes, Werkzeug exceptions, and standard Python exceptions to prevent internal runtime errors from leaking diagnostic information.
from werkzeug.exceptions import BadRequest@app.errorhandler(400)@app.errorhandler(BadRequest)def handle_bad_request(e): return "Invalid request", 400
Decorate background tasks with request context preservation
Use when
Spawning background tasks, greenlets, or worker threads from within an active Flask request context where proxies like request and session must be accessed.
Secure rules
Rule 1: Use @flask.copy_current_request_context to preserve active request context in spawned background tasks.
Decorate functions dispatched to background threads or greenlets with @flask.copy_current_request_context so that flask.request and flask.session remain safely accessible inside the sub-task execution without raising runtime errors or leaking state across threads.
Defining routes, blueprints, HTTP methods, and URL parameters in Flask applications.
Secure rules
Rule 1: Explicitly specify uppercase HTTP methods or use method-specific decorators when defining routes
When defining Flask routes using @app.route() or app.add_url_rule(), specify allowed HTTP methods as an iterable of uppercase string names or use method-specific decorators like @app.get() and @app.post().
Rule 2: Position the route decorator as the outermost decorator on view functions
When applying custom decorators to Flask view functions, ensure @app.route is always positioned as the outermost decorator so that Flask registers the wrapped function rather than the raw inner handler.
Rule 3: Use explicit URL prefixes when registering blueprints
Always configure an explicit url_prefix when registering blueprints to avoid route shadowing and namespace collisions with primary application routes or static handlers.
Use parameter converters such as int, float, or uuid in URL route path templates instead of relying on default generic string or path converters to validate parameter types before reaching view handlers.
Handling user registration and login requests to verify presented identity credentials before creating session variables or querying the database.
Secure rules
Rule 1: Validate username and password fields on the server side prior to processing authentication or registration requests.
Check all incoming username and password inputs in your route handler to ensure they are present before proceeding with database queries or establishing authentication sessions.
@bp.route('/register', methods=('GET', 'POST'))def register(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] if not username: flash('Username is required.') elif not password: flash('Password is required.')
Category: boundary control
Implement Secure WSGI Middleware and Context Isolation in Flask
Use when
Developing or configuring custom WSGI middleware, reverse proxy integrations, and application context routing boundaries in Flask applications.
Secure rules
Rule 1: Isolate distinct application components at the WSGI layer using Werkzeug DispatcherMiddleware
Wrap distinct application components like public frontends and administrative backends with DispatcherMiddleware to ensure each maintains an isolated configuration and route handling within the same process.
from werkzeug.middleware.dispatcher import DispatcherMiddlewarefrom frontend_app import application as frontendfrom backend_app import application as backendapplication = DispatcherMiddleware(frontend, { '/backend': backend})
Rule 2: Unwrap thread-local proxies before passing request data across background boundaries
Call _get_current_object() on proxy objects like request, g, or current_app to retrieve the underlying instance before passing context across thread or worker boundaries.
Rule 3: Validate host header input when dynamically dispatching requests in custom WSGI middleware
Strictly validate and sanitize HTTP_HOST headers when writing custom WSGI middleware for dynamic dispatching, and return explicit WSGI exception applications like NotFound() for unmatched domain boundaries.
from werkzeug.exceptions import NotFoundclass SubdomainDispatcher: def __init__(self, domain, create_app): self.domain = domain.lower() self.create_app = create_app def get_application(self, host): host = host.split(':')[0].lower() if not host.endswith(f".{self.domain}") and host != self.domain: return NotFound() subdomain = host[:-len(self.domain)].rstrip('.') app = self.create_app(subdomain) return app if app is not None else NotFound()
Rule 4: Reassign app.wsgi_app when integrating custom WSGI middleware and align path configurations
Reassign app.wsgi_app rather than replacing the main Flask application object, and ensure path modifications in the WSGI environment align with Flask configurations like APPLICATION_ROOT.
Rule 5: Wrap application with trusted WSGI middleware such as ProxyFix to parse reverse proxy headers securely
Wrap app.wsgi_app with Werkzeug’s ProxyFix during application setup when the application is deployed behind a trusted reverse proxy. Configure only the forwarded headers required by your deployment, and set each option to the correct number of trusted proxies.
from flask import Flaskfrom werkzeug.middleware.proxy_fix import ProxyFixapp = Flask(__name__)# Use when the deployment only needs the original client IP address# and request scheme (HTTP/HTTPS).app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)# Use when the reverse proxy also forwards the original host and URL# prefix (for example, when the application is served under a custom# domain or a path such as "/app").app.wsgi_app = ProxyFix( app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1,)
Rule 6: Write defensive teardown callbacks to guarantee resource cleanup
Ensure teardown_request middleware callbacks handle missing context state gracefully and execute without raising unhandled exceptions.
@app.teardown_requestdef cleanup_resources(exception): db = getattr(g, 'db', None) if db is not None: db.close()
Category: configuration source integrity
Respect environment variable precedence when loading configuration files
Use when
When loading configuration and environment variables via load_dotenv() in Flask applications.
Secure rules
Rule 1: Rely on process environment variables for critical runtime configuration and ensure environment variable precedence is respected.
When loading configuration files using load_dotenv(), existing process variables in os.environ take priority and are not overwritten by .env or .flaskenv files. Ensure sensitive production credentials are supplied through process environment variables rather than source-controlled configuration files.
from flask.cli import load_dotenv# load_dotenv loads .env and .flaskenv without replacing existing os.environ variablesload_dotenv()
Category: cryptography
Hash User Passwords Securely
Use when
When registering new users or validating user login credentials in a web application.
Secure rules
Rule 1: Hash user passwords securely before storage and verify submitted credentials against the stored hash.
Use Werkzeug’s generate_password_hash to hash user passwords before storing them in persistent storage. When authenticating users during login, use check_password_hash to securely verify the submitted password against the stored hash.
from werkzeug.security import generate_password_hash, check_password_hash# Storing hashed password during registrationhashed_password = generate_password_hash(password)db.execute("INSERT INTO user (username, password) VALUES (?, ?)", (username, hashed_password))# Validating password during loginif check_password_hash(user["password"], password): session["user_id"] = user["id"]
Category: csrf
Implement Anti-CSRF Tokens and SameSite Cookie Protection
Use when
Developing state-changing routes and configuring session cookie policies in Flask applications to prevent cross-site request forgery.
Secure rules
Rule 1: Validate anti-CSRF tokens on state-changing requests.
Since Flask does not provide built-in form validation or CSRF protection by default, developers must manually issue a token, store it in a cookie, transmit it with form data, and verify that both values match on state-modifying requests like POST.
@app.route('/user/delete', methods=['POST'])def delete_user(): token_in_cookie = request.cookies.get('csrf_token') token_in_form = request.form.get('csrf_token') if not token_in_cookie or token_in_cookie != token_in_form: abort(400, 'CSRF token verification failed') # Process user deletion safely
Rule 2: Configure SameSite cookie protection for session and response cookies.
Set SESSION_COOKIE_SAMESITE to 'Lax' or 'Strict' in the Flask configuration and pass samesite='Lax' when creating custom response cookies using response.set_cookie() to prevent browsers from sending cookies on cross-site requests.
Disable Debug Mode and Built-in Server in Production
Use when
Configuring deployment settings and launching the application in a production environment.
Secure rules
Rule 1: Avoid running the built-in development server or enabling debug mode in production to prevent arbitrary code execution vulnerabilities.
Do not pass --debug or run the built-in development server in a production environment, because the interactive debugger allows arbitrary Python code execution from the browser. Instead, deploy the application using a dedicated production WSGI server.
gunicorn -w 4 'hello:app'
Category: escape hatch
Restrict Jinja safe filter to trusted HTML generation
Use when
Rendering trusted field widget calls within template macros where HTML structure must be preserved.
Secure rules
Rule 1: Apply the Jinja |safe filter exclusively to trusted HTML generated by WTForms field widget calls.
Isolate the |safe filter to framework-managed widget rendering functions within templates, allowing automatic escaping to protect error messages and labels instead of applying it to raw user input.
Safely handle user-provided file names and paths during uploads and downloads
Use when
Handling file uploads from users or serving requested files from the filesystem.
Secure rules
Rule 1: Sanitize untrusted filenames using werkzeug.utils.secure_filename before saving them to the filesystem.
Always sanitize untrusted filenames using werkzeug.utils.secure_filename before combining them with upload paths or storing them on the filesystem to prevent directory traversal sequences from writing files outside the intended upload directory.
from werkzeug.utils import secure_filenameimport os@app.route('/upload', methods=['POST'])def upload_file(): file = request.files['file'] if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
Rule 2: Use send_from_directory instead of send_file when serving user-provided paths.
Never pass user-supplied file paths directly to send_file(), because it treats path arguments as trusted input and does not perform path traversal containment checks. Always use send_from_directory() to serve user-requested files safely from within a designated base directory.
from flask import send_from_directory@app.get('/uploads/<path:filename>')def download_file(filename): return send_from_directory('/var/www/uploads', filename)
Category: injection
Use Parameterized Queries to Prevent SQL Injection
Use when
Writing database queries with dynamic user input in Flask applications.
Secure rules
Rule 1: Pass dynamic parameters separately from the SQL statement using placeholders.
When executing SQL statements or database commands, always pass user-supplied input as parameterized arguments using placeholders such as ? or named parameters rather than using string interpolation, formatting, or concatenation.
db = get_db()db.execute( 'INSERT INTO post (title, body, author_id) VALUES (?, ?, ?)', (title, body, g.user['id']))db.commit()
Category: input contract definition
Validate form data against explicit input constraints before processing
Use when
Handling incoming HTTP request form data or query parameters that require structural and type validation before executing business logic.
Secure rules
Rule 1: Define explicit input validation rules on form models and enforce validation checks before processing request payloads.
Bind HTTP request data such as request.form or request.args to form validation classes and strictly execute validation methods like form.validate() before accessing submitted fields in your application logic or database operations.
@app.route('/register', methods=['GET', 'POST'])def register(): form = RegistrationForm(request.form) if request.method == 'POST' and form.validate(): user = User(form.username.data, form.email.data, form.password.data) db_session.add(user) return redirect(url_for('login')) return render_template('register.html', form=form)
Category: interface protocol hardening
Configure Security Headers and Vary Cookie Headers on Outgoing Responses
Use when
Developing response processors, custom session interfaces, or after-request hooks in Flask to harden network protocol semantics and enforce browser security controls.
Secure rules
Rule 1: Configure HTTP security headers on outgoing Flask responses or use an extension like Flask-Talisman.
Set security headers such as Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options, and X-Frame-Options on outgoing responses or via an after_request hook to direct the browser to enforce secure transport, restrict resource loading, and prevent MIME-type sniffing or clickjacking.
Rule 2: Preserve the Vary Cookie header for session-dependent responses in custom session interfaces or response processors.
Ensure the Vary: Cookie header is added to the HTTP response whenever session data is accessed or modified by checking session.accessed or session.modified, preventing CDNs and reverse proxies from serving session-tailored content to unauthenticated users.
if session.accessed: response.vary.add("Cookie")
Rely on Request Context Dispatching to Reject Malformed Host Headers
Use when
When handling incoming web requests and validating protocol headers to prevent protocol confusion and request smuggling.
Secure rules
Rule 1: Allow Flask’s request dispatch mechanism to validate incoming WSGI environment headers and automatically reject requests with invalid or non-printable host headers.
Flask’s request context dispatching checks incoming headers and returns a 400 Bad Request error when HTTP Host headers contain invalid or non-printable characters. Applications should rely on this full request dispatch mechanism to automatically reject corrupted headers and prevent request smuggling and host header injection.
Configure Trusted Proxy Settings and Remote Address Verification
Use when
Configuring network trust boundaries and verifying remote IP addresses or proxy configurations in Flask applications.
Secure rules
Rule 1: Explicitly override REMOTE_ADDR when testing or simulating proxy and IP access restrictions.
When writing security tests for IP-based access controls, rate limiters, or trusted proxy rules, explicitly pass custom environ_base dictionaries or request environment variables to simulate untrusted remote IP addresses instead of relying on the default localhost REMOTE_ADDR setting.
Escape Untrusted Input When Rendering HTML Responses in Flask
Use when
Rendering manual HTML strings or dynamic user-controlled data within HTML templates and attributes.
Secure rules
Rule 1: Explicitly escape untrusted user input using markupsafe.escape() or rely on Jinja’s automatic escaping.
Always explicitly escape untrusted user input using markupsafe.escape() when returning manual HTML strings or leverage Jinja’s automatic HTML escaping features by passing dynamic variables into templates rendered via render_template instead of manually interpolating strings.
Rule 2: Use the tojson filter when embedding server-side data into JavaScript or attribute contexts.
Use Jinja’s |tojson filter when embedding server-side data into HTML <script> tags or HTML data attributes to safely serialize and escape the data, and wrap attribute values containing tojson in single quotes.
Configure request size limits and form parsing thresholds
Use when
Configuring Flask application settings to restrict incoming payload sizes and multipart form parsing consumption.
Secure rules
Rule 1: Set global or per-request payload limits and form parsing thresholds.
Use MAX_CONTENT_LENGTH globally or Request.max_content_length per request to restrict total request payload size. Additionally, configure MAX_FORM_MEMORY_SIZE and MAX_FORM_PARTS to control memory usage when parsing non-file multipart form fields.
Deploy Flask Applications Using a Dedicated Production WSGI Server
Use when
Deploying the Flask application to a live production environment rather than running it locally for development.
Secure rules
Rule 1: Avoid running the Flask built-in development server or CLI command in production environments.
Do not deploy Flask applications using the CLI development server flask run in production because the Werkzeug development server lacks concurrency management, resource boundaries, and request-handling safety mechanisms required for live environments. Instead, use a dedicated production WSGI server such as Gunicorn.
# Production deployment command (e.g., Gunicorn):gunicorn -w 4 'myapp:create_app()'
Category: secret handling
Configure Secure Secret Keys and Rotation Mechanisms
Use when
Configuring Flask application secrets and setting up secure credential management for session signing.
Secure rules
Rule 1: Load cryptographic secret keys from environment variables or secure external storage rather than hardcoding them.
Initialize SECRET_KEY using secure environment variables via os.environ to prevent hardcoded credentials from exposing session management to forgery.
Explicitly Invoke Preprocess Request During Testing
Use when
Testing request-dependent code using app.test_request_context() where tests depend on security controls or identity population configured inside before_request hooks.
Secure rules
Rule 1: Explicitly invoke preprocess request within test request contexts to ensure security hooks are executed.
When testing request-dependent code using app.test_request_context(), Flask does not automatically execute request dispatching or before_request hooks. When tests depend on security controls, authentication checks, or identity population configured inside before_request hooks, developers must explicitly call app.preprocess_request() inside the request context or perform full client requests using app.test_client() to prevent bypassing security mechanisms.
Secure Flask Session Cookies and Handle Authentication State
Use when
Configuring session cookie security attributes, managing session lifetimes, and clearing or updating session data during user login, logout, and state modifications.
Secure rules
Rule 1: Configure secure cookie flags and session lifetimes.
Set SESSION_COOKIE_SECURE=True, SESSION_COOKIE_HTTPONLY=True, and SESSION_COOKIE_SAMESITE='Lax' to protect cookies against interception, script theft, and cross-site attacks. Define PERMANENT_SESSION_LIFETIME to bound session validity windows.
Rule 2: Clear session data completely on authentication changes and logout.
Call session.clear() before writing authentication identifier keys during login and entirely when logging out to prevent session fixation and stale session reuse.
Rule 3: Explicitly mark session modified when updating nested mutable objects.
Set session.modified = True after updating nested mutable structures such as dictionaries or lists stored inside the session, ensuring changes are properly tracked and persisted.
from flask import session# Updating top-level dictionary keys is automatically trackedsession["user_id"] = 123# Updating nested objects requires setting session.modified = Trueif "permissions" not in session: session["permissions"] = {}session["permissions"]["is_admin"] = Truesession.modified = True
Enforce Resource Ownership Checks Before Modifying Application Resources
Approximately 174 tokens
Use when
When building state-changing operations or resource modification routes in Flask where authenticated users perform updates or deletions on records.
Secure rules
Rule 1: Verify that the authenticated user matches the resource owner or author before permitting state modifications
Query the database for the target resource and compare its owner identifier or its access against the current user in g.user. If the resource does not exist, raise an HTTP 404 error using abort(404), and if the user does not own the resource, raise an HTTP 403 Forbidden error using abort(403) to prevent unauthorized access and Insecure Direct Object Reference vulnerabilities.
Configure Flask error handling and production exception behavior
Approximately 1,069 tokens
Use when
Configuring application error handling, setting production flags, and registering custom error handlers or teardown hooks.
Secure rules
Rule 1: Keep HTTP exception trapping and exception propagation disabled in production
Ensure TRAP_BAD_REQUEST_ERRORS, TRAP_HTTP_EXCEPTIONS, and PROPAGATE_EXCEPTIONS remain set to False in production configurations so that unhandled exceptions and standard HTTP errors are properly caught by custom error handlers instead of crashing requests or surfacing internal tracebacks.
Rule 2: Sanitize internal server error responses and differentiate HTTP exceptions
Explicitly differentiate HTTPException instances within generic error handlers to preserve original HTTP status codes and headers, and sanitize 500 error handlers to log original exceptions internally while returning obscured client responses.
from flask import json, render_templatefrom werkzeug.exceptions import HTTPException@app.errorhandler(500)def handle_500(e): original = getattr(e, "original_exception", None) if original: app.logger.error(f"Unhandled exception: {original}") return render_template("500.html"), 500
Rule 3: Register explicit application error handlers for HTTP codes and exception classes
Register explicit application error handlers using Flask’s @app.errorhandler decorator for integer HTTP status codes, Werkzeug exceptions, and standard Python exceptions to prevent internal runtime errors from leaking diagnostic information.
from werkzeug.exceptions import BadRequest@app.errorhandler(400)@app.errorhandler(BadRequest)def handle_bad_request(e): return "Invalid request", 400
Decorate background tasks with request context preservation
Use when
Spawning background tasks, greenlets, or worker threads from within an active Flask request context where proxies like request and session must be accessed.
Secure rules
Rule 1: Use @flask.copy_current_request_context to preserve active request context in spawned background tasks.
Decorate functions dispatched to background threads or greenlets with @flask.copy_current_request_context so that flask.request and flask.session remain safely accessible inside the sub-task execution without raising runtime errors or leaking state across threads.
Defining routes, blueprints, HTTP methods, and URL parameters in Flask applications.
Secure rules
Rule 1: Explicitly specify uppercase HTTP methods or use method-specific decorators when defining routes
When defining Flask routes using @app.route() or app.add_url_rule(), specify allowed HTTP methods as an iterable of uppercase string names or use method-specific decorators like @app.get() and @app.post().
Rule 2: Position the route decorator as the outermost decorator on view functions
When applying custom decorators to Flask view functions, ensure @app.route is always positioned as the outermost decorator so that Flask registers the wrapped function rather than the raw inner handler.
Rule 3: Use explicit URL prefixes when registering blueprints
Always configure an explicit url_prefix when registering blueprints to avoid route shadowing and namespace collisions with primary application routes or static handlers.
Use parameter converters such as int, float, or uuid in URL route path templates instead of relying on default generic string or path converters to validate parameter types before reaching view handlers.
Handling user registration and login requests to verify presented identity credentials before creating session variables or querying the database.
Secure rules
Rule 1: Validate username and password fields on the server side prior to processing authentication or registration requests.
Check all incoming username and password inputs in your route handler to ensure they are present before proceeding with database queries or establishing authentication sessions.
@bp.route('/register', methods=('GET', 'POST'))def register(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] if not username: flash('Username is required.') elif not password: flash('Password is required.')
Implement Secure WSGI Middleware and Context Isolation in Flask
Approximately 881 tokens
Use when
Developing or configuring custom WSGI middleware, reverse proxy integrations, and application context routing boundaries in Flask applications.
Secure rules
Rule 1: Isolate distinct application components at the WSGI layer using Werkzeug DispatcherMiddleware
Wrap distinct application components like public frontends and administrative backends with DispatcherMiddleware to ensure each maintains an isolated configuration and route handling within the same process.
from werkzeug.middleware.dispatcher import DispatcherMiddlewarefrom frontend_app import application as frontendfrom backend_app import application as backendapplication = DispatcherMiddleware(frontend, { '/backend': backend})
Rule 2: Unwrap thread-local proxies before passing request data across background boundaries
Call _get_current_object() on proxy objects like request, g, or current_app to retrieve the underlying instance before passing context across thread or worker boundaries.
Rule 3: Validate host header input when dynamically dispatching requests in custom WSGI middleware
Strictly validate and sanitize HTTP_HOST headers when writing custom WSGI middleware for dynamic dispatching, and return explicit WSGI exception applications like NotFound() for unmatched domain boundaries.
from werkzeug.exceptions import NotFoundclass SubdomainDispatcher: def __init__(self, domain, create_app): self.domain = domain.lower() self.create_app = create_app def get_application(self, host): host = host.split(':')[0].lower() if not host.endswith(f".{self.domain}") and host != self.domain: return NotFound() subdomain = host[:-len(self.domain)].rstrip('.') app = self.create_app(subdomain) return app if app is not None else NotFound()
Rule 4: Reassign app.wsgi_app when integrating custom WSGI middleware and align path configurations
Reassign app.wsgi_app rather than replacing the main Flask application object, and ensure path modifications in the WSGI environment align with Flask configurations like APPLICATION_ROOT.
Rule 5: Wrap application with trusted WSGI middleware such as ProxyFix to parse reverse proxy headers securely
Wrap app.wsgi_app with Werkzeug’s ProxyFix during application setup when the application is deployed behind a trusted reverse proxy. Configure only the forwarded headers required by your deployment, and set each option to the correct number of trusted proxies.
from flask import Flaskfrom werkzeug.middleware.proxy_fix import ProxyFixapp = Flask(__name__)# Use when the deployment only needs the original client IP address# and request scheme (HTTP/HTTPS).app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)# Use when the reverse proxy also forwards the original host and URL# prefix (for example, when the application is served under a custom# domain or a path such as "/app").app.wsgi_app = ProxyFix( app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1,)
Rule 6: Write defensive teardown callbacks to guarantee resource cleanup
Ensure teardown_request middleware callbacks handle missing context state gracefully and execute without raising unhandled exceptions.
@app.teardown_requestdef cleanup_resources(exception): db = getattr(g, 'db', None) if db is not None: db.close()
Respect environment variable precedence when loading configuration files
Approximately 182 tokens
Use when
When loading configuration and environment variables via load_dotenv() in Flask applications.
Secure rules
Rule 1: Rely on process environment variables for critical runtime configuration and ensure environment variable precedence is respected.
When loading configuration files using load_dotenv(), existing process variables in os.environ take priority and are not overwritten by .env or .flaskenv files. Ensure sensitive production credentials are supplied through process environment variables rather than source-controlled configuration files.
from flask.cli import load_dotenv# load_dotenv loads .env and .flaskenv without replacing existing os.environ variablesload_dotenv()
Hash User Passwords Securely
Approximately 209 tokens
Use when
When registering new users or validating user login credentials in a web application.
Secure rules
Rule 1: Hash user passwords securely before storage and verify submitted credentials against the stored hash.
Use Werkzeug’s generate_password_hash to hash user passwords before storing them in persistent storage. When authenticating users during login, use check_password_hash to securely verify the submitted password against the stored hash.
from werkzeug.security import generate_password_hash, check_password_hash# Storing hashed password during registrationhashed_password = generate_password_hash(password)db.execute("INSERT INTO user (username, password) VALUES (?, ?)", (username, hashed_password))# Validating password during loginif check_password_hash(user["password"], password): session["user_id"] = user["id"]
Implement Anti-CSRF Tokens and SameSite Cookie Protection
Approximately 374 tokens
Use when
Developing state-changing routes and configuring session cookie policies in Flask applications to prevent cross-site request forgery.
Secure rules
Rule 1: Validate anti-CSRF tokens on state-changing requests.
Since Flask does not provide built-in form validation or CSRF protection by default, developers must manually issue a token, store it in a cookie, transmit it with form data, and verify that both values match on state-modifying requests like POST.
@app.route('/user/delete', methods=['POST'])def delete_user(): token_in_cookie = request.cookies.get('csrf_token') token_in_form = request.form.get('csrf_token') if not token_in_cookie or token_in_cookie != token_in_form: abort(400, 'CSRF token verification failed') # Process user deletion safely
Rule 2: Configure SameSite cookie protection for session and response cookies.
Set SESSION_COOKIE_SAMESITE to 'Lax' or 'Strict' in the Flask configuration and pass samesite='Lax' when creating custom response cookies using response.set_cookie() to prevent browsers from sending cookies on cross-site requests.
Disable Debug Mode and Built-in Server in Production
Approximately 152 tokens
Use when
Configuring deployment settings and launching the application in a production environment.
Secure rules
Rule 1: Avoid running the built-in development server or enabling debug mode in production to prevent arbitrary code execution vulnerabilities.
Do not pass --debug or run the built-in development server in a production environment, because the interactive debugger allows arbitrary Python code execution from the browser. Instead, deploy the application using a dedicated production WSGI server.
gunicorn -w 4 'hello:app'
Restrict Jinja safe filter to trusted HTML generation
Approximately 209 tokens
Use when
Rendering trusted field widget calls within template macros where HTML structure must be preserved.
Secure rules
Rule 1: Apply the Jinja |safe filter exclusively to trusted HTML generated by WTForms field widget calls.
Isolate the |safe filter to framework-managed widget rendering functions within templates, allowing automatic escaping to protect error messages and labels instead of applying it to raw user input.
Safely handle user-provided file names and paths during uploads and downloads
Approximately 320 tokens
Use when
Handling file uploads from users or serving requested files from the filesystem.
Secure rules
Rule 1: Sanitize untrusted filenames using werkzeug.utils.secure_filename before saving them to the filesystem.
Always sanitize untrusted filenames using werkzeug.utils.secure_filename before combining them with upload paths or storing them on the filesystem to prevent directory traversal sequences from writing files outside the intended upload directory.
from werkzeug.utils import secure_filenameimport os@app.route('/upload', methods=['POST'])def upload_file(): file = request.files['file'] if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
Rule 2: Use send_from_directory instead of send_file when serving user-provided paths.
Never pass user-supplied file paths directly to send_file(), because it treats path arguments as trusted input and does not perform path traversal containment checks. Always use send_from_directory() to serve user-requested files safely from within a designated base directory.
from flask import send_from_directory@app.get('/uploads/<path:filename>')def download_file(filename): return send_from_directory('/var/www/uploads', filename)
Use Parameterized Queries to Prevent SQL Injection
Approximately 163 tokens
Use when
Writing database queries with dynamic user input in Flask applications.
Secure rules
Rule 1: Pass dynamic parameters separately from the SQL statement using placeholders.
When executing SQL statements or database commands, always pass user-supplied input as parameterized arguments using placeholders such as ? or named parameters rather than using string interpolation, formatting, or concatenation.
db = get_db()db.execute( 'INSERT INTO post (title, body, author_id) VALUES (?, ?, ?)', (title, body, g.user['id']))db.commit()
Validate form data against explicit input constraints before processing
Approximately 219 tokens
Use when
Handling incoming HTTP request form data or query parameters that require structural and type validation before executing business logic.
Secure rules
Rule 1: Define explicit input validation rules on form models and enforce validation checks before processing request payloads.
Bind HTTP request data such as request.form or request.args to form validation classes and strictly execute validation methods like form.validate() before accessing submitted fields in your application logic or database operations.
@app.route('/register', methods=['GET', 'POST'])def register(): form = RegistrationForm(request.form) if request.method == 'POST' and form.validate(): user = User(form.username.data, form.email.data, form.password.data) db_session.add(user) return redirect(url_for('login')) return render_template('register.html', form=form)
Configure Security Headers and Vary Cookie Headers on Outgoing Responses
Approximately 536 tokens
Use when
Developing response processors, custom session interfaces, or after-request hooks in Flask to harden network protocol semantics and enforce browser security controls.
Secure rules
Rule 1: Configure HTTP security headers on outgoing Flask responses or use an extension like Flask-Talisman.
Set security headers such as Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options, and X-Frame-Options on outgoing responses or via an after_request hook to direct the browser to enforce secure transport, restrict resource loading, and prevent MIME-type sniffing or clickjacking.
Rule 2: Preserve the Vary Cookie header for session-dependent responses in custom session interfaces or response processors.
Ensure the Vary: Cookie header is added to the HTTP response whenever session data is accessed or modified by checking session.accessed or session.modified, preventing CDNs and reverse proxies from serving session-tailored content to unauthenticated users.
if session.accessed: response.vary.add("Cookie")
Rely on Request Context Dispatching to Reject Malformed Host Headers
Use when
When handling incoming web requests and validating protocol headers to prevent protocol confusion and request smuggling.
Secure rules
Rule 1: Allow Flask’s request dispatch mechanism to validate incoming WSGI environment headers and automatically reject requests with invalid or non-printable host headers.
Flask’s request context dispatching checks incoming headers and returns a 400 Bad Request error when HTTP Host headers contain invalid or non-printable characters. Applications should rely on this full request dispatch mechanism to automatically reject corrupted headers and prevent request smuggling and host header injection.
Configure Trusted Proxy Settings and Remote Address Verification
Approximately 194 tokens
Use when
Configuring network trust boundaries and verifying remote IP addresses or proxy configurations in Flask applications.
Secure rules
Rule 1: Explicitly override REMOTE_ADDR when testing or simulating proxy and IP access restrictions.
When writing security tests for IP-based access controls, rate limiters, or trusted proxy rules, explicitly pass custom environ_base dictionaries or request environment variables to simulate untrusted remote IP addresses instead of relying on the default localhost REMOTE_ADDR setting.
Escape Untrusted Input When Rendering HTML Responses in Flask
Approximately 337 tokens
Use when
Rendering manual HTML strings or dynamic user-controlled data within HTML templates and attributes.
Secure rules
Rule 1: Explicitly escape untrusted user input using markupsafe.escape() or rely on Jinja’s automatic escaping.
Always explicitly escape untrusted user input using markupsafe.escape() when returning manual HTML strings or leverage Jinja’s automatic HTML escaping features by passing dynamic variables into templates rendered via render_template instead of manually interpolating strings.
Rule 2: Use the tojson filter when embedding server-side data into JavaScript or attribute contexts.
Use Jinja’s |tojson filter when embedding server-side data into HTML <script> tags or HTML data attributes to safely serialize and escape the data, and wrap attribute values containing tojson in single quotes.
Configure request size limits and form parsing thresholds
Approximately 181 tokens
Use when
Configuring Flask application settings to restrict incoming payload sizes and multipart form parsing consumption.
Secure rules
Rule 1: Set global or per-request payload limits and form parsing thresholds.
Use MAX_CONTENT_LENGTH globally or Request.max_content_length per request to restrict total request payload size. Additionally, configure MAX_FORM_MEMORY_SIZE and MAX_FORM_PARTS to control memory usage when parsing non-file multipart form fields.
Deploy Flask Applications Using a Dedicated Production WSGI Server
Approximately 180 tokens
Use when
Deploying the Flask application to a live production environment rather than running it locally for development.
Secure rules
Rule 1: Avoid running the Flask built-in development server or CLI command in production environments.
Do not deploy Flask applications using the CLI development server flask run in production because the Werkzeug development server lacks concurrency management, resource boundaries, and request-handling safety mechanisms required for live environments. Instead, use a dedicated production WSGI server such as Gunicorn.
# Production deployment command (e.g., Gunicorn):gunicorn -w 4 'myapp:create_app()'
Configure Secure Secret Keys and Rotation Mechanisms
Approximately 175 tokens
Use when
Configuring Flask application secrets and setting up secure credential management for session signing.
Secure rules
Rule 1: Load cryptographic secret keys from environment variables or secure external storage rather than hardcoding them.
Initialize SECRET_KEY using secure environment variables via os.environ to prevent hardcoded credentials from exposing session management to forgery.
Explicitly Invoke Preprocess Request During Testing
Approximately 234 tokens
Use when
Testing request-dependent code using app.test_request_context() where tests depend on security controls or identity population configured inside before_request hooks.
Secure rules
Rule 1: Explicitly invoke preprocess request within test request contexts to ensure security hooks are executed.
When testing request-dependent code using app.test_request_context(), Flask does not automatically execute request dispatching or before_request hooks. When tests depend on security controls, authentication checks, or identity population configured inside before_request hooks, developers must explicitly call app.preprocess_request() inside the request context or perform full client requests using app.test_client() to prevent bypassing security mechanisms.
Secure Flask Session Cookies and Handle Authentication State
Approximately 436 tokens
Use when
Configuring session cookie security attributes, managing session lifetimes, and clearing or updating session data during user login, logout, and state modifications.
Secure rules
Rule 1: Configure secure cookie flags and session lifetimes.
Set SESSION_COOKIE_SECURE=True, SESSION_COOKIE_HTTPONLY=True, and SESSION_COOKIE_SAMESITE='Lax' to protect cookies against interception, script theft, and cross-site attacks. Define PERMANENT_SESSION_LIFETIME to bound session validity windows.
Rule 2: Clear session data completely on authentication changes and logout.
Call session.clear() before writing authentication identifier keys during login and entirely when logging out to prevent session fixation and stale session reuse.
Rule 3: Explicitly mark session modified when updating nested mutable objects.
Set session.modified = True after updating nested mutable structures such as dictionaries or lists stored inside the session, ensuring changes are properly tracked and persisted.
from flask import session# Updating top-level dictionary keys is automatically trackedsession["user_id"] = 123# Updating nested objects requires setting session.modified = Trueif "permissions" not in session: session["permissions"] = {}session["permissions"]["is_admin"] = Truesession.modified = True