Implement security best practices using Flask-Talisman

This commit is contained in:
Baptiste Jonglez 2021-10-10 17:45:56 +02:00 committed by zorun
parent 7554842b1f
commit e626a1cbea
9 changed files with 64 additions and 2 deletions

View file

@ -9,6 +9,7 @@ This document describes changes between each past release.
Breaking changes
----------------
- Enable session cookie security by default (#845)
- Drop support for Python 2 (#483)
- Drop support for Python 3.5 (#571)
- Drop support for MySQL (#743)
@ -25,6 +26,7 @@ Security
- Add CSRF validation on destructive actions (#796)
- Ask for private code to delete project or project history (#796)
- Add headers to mitigate Clickjacking, XSS, and other attacks: `X-Frame-Options`, `X-XSS-Protection`, `X-Content-Type-Options`, `Content-Security-Policy`, `Referrer-Policy` (#845)
Added
-----

View file

@ -21,6 +21,7 @@ ADMIN_PASSWORD = '$ADMIN_PASSWORD'
ALLOW_PUBLIC_PROJECT_CREATION = $ALLOW_PUBLIC_PROJECT_CREATION
ACTIVATE_ADMIN_DASHBOARD = $ACTIVATE_ADMIN_DASHBOARD
BABEL_DEFAULT_TIMEZONE = "$BABEL_DEFAULT_TIMEZONE"
SESSION_COOKIE_SECURE = $SESSION_COOKIE_SECURE
EOF
# Start gunicorn without forking

View file

@ -64,6 +64,21 @@ of the secret key could easily access any project and bypass the private code ve
- **Production value:** `ihatemoney conf-example ihatemoney.cfg` sets it to
something random, which is good.
`SESSION_COOKIE_SECURE`
-----------------------
A boolean that controls whether the session cookie will be marked "secure".
If this is the case, browsers will refuse to send the session cookie over plain HTTP.
- **Default value:** ``True``
- **Production value:** ``True`` if you run your service over HTTPS, ``False`` if you run
your service over plain HTTP.
Note: this setting is actually interpreted by Flask, see the
`Flask documentation`_ for details.
.. _Flask documentation: https://flask.palletsprojects.com/en/2.0.x/config/#SESSION_COOKIE_SECURE
`MAIL_DEFAULT_SENDER`
---------------------

View file

@ -104,12 +104,20 @@ You can create a ``settings.cfg`` file, with the following content::
DEBUG = True
SQLACHEMY_ECHO = DEBUG
You can also set the `TESTING` flag to `True` so no mails are sent
(and no exception is raised) while you're on development mode.
Then before running the application, declare its path with ::
export IHATEMONEY_SETTINGS_FILE_PATH="$(pwd)/settings.cfg"
You can also set the ``TESTING`` flag to ``True`` so no mails are sent
(and no exception is raised) while you're on development mode.
In some cases, you may need to disable secure cookies by setting
``SESSION_COOKIE_SECURE`` to ``False``. This is needed if you
access your dev server over the network: with the default value
of ``SESSION_COOKIE_SECURE``, the browser will refuse to send
the session cookie over insecure HTTP, so many features of Ihatemoney
won't work (project login, language change, etc).
.. _contributing-developer:
Contributing as a developer

View file

@ -65,6 +65,17 @@ If so, pick the ``pip`` commands to use in the relevant section(s) of
Then follow :ref:`general-procedure` from step 1. in order to complete the update.
Disable session cookie security if running over plain HTTP
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.. note:: If you are running Ihatemoney over HTTPS, no special action is required.
Session cookies are now marked "secure" by default to increase security.
If you run Ihatemoney over plain HTTP, you need to explicitly disable this security
feature by setting ``SESSION_COOKIE_SECURE`` to ``False``, see :ref:`configuration`.
Switch to MariaDB >= 10.3.2 instead of MySQL
++++++++++++++++++++++++++++++++++++++++++++

View file

@ -38,3 +38,7 @@ ACTIVATE_ADMIN_DASHBOARD = False
# You can change the timezone used to display time. By default it will be
#derived from the server OS.
#BABEL_DEFAULT_TIMEZONE = "Europe/Paris"
# Enable secure cookies. Requires HTTPS. Disable if you run your ihatemoney
# service over plain HTTP.
SESSION_COOKIE_SECURE = True

View file

@ -8,6 +8,7 @@ ACTIVATE_DEMO_PROJECT = True
ADMIN_PASSWORD = ""
ALLOW_PUBLIC_PROJECT_CREATION = True
ACTIVATE_ADMIN_DASHBOARD = False
SESSION_COOKIE_SECURE = True
SUPPORTED_LANGUAGES = [
"de",
"el",

View file

@ -7,6 +7,7 @@ from flask import Flask, g, render_template, request, session
from flask_babel import Babel, format_currency
from flask_mail import Mail
from flask_migrate import Migrate, stamp, upgrade
from flask_talisman import Talisman
from jinja2 import pass_context
from markupsafe import Markup
import pytz
@ -126,6 +127,24 @@ def create_app(
instance_relative_config=instance_relative_config,
)
# If we need to load external JS/CSS/image resources, it needs to be added here, see
# https://github.com/wntrblm/flask-talisman#content-security-policy
csp = {
"default-src": ["'self'"],
# We have several inline javascript scripts :(
"script-src": ["'self'", "'unsafe-inline'"],
"object-src": "'none'",
}
Talisman(
app,
# Forcing HTTPS is the job of a reverse proxy
force_https=False,
# This is handled separately through the SESSION_COOKIE_SECURE Flask setting
session_cookie_secure=False,
content_security_policy=csp,
)
# If a configuration object is passed, use it. Otherwise try to find one.
load_configuration(app, configuration)
app.wsgi_app = PrefixedWSGI(app)

View file

@ -33,6 +33,7 @@ install_requires =
Flask-Migrate>=2.5.3,<4 # Not following semantic versioning (e.g. https://github.com/miguelgrinberg/flask-migrate/commit/1af28ba273de6c88544623b8dc02dd539340294b)
Flask-RESTful>=0.3.9,<1
Flask-SQLAlchemy>=2.4,<3
Flask-Talisman>=0.8,<1
Flask-WTF>=0.14.3,<1
WTForms>=2.3.1,<2.4
Flask>=2,<3