Security Fix: Cardstream Callback Signature Verification (SEC-05)
Security Fix: Cardstream Callback Signature Verification (SEC-05)
Release: v1.0.28
OWASP Category: A05:2021 — Security Misconfiguration
Severity: High
Affected file: src/app/api/v1/checkout/sessions/callback/route.ts
Background
When Cardstream processes a payment, it sends a POST callback to Calmony Pay's callback endpoint. To prove the request originated from Cardstream, the payload includes an HMAC-SHA512 signature field computed using a shared secret (CARDSTREAM_SIGNATURE_KEY). The callback handler is expected to recompute this signature and reject any request where the values do not match.
The Vulnerability
Prior to v1.0.28, the signature check was guarded by an if statement that required both a configured key and a present signature field:
// Before — vulnerable
if (signatureKey && fields.signature) {
// HMAC verification ran here
// if either condition was falsy, this block was skipped entirely
}
// execution continued regardless
This introduced two distinct bypass conditions:
| Condition | Effect |
|---|---|
CARDSTREAM_SIGNATURE_KEY not set in environment | Verification skipped; all callbacks accepted |
Cardstream callback arrives without a signature field | Verification skipped; callback accepted |
In either case the handler would continue processing the callback as authentic, potentially updating a checkout session's status to succeeded without a real payment having occurred.
Impact
An attacker with knowledge of Calmony Pay's callback URL could craft an HTTP POST that mimics a successful Cardstream payment notification. Without signature verification, this forged request would be accepted and could:
- Mark a checkout session as
succeeded - Trigger downstream fulfilment, licence provisioning, or subscription activation
- Cause real financial discrepancy between Cardstream's records and Calmony Pay's ledger
The Fix
Signature verification is now unconditional in production. The logic has been restructured so that a missing key or a missing signature are both treated as hard failures.
// After — fixed
if (!signatureKey) {
// CARDSTREAM_SIGNATURE_KEY is not configured — refuse to process
return errorResponse(500, 'Signature verification is not configured');
}
if (!fields.signature) {
// Callback arrived without a signature — treat as failed verification
return redirect(cancelUrl);
}
// Proceed with HMAC-SHA512 comparison
const valid = verifySignature(fields, signatureKey);
if (!valid) {
return redirect(cancelUrl);
}
Test-mode bypass
A controlled bypass is still available for local development, automated tests, and CI environments where a real Cardstream connection is not present. The bypass is activated by setting either:
NODE_ENV=testREQUIRE_SIGNATURE=false
This must be set explicitly and intentionally; the bypass is never the default.
Action Required for Self-Hosted Deployments
If you run Calmony Pay in your own infrastructure, take the following steps before or immediately after deploying v1.0.28:
- Ensure
CARDSTREAM_SIGNATURE_KEYis set in every production and staging environment that processes real Cardstream callbacks. The callback endpoint will return500if this variable is absent. - Do not set
REQUIRE_SIGNATURE=falsein any environment that handles real payments. - Review your environment variable management to ensure secrets are injected at runtime and not accidentally omitted during deployments.
Environment Variables Reference
| Variable | Required in Production | Description |
|---|---|---|
CARDSTREAM_SIGNATURE_KEY | ✅ Yes | Shared secret used to verify Cardstream HMAC-SHA512 callback signatures. Obtain from your Cardstream merchant portal. |
REQUIRE_SIGNATURE | — | Set to false only in test/CI environments to bypass signature verification. Never set in production. |
OWASP Reference
This issue is classified under OWASP Top 10 A05:2021 — Security Misconfiguration, which covers cases where missing, incomplete, or insecure default configurations leave an application vulnerable. The root cause here is a conditional verification pattern that silently degrades to no verification when a configuration value is absent.