oplik0/cherrydoor

View on GitHub
cherrydoor/secure.py

Summary

Maintainability
A
0 mins
Test Coverage
"""Set up security headers."""

__author__ = "opliko"
__license__ = "MIT"
__version__ = "0.8.b0"
__status__ = "Prototype"
from uuid import uuid4
from copy import deepcopy
from base64 import b64encode

from aiohttp.web import middleware
from aiohttp_jinja2 import get_env
import secure


def setup(app):
    """Set secure headers values.

    Parameters
    ----------
    app : aiohttp.web.Application
        The aiohttp application instance.
    """
    server = secure.Server().set("Secure")
    csp_value = (
        secure.ContentSecurityPolicy()
        .default_src("'none'")
        .base_uri("'self'")
        .connect_src("'self'")
        .frame_src("'none'")
        .img_src("'self'", "data:", "https:")
        .font_src(
            "'self'",
            "https://fonts.gstatic.com/s/",
            "https://fonts.googleapis.com/css2",
        )
        .manifest_src("'self'")
        .worker_src("'self'", "blob:")
    )
    # feature_value = secure.PermissionsPolicy().geolocation("'none'").vibrate("'none'")
    if app["config"].get("https", False):
        csp_value = csp_value.upgrade_insecure_requests()
        hsts_value = (
            secure.StrictTransportSecurity()
            .include_subdomains()
            .preload()
            .max_age(2592000)
        )
    else:
        hsts_value = None
    if app["config"].get("sentry_csp_url", False):
        csp_value = csp_value.custom_directive(
            "report-uri", app["config"]["sentry_csp_url"]
        )
    else:
        csp_value = csp_value.custom_directive("report-uri", "/csp")

    xfo_value = secure.XFrameOptions().deny()

    referrer_value = secure.ReferrerPolicy().no_referrer()

    cache_value = secure.CacheControl().no_store().must_revalidate().proxy_revalidate()

    app["secure_headers"] = secure.Secure(
        server=server,
        csp=csp_value,
        hsts=hsts_value,
        xfo=xfo_value,
        referrer=referrer_value,
        # permissions=feature_value,
        cache=cache_value,
    )


@middleware
async def set_secure_headers(request, handler):
    """Add secure headers to requests.

    Parameters
    ----------
    request : aiohttp.web.Request
        The request to set secure headers on.
    handler : function
        The middleware handler function.
    Returns
    -------
    aiohttp.web.Response
        The modified response to the request.
    """
    nonce = b64encode(uuid4().bytes).decode("utf-8")
    script_src = [
        "'self'",
        "blob:",
        #   SecurePolicies.CSP().Values.nonce(nonce),
        f"'nonce-{nonce}'",
        "https://unpkg.com",
        "'unsafe-eval'",
        "'unsafe-inline'",
    ]
    style_src = [
        "'self'",
        "'unsafe-inline'",
        "https://fonts.googleapis.com/css2",
    ]
    if request.path == "/api/v1/docs":
        script_src[2] = "'unsafe-inline'"
        style_src.append("'unsafe-inline'")
    get_env(request.app).globals["csp_nonce"] = nonce
    secure_headers = deepcopy(request.app["secure_headers"])
    secure_headers.csp.script_src(*script_src).style_src(*style_src)
    resp = await handler(request)
    secure_headers.framework.aiohttp(resp)
    return resp