Django gives you secure defaults. Flask gives you a router, and everything else is a decision you have to make on purpose.
Debug mode
The Werkzeug debugger renders an interactive Python console on the error page. Anyone who triggers an exception gets code execution as your application user. It's PIN-protected in recent versions, and the PIN is derived from values that are frequently obtainable.
Serve through gunicorn or uwsgi in production and never pass debug=True. Confirm FLASK_DEBUG isn't set in the deployed environment.
The secret key
Flask sessions are signed with this key and stored in a cookie. A known key means forgeable sessions, including an admin one.
Two specifics worth knowing. A hardcoded development key that ships is the common failure. And Flask's session cookie is signed, not encrypted. Anyone holding it can read the contents, so nothing sensitive goes in the session.
CSRF
There is none by default. Add Flask-WTF:
If parts of your app are a token-authenticated API, exempt those routes rather than disabling protection globally.
SQL
SQLAlchemy's query interface parameterises. text() with an f-string doesn't:
Templates
Jinja2 autoescaping is on for .html, .htm, .xml and .xhtml, and off for other extensions. A template named email.txt rendering user content doesn't escape.
|safe and Markup() disable it explicitly. Treat both as review triggers.
Never build a template from user input. render_template_string with user content is server-side template injection, which is code execution rather than XSS.
Ownership
@login_required confirms someone is logged in and nothing more. This was the highest-impact pattern in our 24-app study, and Flask's minimalism makes it easier than usual to overlook.
The rest
Flask-Talisman for security headers and HTTPS enforcement. Flask-Limiter for rate limiting, particularly on login. Set SESSION_COOKIE_SECURE, SESSION_COOKIE_HTTPONLY and SESSION_COOKIE_SAMESITE. Validate uploads and store them outside the static directory.
bandit is a reasonable free Python analyser and catches the raw SQL and template injection cases.