Skip to content
Permalink
Browse files
Implement security best practices using Flask-Talisman
  • Loading branch information
Baptiste Jonglez authored and zorun committed Oct 10, 2021
1 parent 7554842 commit e626a1cbea766ce15d528e7379208435aa5cad4f
Showing with 64 additions and 2 deletions.
  1. +2 −0 CHANGELOG.rst
  2. +1 −0 conf/entrypoint.sh
  3. +15 −0 docs/configuration.rst
  4. +10 −2 docs/contributing.rst
  5. +11 −0 docs/upgrade.rst
  6. +4 −0 ihatemoney/conf-templates/ihatemoney.cfg.j2
  7. +1 −0 ihatemoney/default_settings.py
  8. +19 −0 ihatemoney/run.py
  9. +1 −0 setup.cfg
@@ -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
-----
@@ -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
@@ -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`
---------------------

@@ -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
@@ -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
++++++++++++++++++++++++++++++++++++++++++++

@@ -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
@@ -8,6 +8,7 @@
ADMIN_PASSWORD = ""
ALLOW_PUBLIC_PROJECT_CREATION = True
ACTIVATE_ADMIN_DASHBOARD = False
SESSION_COOKIE_SECURE = True
SUPPORTED_LANGUAGES = [
"de",
"el",
@@ -7,6 +7,7 @@
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)
@@ -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

0 comments on commit e626a1c

Please sign in to comment.