landregistry-security-headers


Namelandregistry-security-headers JSON
Version 0.9.9 PyPI version JSON
download
home_pageNone
SummaryStandardised exception security related HTTP headers for HMLR Flask applications
upload_time2024-10-31 13:54:47
maintainerNone
docs_urlNone
authorIan Harvey
requires_python>=3.9
licenseMIT
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Security Headers for Flask Web Applications

Convienience Flask extension for setting security headers. See below for the full list.

**Note** This extension will not set headers such as `Content-Type`, `Cache-Control` or `Clear-Site-Data`. You'll need to set those yourself when you need them.

This package depends on:

- Flask

## Usage

Instantiate it like a normal flask extension:

```python
from landregistry.security_headers import SecurityHeaders, UIDefaultHeaders
from <somewhere> import app

# ...

headers = SecurityHeaders()
headers.init_app(app, UIDefaultHeaders)
```

Three default configurations are provided:

- `UIDefaultHeaders` - a set of headers suited for a web front-end.
- `APIDefaultHeaders` - a set of headers suited for a REST API.
- `EmptyDefaultHeaders` - no defaults.

### Updating an existing UI

If you have a skeleton-based UI application that pre-dates this extension's inclusion, you can easily update it to use this package as follows:

- Add `landregistry-security-headers` to your requirements.
- Remove imports and references to `security_headers` and `content_security_policy` inbuilt packages
- Add imports and initialisation of the new extension.

```diff
  # ...
  from landregistry.healthchecks import HealthChecks
+ from landregistry.security_headers import SecurityHeaders, UIDefaultHeaders

  from server import config
  # ...
- from server.custom_extensions.content_security_policy.main import ContentSecurityPolicy
  # ...
- from server.custom_extensions.security_headers.main import SecurityHeaders
  from server.exceptions import application_error_renderer, http_error_renderer, unhandled_error_renderer

  # Create empty extension objects here
  # ...
- security_headers = SecurityHeaders()
  # ...
- content_security_policy = ContentSecurityPolicy()
  # ...
  health = HealthChecks()
+ headers = SecurityHeaders()

  def register_extensions(app):
      """Adds any previously created extension objects into the app, and does any further setup they need."""
      enhanced_logging.init_app(app)
-     security_headers.init_app(app)
      # ...
-     content_security_policy.init_app(app)
      # ...
      health.init_app(app)
      health.add_dependencies(DEPENDENCIES)
+     headers.init_app(app, UIDefaultHeaders)
      # ...
```

You can then remove the `content_security_policy` and `security_headers` folders from the `custom_extensions` folder.

If you have customised these, see below on configuring the new extension.

### Default values

| Header                            | UI Default                      | API Default      |
| --------------------------------- | ------------------------------- | ---------------- |
| X-Frame-Options                   | DENY                            |                  |
| Strict-Transport-Security         | max-age=31536000                | max-age=31536000 |
| X-Content-Type-Options            | nosniff                         |                  |
| Report-To                         | (see below)                     |                  |
| Content-Security-Policy           | (see below)                     | (see below)      |
| X-Content-Security-Policy         | Same as Content-Security-Policy | Same as Content-Security-Policy |
| X-XSS-Protection                  | 1; mode=block                   |                  |
| Referrer-Policy                   | strict-origin-when-cross-origin |                  |
| Permissions-Policy                | (see below)                     |                  |
| Cross-Origin-Embedded-Policy      | require-corp                    |                  |
| Cross-Origin-Opener-Policy        | same-origin                     |                  |
| Cross-Origin-Reosurce-Policy      | same-origin                     |                  |
| X-Permitted-Cross-Domain-Policies | none                            |                  |


**Default UI CSP Headers**

```
Content-Security-Policy: default-src 'self';script-src 'self' https://*.googletagmanager.com 'sha256-+6WnXIl4mbFTCARd8N3COQmT3bJJmo32N8q8ZSQAIcU=' 'sha256-G29/qSW/JHHANtFhlrZVDZW1HOkCDRc78ggbqwwIJ2g=' 'sha256-s7w4Nk/Xk6wc1nlA5PiGroLjvaV+XU1ddIlx89jmBjc=';connect-src 'self' https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com;img-src 'self' https://*.google-analytics.com https://*.googletagmanager.com;font-src 'self' data:;style-src 'self';object-src 'none';block-all-mixed-content;report-uri /content-security-policy-report/;report-to default;

Report-To: {"group":"default","max_age":10886400,"endpoints":[{"url": "<schema>://<host>/content-security-policy-report/"}]}
```

**Default API CSP Headers**

```
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
```

**Default UI Permissions Policy**

```
Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-management=(), vertical-scroll=()
```

### Overriding defaults

Provide entries in your application configuration (e.g. `config.py`):

- `X_FRAME_OPTIONS`
- `STRICT_TRANSPORT_SECURITY`
- `X_CONTENT_TYPE_OPTIONS`
- `REPORT_TO`
- `CONTENT_SECURITY_POLICY`
- `X_XSS_PROTECTION`
- `REFERRER_POLICY`
- `PERMISSIONS_POLICY`
- `CROSS_ORIGIN_EMBEDDER_POLICY`
- `CROSS_ORIGIN_OPENER_POLICY`
- `CROSS_ORIGIN_RESOURCE_POLICY`
- `X_PERMITTED_CROSS_DOMAIN_POLICIES`

Whatever you set to the variable will be applied to the corresponding header

### CSP Customisation

To customise parts of the Content Security Policy

- `SECURITY_CSP_SCRIPT_HASHES` - overrides the default script hashes. Space delimited. Default is <something>
- `SECURITY_CSP_SCRIPT_SOURCES` - overrides the default script-src. Default is "https://*.googletagmanager.com"
- `SECURITY_CSP_STYLE_SOURCES` - overrides the style-src. Default is "'self'".
- `REPORT_TO_URI` - overrides the default URI in the REPORT_TO header.

If overriding SCRIPT_HASHES/SOURCES and want to keep defaults, you can get the default values from `DEFAULT_SCRIPT_SOURCES`, `DEFAULT_SCRIPT_HASHES` or `DEFAULT_STYLE_SOURCE`

Some placeholders may be included in the CSP:

- `{script_src}` - replaced with `SECURITY_CSP_SCRIPT_SOURCES`
- `{script_hashes}` - replaced with `SECURITY_CSP_SCRIPT_HASHES`
- `{style_src}` - replaced with `SECURITY_CSP_STYLE_SOURCES`
- `{report_uri}` - replaced with the relative URL of the CSP reporting endpoint

And in the Report-To header:

- `{full_report_uri}` - replaced with the full URL of the CSP reporting endpoint

### CSP Violation Report Logging

To change how the CSP violation report endpoint logs reports, provide a config entry for `CONTENT_SECURITY_POLICY_REPORT_LEVEL`.

Valid values are `ERROR`, `WARNING`, `INFO`, and `DEBUG`, corresponding to to the log level that will be used to log the report. A value of `NONE` may be provided to stop logging altogether. **Be sure you really want to do this**.

The default logging level is `ERROR` if no level is specified.

### Per-endpoint overrides

Use the `headers` object as a decorator to override headers:

```python
@test_blueprint.route("", methods=["GET"])
@headers(X_CONTENT_TYPE_OPTIONS=None, X_XSS_PROTECTION="1")
def get_test():
    return make_response("Test", 200)
```

Note that this will cause your app to fail (to even start) if you specify headers the extension isn't expecting.

Note this extension won't override headers that you've set in a response. For example, in this case:

```python
@test_blueprint.route("/example")
def example():
    return Response("", 200, headers={'X-Content-Type-Options': 'Setting some nonsense here'})
```

...the `X-Content-Type-Options` header will be set to `Setting some nonsense here`, regardless of extension configuration. Setting a header to `None` this way *will not* remove the header, but will set it to the string literal `None`. Removing headers requires the decorator approach.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "landregistry-security-headers",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": null,
    "author": "Ian Harvey",
    "author_email": "ian.harvey@landregistry.gov.uk",
    "download_url": "https://files.pythonhosted.org/packages/8c/fe/4ff77ad17602afd0d06d2946945eee5b57e363fb7c980f4fe205f010b3a4/landregistry_security_headers-0.9.9.tar.gz",
    "platform": null,
    "description": "# Security Headers for Flask Web Applications\n\nConvienience Flask extension for setting security headers. See below for the full list.\n\n**Note** This extension will not set headers such as `Content-Type`, `Cache-Control` or `Clear-Site-Data`. You'll need to set those yourself when you need them.\n\nThis package depends on:\n\n- Flask\n\n## Usage\n\nInstantiate it like a normal flask extension:\n\n```python\nfrom landregistry.security_headers import SecurityHeaders, UIDefaultHeaders\nfrom <somewhere> import app\n\n# ...\n\nheaders = SecurityHeaders()\nheaders.init_app(app, UIDefaultHeaders)\n```\n\nThree default configurations are provided:\n\n- `UIDefaultHeaders` - a set of headers suited for a web front-end.\n- `APIDefaultHeaders` - a set of headers suited for a REST API.\n- `EmptyDefaultHeaders` - no defaults.\n\n### Updating an existing UI\n\nIf you have a skeleton-based UI application that pre-dates this extension's inclusion, you can easily update it to use this package as follows:\n\n- Add `landregistry-security-headers` to your requirements.\n- Remove imports and references to `security_headers` and `content_security_policy` inbuilt packages\n- Add imports and initialisation of the new extension.\n\n```diff\n  # ...\n  from landregistry.healthchecks import HealthChecks\n+ from landregistry.security_headers import SecurityHeaders, UIDefaultHeaders\n\n  from server import config\n  # ...\n- from server.custom_extensions.content_security_policy.main import ContentSecurityPolicy\n  # ...\n- from server.custom_extensions.security_headers.main import SecurityHeaders\n  from server.exceptions import application_error_renderer, http_error_renderer, unhandled_error_renderer\n\n  # Create empty extension objects here\n  # ...\n- security_headers = SecurityHeaders()\n  # ...\n- content_security_policy = ContentSecurityPolicy()\n  # ...\n  health = HealthChecks()\n+ headers = SecurityHeaders()\n\n  def register_extensions(app):\n      \"\"\"Adds any previously created extension objects into the app, and does any further setup they need.\"\"\"\n      enhanced_logging.init_app(app)\n-     security_headers.init_app(app)\n      # ...\n-     content_security_policy.init_app(app)\n      # ...\n      health.init_app(app)\n      health.add_dependencies(DEPENDENCIES)\n+     headers.init_app(app, UIDefaultHeaders)\n      # ...\n```\n\nYou can then remove the `content_security_policy` and `security_headers` folders from the `custom_extensions` folder.\n\nIf you have customised these, see below on configuring the new extension.\n\n### Default values\n\n| Header                            | UI Default                      | API Default      |\n| --------------------------------- | ------------------------------- | ---------------- |\n| X-Frame-Options                   | DENY                            |                  |\n| Strict-Transport-Security         | max-age=31536000                | max-age=31536000 |\n| X-Content-Type-Options            | nosniff                         |                  |\n| Report-To                         | (see below)                     |                  |\n| Content-Security-Policy           | (see below)                     | (see below)      |\n| X-Content-Security-Policy         | Same as Content-Security-Policy | Same as Content-Security-Policy |\n| X-XSS-Protection                  | 1; mode=block                   |                  |\n| Referrer-Policy                   | strict-origin-when-cross-origin |                  |\n| Permissions-Policy                | (see below)                     |                  |\n| Cross-Origin-Embedded-Policy      | require-corp                    |                  |\n| Cross-Origin-Opener-Policy        | same-origin                     |                  |\n| Cross-Origin-Reosurce-Policy      | same-origin                     |                  |\n| X-Permitted-Cross-Domain-Policies | none                            |                  |\n\n\n**Default UI CSP Headers**\n\n```\nContent-Security-Policy: default-src 'self';script-src 'self' https://*.googletagmanager.com 'sha256-+6WnXIl4mbFTCARd8N3COQmT3bJJmo32N8q8ZSQAIcU=' 'sha256-G29/qSW/JHHANtFhlrZVDZW1HOkCDRc78ggbqwwIJ2g=' 'sha256-s7w4Nk/Xk6wc1nlA5PiGroLjvaV+XU1ddIlx89jmBjc=';connect-src 'self' https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com;img-src 'self' https://*.google-analytics.com https://*.googletagmanager.com;font-src 'self' data:;style-src 'self';object-src 'none';block-all-mixed-content;report-uri /content-security-policy-report/;report-to default;\n\nReport-To: {\"group\":\"default\",\"max_age\":10886400,\"endpoints\":[{\"url\": \"<schema>://<host>/content-security-policy-report/\"}]}\n```\n\n**Default API CSP Headers**\n\n```\nContent-Security-Policy: default-src 'none'; frame-ancestors 'none'\n```\n\n**Default UI Permissions Policy**\n\n```\nPermissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), speaker-selection=(), conversion-measurement=(), focus-without-user-activation=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), sync-script=(), trust-token-redemption=(), window-management=(), vertical-scroll=()\n```\n\n### Overriding defaults\n\nProvide entries in your application configuration (e.g. `config.py`):\n\n- `X_FRAME_OPTIONS`\n- `STRICT_TRANSPORT_SECURITY`\n- `X_CONTENT_TYPE_OPTIONS`\n- `REPORT_TO`\n- `CONTENT_SECURITY_POLICY`\n- `X_XSS_PROTECTION`\n- `REFERRER_POLICY`\n- `PERMISSIONS_POLICY`\n- `CROSS_ORIGIN_EMBEDDER_POLICY`\n- `CROSS_ORIGIN_OPENER_POLICY`\n- `CROSS_ORIGIN_RESOURCE_POLICY`\n- `X_PERMITTED_CROSS_DOMAIN_POLICIES`\n\nWhatever you set to the variable will be applied to the corresponding header\n\n### CSP Customisation\n\nTo customise parts of the Content Security Policy\n\n- `SECURITY_CSP_SCRIPT_HASHES` - overrides the default script hashes. Space delimited. Default is <something>\n- `SECURITY_CSP_SCRIPT_SOURCES` - overrides the default script-src. Default is \"https://*.googletagmanager.com\"\n- `SECURITY_CSP_STYLE_SOURCES` - overrides the style-src. Default is \"'self'\".\n- `REPORT_TO_URI` - overrides the default URI in the REPORT_TO header.\n\nIf overriding SCRIPT_HASHES/SOURCES and want to keep defaults, you can get the default values from `DEFAULT_SCRIPT_SOURCES`, `DEFAULT_SCRIPT_HASHES` or `DEFAULT_STYLE_SOURCE`\n\nSome placeholders may be included in the CSP:\n\n- `{script_src}` - replaced with `SECURITY_CSP_SCRIPT_SOURCES`\n- `{script_hashes}` - replaced with `SECURITY_CSP_SCRIPT_HASHES`\n- `{style_src}` - replaced with `SECURITY_CSP_STYLE_SOURCES`\n- `{report_uri}` - replaced with the relative URL of the CSP reporting endpoint\n\nAnd in the Report-To header:\n\n- `{full_report_uri}` - replaced with the full URL of the CSP reporting endpoint\n\n### CSP Violation Report Logging\n\nTo change how the CSP violation report endpoint logs reports, provide a config entry for `CONTENT_SECURITY_POLICY_REPORT_LEVEL`.\n\nValid values are `ERROR`, `WARNING`, `INFO`, and `DEBUG`, corresponding to to the log level that will be used to log the report. A value of `NONE` may be provided to stop logging altogether. **Be sure you really want to do this**.\n\nThe default logging level is `ERROR` if no level is specified.\n\n### Per-endpoint overrides\n\nUse the `headers` object as a decorator to override headers:\n\n```python\n@test_blueprint.route(\"\", methods=[\"GET\"])\n@headers(X_CONTENT_TYPE_OPTIONS=None, X_XSS_PROTECTION=\"1\")\ndef get_test():\n    return make_response(\"Test\", 200)\n```\n\nNote that this will cause your app to fail (to even start) if you specify headers the extension isn't expecting.\n\nNote this extension won't override headers that you've set in a response. For example, in this case:\n\n```python\n@test_blueprint.route(\"/example\")\ndef example():\n    return Response(\"\", 200, headers={'X-Content-Type-Options': 'Setting some nonsense here'})\n```\n\n...the `X-Content-Type-Options` header will be set to `Setting some nonsense here`, regardless of extension configuration. Setting a header to `None` this way *will not* remove the header, but will set it to the string literal `None`. Removing headers requires the decorator approach.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Standardised exception security related HTTP headers for HMLR Flask applications",
    "version": "0.9.9",
    "project_urls": null,
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "0aca8412cc4ad52d3c5981a31dfe51dd09cace410444010ef8d532e205ac0c0b",
                "md5": "2453454d51ea8c16d4aacc2ef8f26aaf",
                "sha256": "288a8396cbda70b40799cc117f790308c8a11b75df56b39089dd9c2af1676fe7"
            },
            "downloads": -1,
            "filename": "landregistry_security_headers-0.9.9-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2453454d51ea8c16d4aacc2ef8f26aaf",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 10531,
            "upload_time": "2024-10-31T13:54:44",
            "upload_time_iso_8601": "2024-10-31T13:54:44.863271Z",
            "url": "https://files.pythonhosted.org/packages/0a/ca/8412cc4ad52d3c5981a31dfe51dd09cace410444010ef8d532e205ac0c0b/landregistry_security_headers-0.9.9-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8cfe4ff77ad17602afd0d06d2946945eee5b57e363fb7c980f4fe205f010b3a4",
                "md5": "b046552ca9d0bd9ca3a24f33a6f62294",
                "sha256": "0affe7f7997856b679674bec8f8544ed711bd0bb6db77c472b607cb8168ff8fc"
            },
            "downloads": -1,
            "filename": "landregistry_security_headers-0.9.9.tar.gz",
            "has_sig": false,
            "md5_digest": "b046552ca9d0bd9ca3a24f33a6f62294",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 10408,
            "upload_time": "2024-10-31T13:54:47",
            "upload_time_iso_8601": "2024-10-31T13:54:47.283598Z",
            "url": "https://files.pythonhosted.org/packages/8c/fe/4ff77ad17602afd0d06d2946945eee5b57e363fb7c980f4fe205f010b3a4/landregistry_security_headers-0.9.9.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-10-31 13:54:47",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "landregistry-security-headers"
}
        
Elapsed time: 0.35097s