🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)
🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)

🔧 Programmierung 🕛 kürzlich 6 Min Lesezeit
0

How to Verify Square Webhook Signatures (and the Notification-URL Trap)

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Square's webhook signature catches people in a specific way: the signature is computed over the notification URL joined to the raw request body, not the body on its own. Miss the URL, or reconstruct a URL that differs by even a trailing slash, and every notification fails validation while your HMAC code looks perfectly correct. This post shows the exact scheme Square uses, the two traps that break it, and the older header you should stop trusting.






What Square actually signs



Every webhook from Square carries an x-square-hmacsha256-signature header. Per Square's own validation docs, the value is an HMAC-SHA-256 built from three things:




  • The signature key for your webhook subscription (found next to the subscription in the Square Dashboard, one per subscription).

  • The notification URL you configured for that subscription.

  • The raw body of the request, exactly as sent.



The message you run through HMAC is the notification URL followed by the raw body, concatenated in that order, and the result is Base64-encoded. To validate, you compute the same thing and compare it against the header.




CODE
const crypto = require('crypto');

// From your webhook subscription in the Square Dashboard
const SIGNATURE_KEY = process.env.SQUARE_SIGNATURE_KEY;

// The EXACT notification URL you configured for the subscription
const NOTIFICATION_URL = 'https://api.yourapp.com/webhooks/square';

function isValidSquareSignature(rawBody, signatureHeader) {
const payload = NOTIFICATION_URL + rawBody; // URL first, then the raw body
const expected = crypto
.createHmac('sha256', SIGNATURE_KEY)
.update(payload, 'utf8')
.digest('base64');

const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader || '');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Express: capture the RAW body, verify, then parse
app.post('/webhooks/square',
express.raw({ type: '*/*' }),
(req, res) => {
const rawBody = req.body.toString('utf8');
const signature = req.get('x-square-hmacsha256-signature');

if (!isValidSquareSignature(rawBody, signature)) {
return res.status(403).send('bad signature');
}

const event = JSON.parse(rawBody); // only parse after the check passes
// ... handle event
res.sendStatus(200);
});




Notice the comparison uses crypto.timingSafeEqual rather than ===. A plain string compare returns early on the first differing byte, and that timing difference leaks how much of the signature you got right. Use a constant-time compare, and guard the length first because timingSafeEqual throws on a length mismatch.






Trap one: the raw body



HMAC signs bytes, not objects. If your framework parses the JSON and you rebuild a string from the parsed object to verify against, the bytes are no longer identical to what Square sent. Key order, whitespace, and number formatting all shift, and the signature never matches. Capture the raw, unparsed body, verify against it, and parse only after the check passes. In Express that means express.raw() on the webhook route, not express.json(). This is the same trap Stripe, Paddle, HubSpot, and Slack share, laid out side by side in the .






Stop using the legacy x-square-signature header



Older Square integrations validated an x-square-signature header that used HMAC-SHA1. That scheme is legacy and being retired in favor of the SHA-256 header above. If your code still reads x-square-signature, move to x-square-hmacsha256-signature. SHA1 is not a hash you want at the door of your payment events, and Square's current guidance is to use the SHA-256 signature.






The mistakes that account for most failures




  • Signing the body only and forgetting to prepend the notification URL.

  • Verifying against a re-serialized body instead of the raw bytes.

  • Rebuilding the URL from request headers behind a proxy instead of using the configured constant.

  • Using the wrong subscription's signature key, or crossing sandbox and production keys.

  • Comparing with === instead of a constant-time function.






Signatures are only half of it



Validating the signature proves a notification really came from Square. It does nothing for the notification that never arrives, the one that fired while your server was mid-deploy, or the one dropped because your handler threw before it acknowledged. Square retries failed deliveries for a while and then disables the subscription if it keeps failing, so a bad hour on your side can turn into a subscription Square has stopped sending to. The events behind that are the ones tied to money, a payment completed, a refund, a dispute, exactly the ones you cannot afford to lose.



That is the layer EventDock adds. You point Square at EventDock instead of your app. EventDock verifies the signature, stores the event, and acknowledges Square right away so no timeout or downtime window costs you a delivery, then forwards it to your app with its own retries and a dead-letter queue you can replay by hand. If your app is down for an hour, the events wait and arrive when it recovers instead of being dropped and eventually disabled. For the duplicate deliveries any retrying pipeline produces, the

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Hackers Just Poisoned the Rust Supply Chain | Threat Wire
1 Quelle
Hackers Found a Way Into Humanoid Robots | Threat Wire
1 Quelle
Bits und so #1021 (Passwort für Laufwerk)
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten How to Verify Square Webhook Signatures (and the Notification-URL Trap)

Thematisch verwandte Begriffe: Verify, Square, Webhook, Signatures · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...