HIGH integer overflowdjangohmac signatures

Integer Overflow in Django with Hmac Signatures

Integer Overflow in Django with Hmac Signatures — how this specific combination creates or exposes the vulnerability

An integer overflow in the context of Django HMAC signatures occurs when a length or size value used in signature computation exceeds the native integer range of the platform (e.g., 32-bit vs 64-bit), wrapping to a lower value and producing an incorrect signature. In Django, developers commonly use HMAC to sign data such as timestamps, tokens, or serialized payloads, often with utilities like django.core.signing or manual hmac.compare_digest checks. If a user-controlled integer (for example, a timestamp, an object ID, or a size parameter) is included in the data that is hashed, and that integer can grow beyond expected bounds, the resulting hash can collide or truncate in unexpected ways, bypassing intended integrity checks.

Consider a scenario where an unsigned 32-bit integer is used to represent a creation timestamp or an incremental counter. If an attacker can manipulate that integer to wrap around (for instance, setting it near the maximum value then incrementing once), the HMAC input changes in a way that may still produce a valid-looking signature if the application does not validate the integer range before signing. In Django, this can manifest when using signed cookies or serialized tokens that embed numeric fields without range validation. The framework’s signing module uses TimestampSigner and JSONSerializer, which serialize Python objects into a canonical form before signing. If the serialized representation of an integer is not bounded, an overflowed integer can lead to signature collisions, potentially enabling an attacker to forge a valid signature for a different logical value that hashes to the same truncated representation.

Additionally, if the HMAC key derivation or the payload construction involves arithmetic on integers (e.g., computing an offset or a size), and those integers are not validated, an overflow can cause the computed signature to differ from what the server expects. While Django’s signing module uses SHA1 by default, the integrity guarantee relies on both the secrecy of the key and the correctness of the data before signing. An unchecked integer overflow can cause the data used in the HMAC to diverge from the application’s logical intent, leading to a situation where a tampered payload appears authentic. This is especially risky in security-sensitive flows such as password reset tokens or one-time download links, where the signed value includes an integer identifier.

Real-world parallels can be drawn to issues where numeric parameters in signed tokens were manipulated to cause collisions or privilege escalation. Although not a direct CVE in Django itself, similar classes of flaws appear in libraries and protocols that combine HMAC with unbounded numeric inputs. The OWASP API Security Top 10 category ‘2: Broken Authentication’ and related implementation concerns highlight the importance of validating and bounding all inputs before cryptographic operations. In practice, this means ensuring that integers used in HMAC payloads are constrained, serialized consistently, and verified server-side before the signature is computed or verified.

Hmac Signatures-Specific Remediation in Django — concrete code fixes

Remediation focuses on validating and bounding integer values before they are included in HMAC input, using constant-time comparison, and ensuring serialization is deterministic. In Django, avoid relying on raw integer concatenation for signed payloads; instead, serialize data with a well-defined format and enforce range checks.

Example 1: Safe signed timestamp using TimestampSigner

import time
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired

signer = TimestampSigner()
value = 12345  # integer that must be bounded
if not (0 <= value <= 2**32 - 1):
    raise ValueError('value out of range')
token = signer.sign(value)
# Later, verify with expiration control
try:
    result = signer.unsign(token, max_age=3600)
    verified_value = int(result)
except (BadSignature, SignatureExpired):
    # Handle invalid or expired token safely
    verified_value = None

Example 2: HMAC with explicit payload construction and integer validation

import hmac
import hashlib
import json
from django.conf import settings

def create_signed_payload(user_id, timestamp):
    # Validate integer bounds explicitly
    if not (0 <= user_id < 2**31):
        raise ValueError('user_id out of allowed range')
    if not (0 <= timestamp <= 2**31):
        raise ValueError('timestamp out of allowed range')
    payload = json.dumps({'user_id': user_id, 'timestamp': timestamp}, sort_keys=True, separators=(',', ':'))
    key = settings.SECRET_KEY.encode()
    signature = hmac.new(key, payload.encode(), hashlib.sha256).hexdigest()
    return {'payload': payload, 'signature': signature}

def verify_signed_payload(payload_str, signature, key=None):
    expected = hmac.new(key or settings.SECRET_KEY.encode(), payload_str.encode(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, signature):
        raise ValueError('invalid signature')
    data = json.loads(payload_str)
    # Re-validate integer ranges on verification side
    if not (0 <= data['user_id'] < 2**31):
        raise ValueError('user_id out of allowed range')
    return data

Example 3: Using Django’s signing module with salted key to isolate domains

from django.core.signing import Signer

# Use a salt to ensure signatures are scoped to a specific context
signer = Signer(key=settings.SECRET_KEY, salt='user_invite_token')
user_id = 999
if not (0 <= user_id < 1000000):
    raise ValueError('invalid user identifier')
token = signer.sign(str(user_id))
# Verification will raise BadSignature if invalid
try:
    original = signer.unsign(token)
    verified_id = int(original)
except Exception:
    verified_id = None

Best practices summary

  • Validate integer ranges explicitly before including them in HMAC input.
  • Use deterministic serialization (e.g., JSON with sort_keys) to avoid signature variability.
  • Use hmac.compare_digest for signature comparison to avoid timing attacks.
  • Apply salting in Django Signer to isolate signing contexts and reduce cross-application forgery risk.
  • Treat signed integers as untrusted until re-validated on the server side, even if a signature is valid.

Frequently Asked Questions

Why is integer range validation important before HMAC signing in Django?
Integer values included in HMAC input must be bounded to prevent overflow-induced signature collisions or bypasses. Without validation, an attacker can manipulate numeric fields to wrap around and forge tokens that appear valid.
Does using Django’s TimestampSigner protect against integer overflow in signed values?
TimestampSigner protects against replay attacks via timestamps, but it does not inherently prevent integer overflow. You must validate and bound integer inputs before they are serialized and signed to avoid overflow-related signature issues.