Skip to main content

Detect tampered signed values

When you transmit data to a client and expect it back later, you must ensure the data has not been modified. If a user changes a signed value—such as a user ID in a cookie—itsdangerous detects the mismatch and prevents the application from processing the tampered data.

The Signer class in itsdangerous.signer provides the core mechanism for this protection. It appends a cryptographic signature to a byte string using a secret key. When you attempt to retrieve the original value using unsign, the library recalculates the signature and compares it to the one provided. If the signatures do not match, it raises a BadSignature exception.

How Signer detects tampering

The Signer.sign method produces a value consisting of the original data, a separator (defaulting to .), and a base64-encoded HMAC signature. Internally, Signer.unsign performs the following steps:

  1. Locates the separator to split the payload from the signature.
  2. Decodes the signature.
  3. Uses Signer.verify_signature to check the payload against the signature using the configured secret_key.
  4. Raises itsdangerous.exc.BadSignature if the verification fails.

The BadSignature exception includes a payload attribute, which contains the data that failed the signature check. This allows you to inspect the tampered data if necessary, though it should not be trusted for application logic.

from itsdangerous import BadSignature, Signer

# Initialize a Signer with a fixed secret key
signer = Signer(b"secret-key")
original_data = b"user-123"

# Sign the data to create a signed value
signed_value = signer.sign(original_data)

# Verify the unchanged value succeeds
verified_value = signer.unsign(signed_value)
assert verified_value == original_data

# Tamper with the signed value by changing one byte
# (e.g., changing the last character of the signature)
tampered_value = signed_value[:-1] + (b"a" if signed_value[-1:] != b"a" else b"b")

try:
# Attempting to unsign tampered data raises BadSignature
signer.unsign(tampered_value)
except BadSignature as e:
# The exception provides access to the tampered payload
assert e.payload == original_data

Key rotation and verification

The Signer supports key rotation by accepting a list of keys. When unsign is called, it iterates through the keys in reverse order (newest to oldest) within verify_signature. This ensures that values signed with an older key remain valid while new signatures are generated using the most recent key. If none of the keys produce a matching signature, the verification fails.