3U

Plugin Security

Signature verification and security best practices.

Signature Model

All communication is signed using HMAC-SHA256. The signature ensures:

  • Authenticity — Request came from the expected sender
  • Integrity — Payload wasn't modified
  • Freshness — Not a replay attack

Verification — Node.js

Verify inbound requests using HMAC-SHA256:

Signature Verification — Node.js
TypeScript
const crypto = require('crypto');

function verifySignature(req, secret) {
  const signature = req.headers['x-plugin-signature'];
  const timestamp = req.headers['x-plugin-timestamp'];
  
  // Check timestamp freshness (5 min window)
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    throw new Error('Request timestamp too old');
  }
  
  const rawBody = req.rawBody || JSON.stringify(req.body);
  const expected = crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.' + rawBody)
    .digest('hex');
  
  if (!crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from('sha256=' + expected)
  )) {
    throw new Error('Invalid signature');
  }
  return true;
}

Verification — PHP

PHP/Laravel verification:

Signature Verification — PHP
PHP
<?php
function verifySignature(Request $request, string $secret): bool
{
    $signature = $request->header('X-Plugin-Signature');
    $timestamp = $request->header('X-Plugin-Timestamp');
    
    if (abs(time() - (int) $timestamp) > 300) {
        abort(401, 'Timestamp too old');
    }
    
    $rawBody = $request->getContent();
    $expected = 'sha256=' . hash_hmac(
        'sha256', $timestamp . '.' . $rawBody, $secret
    );
    
    if (!hash_equals($expected, $signature)) {
        abort(401, 'Invalid signature');
    }
    return true;
}

Why Raw Body Signing?

ThreeU signs the raw request body rather than json_encode(payload):

  1. Deterministic — Exact bytes transmitted are signed
  2. Language-agnostic — No JSON encoding differences
  3. Tamper-proof — Any modification invalidates the signature
  4. Standard — Matches Stripe, GitHub, etc.