Verify Webhook Signatures

View as Markdown

BlockBee uses a 1024-bit RSA SHA256 signature with a public-key signature scheme to sign every webhook sent to your service. This ensures all data was sent from BlockBee and can be trusted.

How BlockBee Signature Works

What Gets Signed

  • For GET requests: The full URL including all query parameters is signed
  • For POST requests: The entire request body is signed

Signature Details

  • Algorithm: RSA SHA256 with PKCS1v15 padding
  • Key Size: 1024-bit RSA key
  • Header: Signature is sent in the x-ca-signature header
  • Encoding: Signature is base64-encoded
  • Public Key: Available at https://api.blockbee.io/pubkey/

Example Signed Data

For a GET request, the signed data looks like:

Bash
https://yoursite.com/webhook?uuid=dbfcb40e-5a6b-4305-9fa2-b0fbda6e3ff2&address_in=3PFoGK63cVVUWnd2vu7W1kM83NXUfvzMqM&txid_in=a2174ffd39289100709f2a07b129cdbba69df2e22e5be1830221dab1fd4e332c&value_coin=0.05&confirmations=3&pending=0

For a POST request, the signed data is the raw request body:

Bash
uuid=dbfcb40e-5a6b-4305-9fa2-b0fbda6e3ff2&address_in=3PFoGK63cVVUWnd2vu7W1kM83NXUfvzMqM&txid_in=a2174ffd39289100709f2a07b129cdbba69df2e22e5be1830221dab1fd4e332c&value_coin=0.05&confirmations=3&pending=0

Implementation Examples

// Node.js verification
const crypto = require('crypto');

// BlockBee's public key (you can also fetch from https://api.blockbee.io/pubkey/)
const BLOCKBEE_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3FT0Ym8b3myVxhQW7ESuuu6lo
dGAsUJs4fq+Ey//jm27jQ7HHHDmP1YJO7XE7Jf/0DTEJgcw4EZhJFVwsk6d3+4fy
Bsn0tKeyGMiaE6cVkX0cy6Y85o8zgc/CwZKc0uw6d5siAo++xl2zl+RGMXCELQVE
ox7pp208zTvown577wIDAQAB
-----END PUBLIC KEY-----`;

function verifyBlockBeeSignature(req) {
    try {
        // Get signature from header
        const signature_b64 = req.headers['x-ca-signature'];
        if (!signature_b64) {
            console.error('Missing x-ca-signature header');
            return false;
        }
        
        // Decode signature from base64
        const signature = Buffer.from(signature_b64, 'base64');
        
        // Determine what data to verify based on request method
        let dataToVerify;
        if (req.method === 'GET') {
            // For GET requests, sign the full URL
            dataToVerify = req.protocol + '://' + req.get('host') + req.originalUrl;
        } else {
            // For POST requests, sign the raw body
            dataToVerify = req.rawBody; // You need raw body middleware
        }
        
        // Create verifier and verify signature
        const verifier = crypto.createVerify('RSA-SHA256');
        verifier.update(dataToVerify);
        
        const isValid = verifier.verify(BLOCKBEE_PUBLIC_KEY, signature);
        
        if (!isValid) {
            console.error('Invalid signature');
        }
        
        return isValid;
        
    } catch (error) {
        console.error('Signature verification error:', error);
        return false;
    }
}

// Express middleware to capture raw body
function captureRawBody(req, res, next) {
    req.rawBody = '';
    req.on('data', chunk => {
        req.rawBody += chunk.toString();
    });
    req.on('end', () => {
        next();
    });
}

// Usage in Express
app.use('/webhook', captureRawBody);

app.post('/webhook', (req, res) => {
    if (!verifyBlockBeeSignature(req)) {
        return res.status(401).send('Invalid signature');
    }
    
    // Process webhook safely
    console.log('Verified webhook received');
    res.status(200).send('*ok*');
});

Security Best Practices

  1. Always verify signatures - Never skip signature verification in production
  2. Use HTTPS - Ensure your webhook endpoint uses HTTPS
  3. Implement rate limiting - Prevent abuse of your webhook endpoint
  4. Log security events - Log all signature verification failures
  5. Validate webhook data - Even verified webhooks should have data validation
  6. Use idempotency - Prevent duplicate processing of webhooks