Skip to content

MashSoftware/flask-bootstrap-ui

Repository files navigation

Flask Bootstrap UI Template

This template repository contains a Flask app using the Bootstrap UI framework. The app is structured based on best practices and experience gained through previous implementations. By using a template repository you can generate a new repository with the same directory structure and files to get a new project started quicker.

Prerequisites

Required

  • Python 3.7.x or higher

Optional

  • Redis 4.0.x or higher (for rate limiting, otherwise in-memory storage is used)

Getting started

Create venv and install requirements

python3 -m venv venv
source venv/bin/activate
pip3 install -r requirements.txt ; pip3 install -r requirements_dev.txt

Run app

flask run

Testing

Run the test suite

python -m pytest --cov=app --cov-report=term-missing --cov-branch

Features

This template app uses a number of packages to provide the following features with sensible defaults. Please refer to the specific packages documentation for more details.

Asset compression

Custom CSS and JavaScript files are merged and compressed using Flask Assets and Webassets. This takes all *.css files in app/static/src/css and all *.js files in app/static/src/js and outputs a single compressed file to both app/static/dist/css and app/static/dist/js respectively.

CSS is minified using CSSMin and JavaScript is minified using JSMin. This removes all whitespace characters, comments and line breaks to reduce the size of the source code, making its transmission over a network more efficient.

Cache busting

Merged and compressed assets are browser cache busted on update by modifying their URL with their MD5 hash using Flask Assets and Webassets. The MD5 hash is appended to the file name, for example custom-d41d8cd9.css instead of a query string, to support certain older browsers and proxies that ignore the querystring in their caching behaviour.

Forms

Uses Flask WTF and WTForms to define and validate forms. Forms are rendered in your template using regular Jinja syntax with the relevent Bootstrap classes applied:

<form action="" method="post" novalidate>
    {{ form.csrf_token }}
    <div class="mb-3">
        {{ form.email_address.label(class="form-label") }}
        {{ form.email_address(class="form-control", type="email", spellcheck="false", autocomplete="email") }}
        <div id="emailHelp" class="form-text">{{ form.email_address.description }}</div>
    </div>       
</form>

CSRF protection

Uses Flask WTF to enable Cross Site Request Forgery protection per form and for the whole app.

Security headers

Uses Flask Talisman to set HTTP headers that can help protect against a few common web application security issues.

  • Forces all connects to https, unless running with debug enabled.
  • Enables HTTP Strict Transport Security.
  • Sets Flask's session cookie to secure, so it will never be set if your 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.
  • Sets X-Frame-Options to SAMEORIGIN to avoid clickjacking.
  • Sets X-XSS-Protection to enable a cross site scripting filter for IE and Safari (note Chrome has removed this and Firefox never supported it).
  • Sets X-Content-Type-Options to prevent content type sniffing.
  • Sets a strict Referrer-Policy of strict-origin-when-cross-origin that governs which referrer information should be included with requests made.

Content Security Policy

A strict default Content Security Policy (CSP) is set using Flask Talisman to mitigate Cross Site Scripting (XSS) and packet sniffing attacks. This prevents loading any resources that are not in the same domain as the application, with the following exceptions:

  • style-src and script-src can be loaded from the Bootstrap CDN over HTTPS only. These resources also use Subresource Integrity (SRI) to specify a base64-encoded sha384 cryptographic hash for additional security.
  • To enable Bootstrap components that include embedded SVGs, an additional policy of img-src data: 'self' has also been added.

Response compression

Uses Flask Compress to compress response data. This inspects the Accept-Encoding request header, compresses using either gzip, deflate or brotli algorithms and sets the Content-Encoding response header. HTML, CSS, XML, JSON and JavaScript MIME types will all be compressed.

Rate limiting

Uses Flask Limiter to set request rate limits on routes. The default rate limit is 2 requests per second and 60 requests per minite (whichever is hit first) based on the clients remote IP address. Every time a request exceeds the rate limit, the view function will not get called and instead a HTTP 429 status will be returned. If you're implementing user authentication using Flask Login you should also use a key_func to identify users on routes that require authentication, for example:

@login_required
@limiter.limit("2 per second", key_func=lambda: current_user.id)

This fixes the issue of rate limiting multiple users behind a single IP NAT or proxy, since the request is identified using a different unique value for each user.

Rate limit storage can be backed by Redis using the RATELIMIT_STORAGE_URL config value in config.py, or fall back to in-memory if not present. Rate limit information will also be added to various response headers.

Logging