{"record":{"id":"3055a1c5a0b5231a","repo":"openai/openai-python","slug":"webhook-timestamp-is-too-new","errorCode":null,"errorMessage":"Webhook timestamp is too new","messagePattern":"Webhook timestamp is too new","errorType":"validation","errorClass":"InvalidWebhookSignatureError","httpStatus":null,"severity":"error","filePath":"src/openai/lib/_webhooks.py","lineNumber":37,"sourceCode":") -> bool:\n    \"\"\"Validate the replay window and compare the supplied signatures.\"\"\"\n    signature_header = get_required_header(headers, \"webhook-signature\")\n    timestamp = get_required_header(headers, \"webhook-timestamp\")\n    webhook_id = get_required_header(headers, \"webhook-id\")\n\n    # Validate timestamp to prevent replay attacks\n    try:\n        timestamp_seconds = int(timestamp)\n    except ValueError:\n        raise InvalidWebhookSignatureError(\"Invalid webhook timestamp format\") from None\n\n    now = int(time.time())\n\n    if now - timestamp_seconds > tolerance:\n        raise InvalidWebhookSignatureError(\"Webhook timestamp is too old\") from None\n\n    if timestamp_seconds > now + tolerance:\n        raise InvalidWebhookSignatureError(\"Webhook timestamp is too new\") from None\n\n    # Extract signatures from v1,<base64> format\n    # The signature header can have multiple values, separated by spaces.\n    # Each value is in the format v1,<base64>. We should accept if any match.\n    signatures: list[str] = []\n    for part in signature_header.split():\n        if part.startswith(\"v1,\"):\n            signatures.append(part[3:])\n        else:\n            signatures.append(part)\n\n    # Decode the secret if it starts with whsec_\n    if secret.startswith(\"whsec_\"):\n        decoded_secret = base64.b64decode(secret[6:])\n    else:\n        decoded_secret = secret.encode()\n\n    body = payload.decode(\"utf-8\") if isinstance(payload, bytes) else payload","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/openai/openai-python/blob/9917c6e28e66e90e1227b3d223c06a8c5441515a/src/openai/lib/_webhooks.py#L19-L55","documentation":"webhook_signature_matches rejects webhooks whose timestamp is further in the future than the tolerance window, indicating a stale signed payload being replayed against a newer context or severe clock skew on the receiver.","triggerScenarios":"timestamp_seconds > now + tolerance: receiving host's clock behind the sender, or a forged/manipulated timestamp header.","commonSituations":"Clock drift on the webhook receiver server; VMs resumed from snapshots with stale clocks; tampered payloads during attack attempts.","solutions":["Correct the receiver's system clock via NTP","Log both the header timestamp and local time when this fires to diagnose skew direction","If skew is expected in your infra, increase tolerance explicitly rather than disabling verification"],"exampleFix":"// n/a (infrastructure fix)\n# example: widen tolerance only if justified\nwebhook_signature_matches(body, headers, secret, tolerance=600)","handlingStrategy":"try-catch","validationCode":"ts = int(headers[\"webhook-timestamp\"])\nimport time\nif ts > time.time() + tolerance:\n    logger.warning(\"receiver clock behind sender by %ss\", ts - time.time())","typeGuard":null,"tryCatchPattern":"try:\n    ok = webhook_signature_matches(body, headers, secret)\nexcept InvalidWebhookSignatureError as e:\n    if \"too new\" in str(e):\n        return Response(401)\n    raise","preventionTips":["Keep receiver clocks synced (chrony/ntp)","Avoid snapshot-resumed VMs as webhook hosts","Alert on future-timestamp failures; they indicate skew or tampering"],"tags":["webhooks","clock-skew","signature"],"backgroundTag":"webhook-timestamp-invalid","analyzedSha":"9917c6e28e66e90e1227b3d223c06a8c5441515a","analyzedAt":"2026-08-28T11:46:34.183Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}