F
FromChain
Security & Best Practices

Security

Best practices for securing your integration with FromChain payment gateway.

API Key Management

Never Expose API Keys

Keep API keys server-side only. Never expose them in client-side code, mobile apps, or public repositories.

Use Environment Variables

Store keys in environment variables or secure secret management systems like AWS Secrets Manager or HashiCorp Vault.

Rotate Keys Regularly

Rotate API keys every 90 days or immediately if you suspect a compromise.

Use Scoped Keys

Create API keys with minimal required permissions. Use read-only keys for monitoring and full-access keys only when necessary.

Never commit API keys to version control. Add .env files to your .gitignore.
.env.example
# API Configuration
FROMCHAIN_API_KEY=gw_live_your_key_here
FROMCHAIN_WEBHOOK_SECRET=whsec_your_secret_here

# Never commit .env to git!
# Add to .gitignore:
# .env
# .env.local

Webhook Signature Verification

Always verify webhook signatures to prevent spoofing and replay attacks.

verify-webhook.ts
import { createHmac, timingSafeEqual } from 'crypto';

function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string,
  timestamp: string
): boolean {
  // Prevent replay attacks (reject requests older than 5 minutes)
  const currentTime = Date.now();
  const webhookTime = parseInt(timestamp);
  const fiveMinutes = 5 * 60 * 1000;
  
  if (currentTime - webhookTime > fiveMinutes) {
    throw new Error('Webhook timestamp too old');
  }

  // Verify HMAC signature
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSignature = createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
  
  const expected = Buffer.from(`v1=${expectedSignature}`);
  const actual = Buffer.from(signature);
  
  // Use timing-safe comparison to prevent timing attacks
  if (expected.length !== actual.length) {
    return false;
  }
  
  return timingSafeEqual(expected, actual);
}

// Express.js example
app.post('/api/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const timestamp = req.headers['x-webhook-timestamp'];
  const payload = req.body.toString('utf8');
  
  try {
    const isValid = verifyWebhookSignature(
      payload,
      signature,
      process.env.FROMCHAIN_WEBHOOK_SECRET,
      timestamp
    );
    
    if (!isValid) {
      return res.status(401).json({ error: 'Invalid signature' });
    }
    
    // Process webhook
    const event = JSON.parse(payload);
    console.log('Verified webhook:', event.type);
    
    res.json({ received: true });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

Security Checklist:

Verify HMAC-SHA256 signature on every webhook
Check timestamp to prevent replay attacks
Use timing-safe comparison for signature validation
Store webhook secrets securely (never hardcode)

Wallet Address Best Practices

Validate Addresses

Always validate BSC wallet addresses before using them. Ensure they are valid BEP-20 addresses.

Test First

When updating payout addresses, send a small test transaction first to verify the address is correct.

Use Cold Storage

For large amounts, consider using a hot wallet for payouts and regularly transferring to cold storage.

validate-address.js
import { ethers } from 'ethers';

function isValidBSCAddress(address: string): boolean {
  try {
    // Check if it's a valid Ethereum-style address
    return ethers.utils.isAddress(address);
  } catch (error) {
    return false;
  }
}

// Example usage
const payoutAddress = '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb';

if (!isValidBSCAddress(payoutAddress)) {
  throw new Error('Invalid BSC wallet address');
}

console.log('✓ Valid address'):

HTTPS Requirements

All webhook endpoints must use HTTPS

Webhook URLs must use TLS 1.2 or higher. HTTP endpoints will be rejected.

https://yourdomain.com/webhooks
http://yourdomain.com/webhooks

Idempotency

Use idempotency keys to safely retry requests without creating duplicates.

idempotent-request.js
import { v4 as uuidv4 } from 'uuid';

async function createInvoice(orderData) {
  // Generate a unique idempotency key based on your order ID
  const idempotencyKey = `order_${orderData.id}_${Date.now()}`;
  
  const response = await fetch('https://api.fromchain.io/v1/invoices', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer gw_live_...',
      'Content-Type': 'application/json',
      'Idempotency-Key': idempotencyKey  // Prevent duplicates
    },
    body: JSON.stringify({
      amountExpected: orderData.amount,
      externalId: orderData.id,
      description: orderData.description
    })
  });
  
  if (!response.ok) {
    // Safe to retry with same idempotency key
    throw new Error('Failed to create invoice');
  }
  
  return response.json();
}

// If the request fails due to network issues, retrying with
// the same idempotency key will return the original invoice
// instead of creating a duplicate.

💡 Tip: Idempotency keys are valid for 24 hours. The same key will return the same response within this window.

Rate Limiting Compliance

Respect rate limits to ensure reliable service for all users.

Current Limits

  • 100 requests per minute
  • 1000 requests per hour
  • • Per API key basis

Response Headers

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset
handle-rate-limits.js
async function makeAPIRequest(url, options) {
  const response = await fetch(url, options);
  
  // Check rate limit headers
  const limit = response.headers.get('X-RateLimit-Limit');
  const remaining = response.headers.get('X-RateLimit-Remaining');
  const reset = response.headers.get('X-RateLimit-Reset');
  
  if (response.status === 429) {
    // Rate limit exceeded
    const retryAfter = parseInt(reset) - Date.now();
    console.warn(`Rate limited. Retry after ${retryAfter}ms`);
    
    // Implement exponential backoff
    await new Promise(resolve => setTimeout(resolve, retryAfter));
    return makeAPIRequest(url, options);
  }
  
  // Log when approaching limit
  if (parseInt(remaining) < 10) {
    console.warn(`Only ${remaining} requests remaining`);
  }
  
  return response;
}

Secure Error Handling

Don't Expose Sensitive Data

Never return API keys, webhook secrets, or internal error details to clients.

Log Securely

Sanitize logs to exclude sensitive information. Never log full API keys or secrets.

secure-logging.js
// ❌ Bad: Logs full API key
console.log('API Key:', process.env.FROMCHAIN_API_KEY);

// ✅ Good: Logs only last 4 characters
const apiKey = process.env.FROMCHAIN_API_KEY;
const masked = 'gw_****' + apiKey.slice(-4);
console.log('API Key:', masked);

// ✅ Good: Sanitize error responses
app.use((error, req, res, next) => {
  // Log full error server-side
  console.error('Internal error:', error);
  
  // Return sanitized error to client
  res.status(500).json({
    error: 'Internal server error',
    requestId: req.id,
    // Don't expose: stack traces, database errors, keys, etc.
  });
});

Security Monitoring

Monitor for Anomalies

  • • Unusual API request patterns
  • • Failed authentication attempts
  • • Unexpected webhook deliveries
  • • Large or unusual payout requests

Set Up Alerts

  • • Failed payout notifications
  • • Rate limit approaching threshold
  • • Invalid webhook signatures
  • • Unexpected invoice statuses

Compliance & Data Protection

Data Retention

FromChain retains transaction data for 7 years for compliance purposes. Invoice metadata is stored as provided.

PII Handling

Avoid storing personally identifiable information (PII) in invoice metadata or descriptions unless necessary for your business.

KYC/AML Compliance

Implement your own KYC/AML procedures. FromChain provides the payment infrastructure, but compliance with local regulations is your responsibility.

Security Checklist

Before Going to Production:

API keys stored in environment variables or secret manager
Webhook signature verification implemented
Webhook endpoint uses HTTPS with valid TLS certificate
Payout address validated and tested with small amount
Rate limiting handled with exponential backoff
Idempotency keys implemented for invoice creation
Error logging sanitized (no secrets exposed)
Monitoring and alerting configured
Tested with mainnet small amounts first