Handling Secrets#

Overview#

Two data structures are available to assist in handling secrets in web services: SecretString and SecretBytes. These are containers for holding sensitive data within your application.

Secret Parameters#

The following example demonstrates how to use SecretString to accept a secret value as a parameter in a GET request:

Example of using SecretString for a Header Parameter#
from __future__ import annotations

from dataclasses import dataclass
from secrets import compare_digest

from typing_extensions import Annotated

from litestar import get
from litestar.datastructures.secret_values import SecretString
from litestar.exceptions import NotAuthorizedException
from litestar.params import Parameter

SECRET_VALUE = "super-secret"  # An example secret value - this should be stored securely in production.


@dataclass
class Sensitive:
    value: str


@get(sync_to_thread=False)
def get_handler(secret: Annotated[SecretString, Parameter(header="x-secret")]) -> Sensitive:
    if not compare_digest(secret.get_secret(), SECRET_VALUE):
        raise NotAuthorizedException
    return Sensitive(value="sensitive data")
Example of using SecretString for a Header Parameter#
from __future__ import annotations

from dataclasses import dataclass
from secrets import compare_digest

from typing import Annotated

from litestar import get
from litestar.datastructures.secret_values import SecretString
from litestar.exceptions import NotAuthorizedException
from litestar.params import Parameter

SECRET_VALUE = "super-secret"  # An example secret value - this should be stored securely in production.


@dataclass
class Sensitive:
    value: str


@get(sync_to_thread=False)
def get_handler(secret: Annotated[SecretString, Parameter(header="x-secret")]) -> Sensitive:
    if not compare_digest(secret.get_secret(), SECRET_VALUE):
        raise NotAuthorizedException
    return Sensitive(value="sensitive data")

Note

When storing and comparing secrets, use secure practices to prevent unauthorized access. For example, use environment variables, secret management services, or encrypted databases to store secrets securely. When comparing secrets, use secrets.compare_digest() or similar to mitigate the risk of timing attacks.

Note

The headers attribute of the ASGIConnection object stores the headers exactly as they are parsed from the ASGI message. Care should be taken to ensure that these headers are not logged or otherwise exposed in a way that could compromise the security of the application.

Secret Body#

This example demonstrates use of a data structure with a SecretString field to accept a secret within the HTTP body of a request:

Example of using SecretString for a Request Body#
from dataclasses import dataclass

from litestar import post
from litestar.datastructures.secret_values import SecretString


@dataclass
class Sensitive:
    value: SecretString


@post(sync_to_thread=False)
def post_handler(data: Sensitive) -> Sensitive:
    return data

Security Considerations#

While SecretString and SecretBytes can help in securely transferring secret data through the framework, it’s vital to adopt secure practices for storing and comparing secrets within your application. Here are a few guidelines:

  • Store secrets securely, using environment variables, secret management services, or encrypted databases.

  • Always use constant time comparison functions such as secrets.compare_digest() for comparing secret values to mitigate the risk of timing attacks.

  • Implement access controls and logging to monitor and restrict who can access sensitive information.