Skip to content

Security: Use constant-time comparison in check_webapp_signature #1711

@desspperate

Description

@desspperate

aiogram version

3.x

Problem

Hello,

The check_webapp_signature function in aiogram.utils.web_app currently uses the standard == operator for comparing the calculated hash with the provided hash.

This operation is not constant-time and could theoretically be vulnerable to timing attacks. While the practical risk in this specific context is low due to the significant overhead from the HMAC-SHA256 calculations, it is a cryptographic best practice to always use a constant-time string comparison function for security-sensitive data.

I suggest replacing the comparison with hmac.compare_digest to mitigate this potential vulnerability entirely and adhere to security best practices.

Possible solution

To enhance security, the check_webapp_signature function should use the constant-time comparison function hmac.compare_digest instead of the standard equality operator (==) for verifying the signature hash.

This change directly addresses a potential timing attack vulnerability. By ensuring the comparison operation always takes the same amount of time regardless of how many characters match, we eliminate any side-channel information an attacker could use. This aligns the implementation with cryptographic best practices and makes the function more robust.

Alternatives

An alternative is to leave the implementation as is, arguing that the practical risk of a successful timing attack is extremely low. The rationale is that the significant computational overhead from the two HMAC-SHA256 calculations creates enough timing "noise" to effectively mask the subtle signal from the string comparison.

However, relying on implementation side-effects for security is fragile and not a recommended practice. The proposed fix using hmac.compare_digest is trivial to implement, has no negative performance impact, and provides a provably secure solution against this specific attack vector, making the code more resilient for the future.

Code example

# In `aiogram/utils/web_app.py`

# --- Current Code ---
def check_webapp_signature(token: str, init_data: str) -> bool:
    # ... (omitted code for hash calculation)
    calculated_hash = hmac.new(
        key=secret_key.digest(), msg=data_check_string.encode(), digestmod=hashlib.sha256
    ).hexdigest()
    return calculated_hash == bot_hash


# --- Proposed Code ---
def check_webapp_signature(token: str, init_data: str) -> bool:
    # ... (omitted code for hash calculation)
    calculated_hash = hmac.new(
        key=secret_key.digest(), msg=data_check_string.encode(), digestmod=hashlib.sha256
    ).hexdigest()
    # Use constant-time comparison to prevent timing attacks
    return hmac.compare_digest(calculated_hash, bot_hash)

Additional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions