Talisman: HTTP security headers for Flask
=========================================

|Build Status| |Coverage Status| |PyPI Version|

Talisman is a small Flask extension that handles setting HTTP headers
that can help protect against a few common web application security
issues.

The default configuration:

-  Forces all connects to ``https``, unless running with debug enabled.
-  Enables `HTTP Strict Transport
   Security <https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security>`__.
-  Enables HSTS preloading. If you register your application with
   `Google's HSTS preload list <https://hstspreload.appspot.com/>`__,
   Firefox and Chrome will never load your site over a non-secure
   connection.
-  Sets Flask's session cookie to ``secure``, so it will never be set if
   you application is somehow accessed via a non-secure connection.
-  Sets Flask's session cookie to ``httponly``, preventing JavaScript
   from being able to access its content. CSRF via Ajax uses a separate
   cookie and should be unaffected.
-  Sets
   `X-Frame-Options <https://developer.mozilla.org/en-US/docs/Web/HTTP/X-Frame-Options>`__
   to ``SAMEORIGIN`` to avoid
   `clickjacking <https://en.wikipedia.org/wiki/Clickjacking>`__.
-  Sets a strict `Content Security
   Policy <https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Introducing_Content_Security_Policy>`__
   of ``default-src: 'self'``. This is intended to almost completely
   prevent Cross Site Scripting (XSS) attacks. This is probably the only
   setting that you should reasonably change. See the `section
   below <#content-security-policy>`__ on configuring this.

In addition to Talisman, you **should always use a cross-site request
forgery (CSRF) library**. I highly recommend
`Flask-SeaSurf <https://flask-seasurf.readthedocs.org/en/latest/>`__,
which is based on Django's excellent library.

Installation & Basic Usage
--------------------------

Install via `pip <https://pypi.python.org/pypi/pip>`__:

::

    pip install talisman


.. code:: python

    from flask import Flask
    from talisman import Talisman

    app = Flask(__name__)
    Talisman(app)


There is also a full `Example App <https://github.com/GoogleCloudPlatform/flask-talisman/blob/master/example_app>`__.

Options
-------

-  ``force_https``, default ``True``, forces all non-debug connects to
   ``https``.
-  ``force_https_permanent``, default ``False``, uses ``301`` instead of
   ``302`` for ``https`` redirects.
-  ``frame_options``, default ``SAMEORIGIN``, can be ``SAMEORIGIN``,
   ``DENY``, or ``ALLOWFROM``.
-  ``frame_options_allow_from``, default ``None``, a string indicating
   the domains that arrow allowed to embed the site via iframe.
-  ``strict_transport_security``, default ``True``, whether to send HSTS
   headers.
-  ``strict_transport_security_max_age``, default ``ONE_YEAR_IN_SECS``,
   length of time the browser will respect the HSTS header.
-  ``strict_transport_security_include_subdomains``, default ``True``,
   whether subdomains should also use HSTS.
-  ``content_security_policy``, default ``default-src: 'self'``, see the
   `section below <#content-security-policy>`__.
-  ``session_cookie_secure``, default ``True``, set the session cookie
   to ``secure``, preventing it from being sent over plain ``http``.
-  ``session_cookie_http_only``, default ``True``, set the session
   cookie to ``httponly``, preventing it from being read by JavaScript.

Per-view options
~~~~~~~~~~~~~~~~

Sometimes you want to change the policy for a specific view. The
``frame_options``, ``frame_options_allow_from``, and
``content_security_policy`` options can be changed on a per-view basis.

.. code:: python

    from flask import Flask
    from talisman import Talisman, ALLOW_FROM

    app = Flask(__name__)
    talisman = Talisman(app)

    @app.route('/normal')
    def normal():
        return 'Normal'

    @app.route('/embeddable')
    @talisman(frame_options=ALLOW_FROM, frame_options_allow_from='*')
    def embeddable():
        return 'Embeddable'

Content Security Policy
-----------------------

The default content security policy is extremely strict, and will
prevent loading any resources that are not in the same domain as the
application.

A slightly more permissive policy is available at
``talisman.GOOGLE_CSP_POLICY``, which allows loading Google-hosted JS
libraries, fonts, and embeding media from YouTube and Maps.

You can and should create your own policy to suit your site's needs.
Here's a few examples adapted from
`MDN <https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Using_Content_Security_Policy>`__:

Example 1
~~~~~~~~~

This is the default policy. A web site administrator wants all content
to come from the site's own origin (this excludes subdomains.)

.. code:: python

    csp = {
        'default-src': '\'self\''
    }

Example 2
~~~~~~~~~

A web site administrator wants to allow content from a trusted domain
and all its subdomains (it doesn't have to be the same domain that the
CSP is set on.)

.. code:: python

    csp = {
        'default-src': [
            '\'self\'',
            '*.trusted.com'
        ]
    }

Example 3
~~~~~~~~~

A web site administrator wants to allow users of a web application to
include images from any origin in their own content, but to restrict
audio or video media to trusted providers, and all scripts only to a
specific server that hosts trusted code.

.. code:: python

    csp = {
        'default-src': '\'self\'',
        'image-src': '*',
        'media-src': [
            'media1.com',
            'media2.com',
        ],
        'script-src': 'userscripts.example.com'
    }

Here, by default, content is only permitted from the document's origin,
with the following exceptions:

-  Images may loaded from anywhere (note the ``*`` wildcard).
-  Media is only allowed from media1.com and media2.com (and not from
   subdomains of those sites).
-  Executable script is only allowed from userscripts.example.com.

Example 4
~~~~~~~~~

A web site administrator for an online banking site wants to ensure that
all its content is loaded using SSL, in order to prevent attackers from
eavesdropping on requests.

.. code:: python

    csp = {
        'default-src': 'https://onlinebanking.jumbobank.com'
    }

The server only permits access to documents being loaded specifically
over HTTPS through the single origin onlinebanking.jumbobank.com.

Example 5
~~~~~~~~~

A web site administrator of a web mail site wants to allow HTML in
email, as well as images loaded from anywhere, but not JavaScript or
other potentially dangerous content.

.. code:: python

    csp = {
        'default-src': [
            '\'self\'',
            '*.mailsite.com',
        ],
        'img-src': '*'
    }

Note that this example doesn't specify a ``script-src``; with the
example CSP, this site uses the setting specified by the ``default-src``
directive, which means that scripts can be loaded only from the
originating server.

Disclaimer
----------

This is not an official Google product, experimental or otherwise.

There is no silver bullet for web application security. Talisman can
help, but security is more than just setting a few headers. Any
public-facing web application should have a comprehensive approach to
security.


Contributing changes
--------------------

-  See `CONTRIBUTING.md`_

Licensing
---------

- Apache 2.0 - See `LICENSE`_

.. _LICENSE: https://github.com/GoogleCloudPlatform/talisman/blob/master/LICENSE
.. _CONTRIBUTING.md: https://github.com/GoogleCloudPlatform/talisman/blob/master/CONTRIBUTING.md
.. |Build Status| image:: https://travis-ci.org/GoogleCloudPlatform/talisman.svg
   :target: https://travis-ci.org/GoogleCloudPlatform/talisman
.. |PyPI Version| image:: https://img.shields.io/pypi/v/talisman.svg
   :target: https://pypi.python.org/pypi/talisman
