VYPR
Critical severityNVD Advisory· Published Feb 3, 2018· Updated Aug 5, 2024

CVE-2018-6596

CVE-2018-6596

Description

webhooks/base.py in Anymail (aka django-anymail) before 1.2.1 is prone to a timing attack vulnerability on the WEBHOOK_AUTHORIZATION secret, which allows remote attackers to post arbitrary e-mail tracking events.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A timing attack vulnerability in django-anymail before 1.2.1 allows remote attackers to bypass WEBHOOK_AUTHORIZATION secret validation and post arbitrary email tracking events.

Vulnerability

A timing attack vulnerability exists in the webhooks/base.py module of django-anymail prior to version 1.2.1 [1]. The flaw affects the verification of the WEBHOOK_AUTHORIZATION secret, which is used to authenticate incoming webhook requests from email service providers. Due to a non-constant-time comparison, an attacker can determine the secret byte-by-byte by measuring response times, bypassing authentication for webhook endpoints [2].

Exploitation

An attacker requires network access to the webhook endpoint and does not need prior authentication. By sending a series of crafted HTTP requests with guessed Authorization header values and measuring the server's response time, the attacker can incrementally leak the secret. Once the secret is recovered, the attacker can craft valid requests to the webhook URL [2][4].

Impact

Successful exploitation allows the remote attacker to post arbitrary email tracking events to the webhook endpoint. This can lead to injection of false delivery, open, click, or other engagement events, potentially corrupting email analytics, triggering unintended application logic, and enabling further attacks if the webhook data is processed without additional validation [1][2].

Mitigation

Upgrade to django-anymail version 1.2.1 or later, which was released on 2018-02-03 and fixes the timing attack by implementing a constant-time comparison for the secret [3][4]. No workaround is available for earlier versions; users on unpatched versions should update immediately. This CVE is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
django-anymailPyPI
< 1.2.11.2.1

Affected products

2

Patches

2
db586ede1fbb

Security: prevent timing attack on WEBHOOK_AUTHORIZATION secret

1 file changed · +12 3
  • anymail/webhooks/base.py+12 3 modified
    @@ -2,6 +2,7 @@
     
     import six
     from django.http import HttpResponse
    +from django.utils.crypto import constant_time_compare
     from django.utils.decorators import method_decorator
     from django.views.decorators.csrf import csrf_exempt
     from django.views.generic import View
    @@ -40,8 +41,13 @@ def __init__(self, **kwargs):
         def validate_request(self, request):
             """If configured for webhook basic auth, validate request has correct auth."""
             if self.basic_auth:
    -            basic_auth = get_request_basic_auth(request)
    -            if basic_auth is None or basic_auth not in self.basic_auth:
    +            request_auth = get_request_basic_auth(request)
    +            # Use constant_time_compare to avoid timing attack on basic auth. (It's OK that any()
    +            # can terminate early: we're not trying to protect how many auth strings are allowed,
    +            # just the contents of each individual auth string.)
    +            auth_ok = any(constant_time_compare(request_auth, allowed_auth)
    +                          for allowed_auth in self.basic_auth)
    +            if not auth_ok:
                     # noinspection PyUnresolvedReferences
                     raise AnymailWebhookValidationFailure(
                         "Missing or invalid basic auth in Anymail %s webhook" % self.esp_name)
    @@ -77,8 +83,11 @@ def validate_request(self, request):
             *All* definitions of this method in the class chain (including mixins)
             will be called. There is no need to chain to the superclass.
             (See self.run_validators and collect_all_methods.)
    +
    +        Security note: use django.utils.crypto.constant_time_compare for string
    +        comparisons, to avoid exposing your validation to a timing attack.
             """
    -        # if request.POST['signature'] != expected_signature:
    +        # if not constant_time_compare(request.POST['signature'], expected_signature):
             #     raise AnymailWebhookValidationFailure("...message...")
             # (else just do nothing)
             pass
    
c07998304b4a

Security: prevent timing attack on WEBHOOK_AUTHORIZATION secret

1 file changed · +12 3
  • anymail/webhooks/base.py+12 3 modified
    @@ -3,6 +3,7 @@
     
     import six
     from django.http import HttpResponse
    +from django.utils.crypto import constant_time_compare
     from django.utils.decorators import method_decorator
     from django.views.decorators.csrf import csrf_exempt
     from django.views.generic import View
    @@ -41,8 +42,13 @@ def __init__(self, **kwargs):
         def validate_request(self, request):
             """If configured for webhook basic auth, validate request has correct auth."""
             if self.basic_auth:
    -            basic_auth = get_request_basic_auth(request)
    -            if basic_auth is None or basic_auth not in self.basic_auth:
    +            request_auth = get_request_basic_auth(request)
    +            # Use constant_time_compare to avoid timing attack on basic auth. (It's OK that any()
    +            # can terminate early: we're not trying to protect how many auth strings are allowed,
    +            # just the contents of each individual auth string.)
    +            auth_ok = any(constant_time_compare(request_auth, allowed_auth)
    +                          for allowed_auth in self.basic_auth)
    +            if not auth_ok:
                     # noinspection PyUnresolvedReferences
                     raise AnymailWebhookValidationFailure(
                         "Missing or invalid basic auth in Anymail %s webhook" % self.esp_name)
    @@ -78,8 +84,11 @@ def validate_request(self, request):
             *All* definitions of this method in the class chain (including mixins)
             will be called. There is no need to chain to the superclass.
             (See self.run_validators and collect_all_methods.)
    +
    +        Security note: use django.utils.crypto.constant_time_compare for string
    +        comparisons, to avoid exposing your validation to a timing attack.
             """
    -        # if request.POST['signature'] != expected_signature:
    +        # if not constant_time_compare(request.POST['signature'], expected_signature):
             #     raise AnymailWebhookValidationFailure("...message...")
             # (else just do nothing)
             pass
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

9

News mentions

0

No linked articles in our index yet.