Webhook Security

Partner Webhook Integration Guide

Secure Webhook Integration with Coinme


Table of Contents

  1. Overview
  2. Security Options Available
  3. HMAC Signature Authentication
  4. SSL/TLS Encryption
  5. API Key Authentication
  6. Security Feature Combinations
  7. Webhook Request Format
  8. Integration Checklist
  9. Testing Your Integration
  10. Support and Contact
  11. Security Best Practices Summary

Appendix: Security Feature Comparison


Overview

Coinme's notification service sends real-time webhook notifications to partner endpoints. We support multiple security options to ensure secure and authenticated delivery of notifications. This guide explains the available security features and how to integrate them.


Security Options Available

Coinme provides three security features that can be enabled individually or in combination:

#FeatureDescription
1HMAC Signature AuthenticationVerify webhook authenticity
2SSL/TLS EncryptionSecure transport layer
3API Key AuthenticationSimple header-based authentication

1. HMAC Signature Authentication

What It Is

HMAC (Hash-based Message Authentication Code) signatures allow you to verify that webhooks are authentic and haven't been tampered with. Each webhook includes a cryptographic signature in the HTTP header that you can verify using a shared secret.

How It Works

Coinme Side

Your Side

  1. Compute HMAC-SHA256 signature of payload
  1. Receive webhook with signature header
  1. Base64 or HEX-encode the signature
  1. Compute HMAC-SHA256 of request body
  1. Send in HTTP header (X-Coinme-Signature)
  1. Base64-encode the result
  1. Compare with signature header

Configuration

To enable HMAC signatures, provide Coinme with:

  • Signature Secret: A shared secret key (we recommend at least 32 characters)
  • Signature Header Name (optional): Custom header name (default: X-Coinme-Signature)

Integration Example

Node.js Example

const crypto = require('crypto');

function verifyWebhookSignature(requestBody, signatureHeader, secret) {
  // Compute HMAC-SHA256
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(requestBody, 'utf8');
  
  // Base64 encode
  const computedSignature = hmac.digest('base64');
  
  // Compare signatures
  return crypto.timingSafeEqual(
    Buffer.from(computedSignature),
    Buffer.from(signatureHeader)
  );
}

// Usage
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-coinme-signature'];
  const isValid = verifyWebhookSignature(
    req.body.toString(),
    signature,
    process.env.WEBHOOK_SECRET
  );
  
  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process webhook
  const payload = JSON.parse(req.body);
  // ... handle webhook
});

Python Example

import hmac
import hashlib
import base64

def verify_webhook_signature(request_body, signature_header, secret):
    # Compute HMAC-SHA256
    computed_signature = hmac.new(
        secret.encode('utf-8'),
        request_body.encode('utf-8'),
        hashlib.sha256
    ).digest()
    
    # Base64 encode
    computed_signature_b64 = base64.b64encode(computed_signature).decode('utf-8')
    
    # Compare signatures (use constant-time comparison)
    return hmac.compare_digest(computed_signature_b64, signature_header)

# Usage (Flask example)
@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Coinme-Signature')
    body = request.get_data(as_text=True)
    
    if not verify_webhook_signature(body, signature, os.environ['WEBHOOK_SECRET']):
        return 'Invalid signature', 401
    
    # Process webhook
    payload = json.loads(body)
    # ... handle webhook

Best Practices

  • Always verify signatures before processing webhooks
  • Use constant-time comparison to prevent timing attacks
  • Store secrets securely (environment variables, secret management systems)
  • Never log signature secrets
  • Rotate secrets periodically (coordinate with Coinme support)

Troubleshooting

IssueSolution
Signature mismatchEnsure you're using the exact same secret key
Encoding errorsUse UTF-8 encoding for payload and secret
Header not foundCheck header name (default: X-Coinme-Signature)
Base64 decode failsVerify signature header format

2. SSL/TLS Encryption

What It Is

SSL/TLS encryption ensures that webhook data is encrypted in transit between Coinme and your endpoint. We support both standard HTTPS and mutual TLS (mTLS) with client certificates.

How It Works

Standard HTTPS

  • Your endpoint must support HTTPS (TLS 1.2 or higher)
  • Valid SSL certificate required
  • Certificate must be from a trusted Certificate Authority (CA)

Mutual TLS (mTLS)

  • Both parties authenticate with certificates
  • You provide client certificate and private key to Coinme
  • Coinme uses these to authenticate when connecting to your endpoint

Certificate Requirements

  • Format: PEM (preferred) or DER
  • Key Size: RSA 2048-bit minimum, or ECDSA equivalent
  • Validity: Certificates should be valid and not expired
  • Chain: Include full certificate chain if using intermediate CAs

Integration Example - Nginx Configuration (mTLS)

server {
    listen 443 ssl;
    server_name webhook.yourdomain.com;
    
    # Server certificate
    ssl_certificate /path/to/server.crt;
    ssl_certificate_key /path/to/server.key;
    
    # Client certificate verification
    ssl_client_certificate /path/to/ca.crt;  # Coinme's CA
    ssl_verify_client on;
    ssl_verify_depth 2;
    
    location /webhook {
        proxy_pass http://localhost:3000;
        proxy_set_header X-Client-Cert $ssl_client_cert;
    }
}

3. API Key Authentication

What It Is

API key authentication is a simple method where Coinme includes an API key in a custom HTTP header when sending webhooks. You verify this key matches your expected value.

How It Works

Coinme SideYour Side
Include your API key in custom HTTP headerExtract API key from configured header
Header name is configurableCompare with stored API key value
Reject requests with invalid or missing keys

Integration Example

// Node.js Example
app.post('/webhook', (req, res) => {
  const apiKey = req.headers['x-api-key'];
  const expectedKey = process.env.API_KEY;
  
  if (apiKey !== expectedKey) {
    return res.status(401).send('Invalid API key');
  }
  
  // Process webhook
  const payload = req.body;
  // ... handle webhook
});
# Python Example
@app.route('/webhook', methods=['POST'])
def webhook():
    api_key = request.headers.get('X-API-Key')
    expected_key = os.environ['API_KEY']
    
    if api_key != expected_key:
        return 'Invalid API key', 401
    
    # Process webhook
    payload = request.json
    # ... handle webhook

Security Considerations

Note: API key authentication alone provides basic security. For stronger security, combine with:

  • HMAC signatures (recommended)
  • SSL/TLS encryption (required for production)

4. Security Feature Combinations

You can enable any combination of security features based on your requirements:

Recommended Configurations

Production (High Security)

  • HMAC Signature Authentication
  • SSL/TLS Encryption (HTTPS)
  • API Key Authentication (optional)

Staging/Development

  • HMAC Signature Authentication
  • SSL/TLS Encryption (HTTPS)

Basic Security

  • SSL/TLS Encryption (HTTPS)
  • API Key Authentication

5. Webhook Request Format

Standard Webhook Request

POST /webhook HTTP/1.1
Host: your-endpoint.com
Content-Type: application/json
X-Coinme-Signature: <base64-encoded-hmac-signature>
X-API-Key: <your-api-key>
Content-Length: 1234

{
  "eventType": "ORDER_CREATED",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    // Event-specific data
  }
}

Headers Included

HeaderWhen IncludedDescription
Content-TypeAlwaysapplication/json
X-Coinme-SignatureIf HMAC enabledBase64-encoded HMAC-SHA256 signature
X-API-KeyIf API key configuredYour configured API key value
Custom headersAs configuredAny custom headers you've specified

6. Integration Checklist

Use this checklist to ensure proper integration:

Pre-Integration

  • Determine which security features you need
  • Prepare webhook endpoint (HTTPS required)
  • Generate HMAC secret (if using signatures)
  • Generate API key (if using API key auth)
  • Prepare SSL certificates (if using mTLS)

Configuration

  • Contact Coinme support to configure security features
  • Provide webhook URL to Coinme
  • Share HMAC secret securely (if using signatures)
  • Share API key securely (if using API key auth)
  • Provide SSL certificates (if using mTLS)

Implementation

  • Implement HMAC signature verification (if enabled)
  • Implement API key validation (if enabled)
  • Configure SSL/TLS on your endpoint
  • Test webhook reception
  • Test signature verification (if enabled)
  • Test API key validation (if enabled)

Testing

  • Test webhook delivery from Coinme
  • Verify signature validation (if enabled)
  • Verify API key validation (if enabled)
  • Test error handling (invalid signature, invalid key)
  • Test SSL/TLS connection (if enabled)
  • Monitor webhook logs

Production

  • Enable all security features
  • Monitor webhook delivery
  • Set up alerts for failures
  • Document your integration
  • Plan for secret rotation

7. Testing Your Integration

Test Webhook Endpoint

Create a test endpoint to verify your integration:

// Test endpoint example
app.post('/webhook-test', express.raw({ type: 'application/json' }), (req, res) => {
  console.log('Headers:', req.headers);
  console.log('Body:', req.body.toString());
  
  // Verify signature
  if (req.headers['x-coinme-signature']) {
    const isValid = verifyWebhookSignature(
      req.body.toString(),
      req.headers['x-coinme-signature'],
      process.env.WEBHOOK_SECRET
    );
    console.log('Signature valid:', isValid);
  }
  
  // Verify API key
  if (req.headers['x-api-key']) {
    const keyValid = req.headers['x-api-key'] === process.env.API_KEY;
    console.log('API key valid:', keyValid);
  }
  
  res.status(200).send('OK');
});

Common Test Scenarios

ScenarioExpected Result
Valid webhookShould process successfully
Invalid signatureShould return 401
Missing signatureShould return 401 (if required)
Invalid API keyShould return 401
Missing API keyShould return 401 (if required)
Malformed payloadShould handle gracefully

8. Support and Contact

Getting Help

  • Integration Support: Contact your Coinme account manager
  • Security Configuration: Reach out to Coinme support team
  • Technical Issues: Submit a support ticket

Required Information

When requesting support, please provide:

  • Partner ID
  • Webhook endpoint URL
  • Security features you want to enable
  • Any error messages or logs
  • Integration environment (dev/staging/prod)

9. Security Best Practices Summary

Do'sDon'ts
Always verify HMAC signaturesDon't skip signature verification
Use HTTPS for all webhook endpointsDon't use HTTP (use HTTPS)
Store secrets securely (never in code)Don't log secrets or API keys
Use constant-time comparison for signaturesDon't use weak secrets or keys
Monitor webhook delivery and failuresDon't ignore SSL certificate warnings
Rotate secrets and keys periodicallyDon't hardcode secrets in your code
Implement proper error handlingDon't share secrets via insecure channels
Log security events (without logging secrets)

Appendix: Security Feature Comparison

FeatureSecurity LevelComplexityUse Case
API KeyLow-MediumLowSimple authentication
SSL/TLSMediumMediumData encryption in transit
HMAC SignatureHighMediumMessage authenticity & integrity
mTLSVery HighHighMaximum security with mutual authentication

Recommended: Use HMAC signatures + SSL/TLS for production environments.


For questions or additional support, please contact your Coinme account manager or support team.