Critical Security Issue: Unverified Payment Webhook Source
Description
Our application currently does not verify if the incoming webhook events truly originate from MercadoPago. This lack of validation leaves us vulnerable to external exploit attempts, as unauthorized actors could potentially send fake notifications to our webhook endpoint.
Risk
If not addressed, this vulnerability could expose the application to fraud, potentially allowing malicious actors to trigger or manipulate payment processing events.
Proposed Solution
To prevent this exploit, we need to validate each incoming webhook notification from MercadoPago by checking the x-signature header, which contains a cryptographic signature ensuring the authenticity of the notification. The header has the format ts=timestamp,v1=signature, where:
ts: The timestamp when the message was generated.
v1: The HMAC signature generated by MercadoPago.
Validation Steps
- Parse the
x-signature header and extract ts and v1 components.
- Retrieve the secret key provided by MercadoPago from our application's environment variables.
- Construct a manifest string with required values (e.g.,
id, request-id, ts).
- Generate an HMAC using SHA-256 with the manifest string and secret key.
- Compare the resulting hash with the
v1 signature from the header. If they match, the message is authentic.
Here’s an example of how this verification could be implemented in Java, converted from JavaScript:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Map;
public boolean verifyWebhook(Map<String, String> headers, String requestBody, String secret) throws Exception {
String xSignature = headers.get("x-signature");
String xRequestId = headers.get("x-request-id");
String[] parts = xSignature.split(",");
String ts = null;
String hash = null;
for (String part : parts) {
String[] keyValue = part.split("=");
if ("ts".equals(keyValue[0])) {
ts = keyValue[1];
} else if ("v1".equals(keyValue[0])) {
hash = keyValue[1];
}
}
String manifest = String.format("id:%s;request-id:%s;ts:%s;", extractDataId(requestBody), xRequestId, ts);
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
hmac.init(secretKeySpec);
byte[] signature = hmac.doFinal(manifest.getBytes());
String computedHash = Base64.getEncoder().encodeToString(signature);
return computedHash.equals(hash);
}
private String extractDataId(String requestBody) {
// Implementation to extract data.id from request body JSON
}
References
Critical Security Issue: Unverified Payment Webhook Source
Description
Our application currently does not verify if the incoming webhook events truly originate from MercadoPago. This lack of validation leaves us vulnerable to external exploit attempts, as unauthorized actors could potentially send fake notifications to our webhook endpoint.
Risk
If not addressed, this vulnerability could expose the application to fraud, potentially allowing malicious actors to trigger or manipulate payment processing events.
Proposed Solution
To prevent this exploit, we need to validate each incoming webhook notification from MercadoPago by checking the
x-signatureheader, which contains a cryptographic signature ensuring the authenticity of the notification. The header has the formatts=timestamp,v1=signature, where:ts: The timestamp when the message was generated.v1: The HMAC signature generated by MercadoPago.Validation Steps
x-signatureheader and extracttsandv1components.id,request-id,ts).v1signature from the header. If they match, the message is authentic.Here’s an example of how this verification could be implemented in Java, converted from JavaScript:
References