Security Best Practices

Secure your Koywe integration

Security Best Practices

Essential security practices for production Koywe integrations.

API Credentials

Store Securely

Do:

  • Store in environment variables
  • Use secret management services (AWS Secrets Manager, HashiCorp Vault)
  • Rotate credentials periodically
  • Use different credentials for sandbox and production

Don’t:

  • Hardcode credentials in source code
  • Commit credentials to version control
  • Share credentials via email/chat
  • Use production credentials in development
Secure Storage
1// ✅ Good - Environment variables
2const API_KEY = process.env.KOYWE_API_KEY;
3const SECRET = process.env.KOYWE_SECRET;
4
5// ❌ Bad - Hardcoded
6const API_KEY = 'sk_live_abc123...'; // NEVER DO THIS

Environment Separation

EnvironmentPurposeCredentials
DevelopmentLocal developmentSandbox
StagingPre-production testingSandbox
ProductionLive applicationProduction

Webhook Security

1. Signature Verification

Always verify webhook signatures:

Signature Verification
1function verifyWebhookSignature(payload, signature, secret) {
2 const expectedSignature = crypto
3 .createHmac('sha256', secret)
4 .update(payload)
5 .digest('hex');
6
7 return signature === expectedSignature;
8}
9
10// In webhook handler
11if (!verifyWebhookSignature(req.body, req.headers['koywe-signature'], secret)) {
12 return res.status(401).send('Invalid signature');
13}

2. HTTPS Only

  • Webhook endpoints must use HTTPS
  • Obtain valid SSL certificate
  • Redirect HTTP to HTTPS

3. Validate Event Structure

1function validateWebhookEvent(event) {
2 if (!event.id || !event.type || !event.data) {
3 throw new Error('Invalid event structure');
4 }
5
6 if (!event.data.orderId) {
7 throw new Error('Missing orderId');
8 }
9
10 return true;
11}

Token Security

Token Management

Secure Token Handling
1class SecureTokenManager {
2 constructor(apiKey, secret) {
3 this.apiKey = apiKey;
4 this.secret = secret;
5 this.token = null;
6 this.tokenExpiry = null;
7 }
8
9 async getToken() {
10 // Check if token is still valid
11 if (this.token && this.tokenExpiry > Date.now() + (5 * 60 * 1000)) {
12 return this.token;
13 }
14
15 // Get new token
16 const response = await axios.post(
17 'https://api.koywe.com/api/v1/auth/sign-in',
18 {
19 apiKey: this.apiKey,
20 secret: this.secret
21 }
22 );
23
24 this.token = response.data.token;
25 this.tokenExpiry = Date.now() + (60 * 60 * 1000); // 1 hour
26
27 return this.token;
28 }
29
30 clearToken() {
31 this.token = null;
32 this.tokenExpiry = null;
33 }
34}
35
36// Usage
37const tokenManager = new SecureTokenManager(API_KEY, SECRET);
38const token = await tokenManager.getToken();

Token Transmission

  • Always use HTTPS for API requests
  • Include token in Authorization header (not URL)
  • Never log tokens
  • Clear tokens on logout
1// ✅ Good - Header
2axios.get(url, {
3 headers: { 'Authorization': `Bearer ${token}` }
4});
5
6// ❌ Bad - URL parameter
7axios.get(`${url}?token=${token}`); // NEVER DO THIS

Data Protection

PII (Personally Identifiable Information)

Sensitive Data:

  • Customer names
  • Email addresses
  • Phone numbers
  • Document numbers
  • Bank account numbers

Best Practices:

Encrypt at Rest
1const crypto = require('crypto');
2
3function encryptPII(data, key) {
4 const iv = crypto.randomBytes(16);
5 const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
6
7 let encrypted = cipher.update(data, 'utf8', 'hex');
8 encrypted += cipher.final('hex');
9
10 const authTag = cipher.getAuthTag();
11
12 return {
13 encrypted,
14 iv: iv.toString('hex'),
15 authTag: authTag.toString('hex')
16 };
17}
18
19// Store encrypted data
20const encryptedEmail = encryptPII(customer.email, ENCRYPTION_KEY);
21await db.save({ encryptedEmail });

Logging

Do:

  • Log request IDs, timestamps, statuses
  • Log errors and exceptions
  • Log business events

Don’t Log:

  • API credentials
  • Tokens
  • PII (emails, phone numbers, documents)
  • Bank account numbers
  • Full credit card numbers
1// ✅ Good logging
2console.log('Order created:', {
3 orderId: order.id,
4 type: order.type,
5 amount: order.amountIn,
6 currency: order.originCurrencySymbol
7});
8
9// ❌ Bad logging
10console.log('Order created:', {
11 orderId: order.id,
12 customerEmail: 'customer@example.com', // DON'T LOG PII
13 token: 'Bearer abc123...' // DON'T LOG TOKENS
14});

Network Security

Firewall Configuration

Recommended:

  • Whitelist Koywe API IPs
  • Restrict outbound traffic
  • Use VPC/private subnets
  • Enable DDoS protection

TLS/SSL

  • Use TLS 1.2 or higher
  • Verify SSL certificates
  • Enable certificate pinning (optional)
1const axios = require('axios');
2const https = require('https');
3
4// Enforce TLS 1.2+
5const agent = new https.Agent({
6 minVersion: 'TLSv1.2',
7 rejectUnauthorized: true
8});
9
10const api = axios.create({
11 httpsAgent: agent
12});

Input Validation

Validate All Inputs

Input Validation
1function validateOrderInput(input) {
2 // Amount
3 if (typeof input.amount !== 'number' || input.amount <= 0) {
4 throw new Error('Invalid amount');
5 }
6
7 // Currency
8 const validCurrencies = ['COP', 'BRL', 'MXN', 'CLP', 'USD'];
9 if (!validCurrencies.includes(input.currency)) {
10 throw new Error('Invalid currency');
11 }
12
13 // External ID
14 if (!/^[a-zA-Z0-9-_]+$/.test(input.externalId)) {
15 throw new Error('Invalid external ID format');
16 }
17
18 return true;
19}

Sanitize User Input

1function sanitizeDescription(description) {
2 // Remove potentially harmful characters
3 return description
4 .replace(/[<>]/g, '') // Remove < >
5 .replace(/javascript:/gi, '') // Remove javascript:
6 .trim()
7 .substring(0, 255); // Limit length
8}

Rate Limiting

Implement rate limiting to prevent abuse:

Rate Limiting
1const rateLimit = require('express-rate-limit');
2
3const apiLimiter = rateLimit({
4 windowMs: 15 * 60 * 1000, // 15 minutes
5 max: 100, // Limit each IP to 100 requests per windowMs
6 message: 'Too many requests, please try again later.'
7});
8
9// Apply to API routes
10app.use('/api/', apiLimiter);
11
12// Stricter limit for sensitive operations
13const authLimiter = rateLimit({
14 windowMs: 15 * 60 * 1000,
15 max: 5, // Only 5 auth attempts
16 skipSuccessfulRequests: true
17});
18
19app.post('/api/auth', authLimiter, authHandler);

Error Handling

Don’t Expose Internal Details

Safe Error Messages
1function handleError(error, res) {
2 // Log detailed error internally
3 console.error('Internal error:', {
4 stack: error.stack,
5 message: error.message,
6 timestamp: new Date()
7 });
8
9 // Return generic message to client
10 res.status(500).json({
11 error: 'An error occurred. Please try again or contact support.',
12 requestId: generateRequestId()
13 });
14
15 // ❌ Don't do this
16 // res.status(500).json({ error: error.stack }); // Exposes internals
17}

Compliance

PCI DSS

If handling card data:

  • Never store CVV
  • Tokenize card numbers
  • Use PCI-compliant infrastructure
  • Conduct regular security audits

GDPR/Data Protection

  • Obtain user consent for data storage
  • Provide data export functionality
  • Implement data deletion
  • Maintain audit logs
  • Encrypt PII

Monitoring and Alerts

Security Monitoring

Security Alerts
1function monitorSuspiciousActivity(request) {
2 // Multiple failed auth attempts
3 if (failedAttempts > 5) {
4 alertSecurityTeam('Multiple failed auth attempts', {
5 ip: request.ip,
6 attempts: failedAttempts
7 });
8 }
9
10 // Unusual payment amounts
11 if (amount > THRESHOLD) {
12 alertSecurityTeam('High-value transaction', {
13 orderId: order.id,
14 amount: amount,
15 currency: currency
16 });
17 }
18
19 // Geographic anomalies
20 if (isUnusualLocation(request.ip)) {
21 alertSecurityTeam('Unusual location', {
22 ip: request.ip,
23 location: getLocation(request.ip)
24 });
25 }
26}

Audit Logging

1function auditLog(action, details) {
2 await db.auditLogs.insert({
3 timestamp: new Date(),
4 action: action,
5 userId: details.userId,
6 resource: details.resource,
7 changes: details.changes,
8 ip: details.ip,
9 userAgent: details.userAgent
10 });
11}
12
13// Usage
14await auditLog('order.created', {
15 userId: user.id,
16 resource: `order:${order.id}`,
17 changes: { amount: order.amountIn, currency: order.currency },
18 ip: req.ip,
19 userAgent: req.headers['user-agent']
20});

Security Checklist

Production Checklist:

  • API credentials stored in environment variables
  • Different credentials for sandbox/production
  • HTTPS enforced on all endpoints
  • Webhook signature verification implemented
  • Token management with expiry handling
  • PII encrypted at rest
  • Sensitive data not logged
  • Input validation on all user inputs
  • Rate limiting configured
  • Error messages don’t expose internals
  • Security monitoring and alerts setup
  • Audit logging implemented
  • Regular security audits scheduled
  • Incident response plan documented

Next Steps