This document describes the security measures implemented in PayLink.
- Overview
- Authentication
- Webhook Security
- Input Validation
- Cryptographic Operations
- Secrets Management
- OWASP Compliance
PayLink implements defense-in-depth security with multiple layers of protection:
| Layer | Protection |
|---|---|
| Transport | HTTPS/TLS encryption |
| Authentication | API key validation |
| Authorization | Merchant-scoped access |
| Input | Validation and sanitization |
| Crypto | Constant-time operations |
| Logging | Sensitive data masking |
All API requests (except /health and /metrics) require authentication via API key:
Header: X-Api-Key: <merchant_api_key>
- API keys are stored as bcrypt hashes in the database
- Original keys are never stored
- Keys are validated using constant-time comparison
Recommended key rotation schedule:
- Production: Every 90 days
- Sandbox: As needed
Each provider uses specific signature verification:
Algorithm: SHA512
Input: order_id + status_code + gross_amount + ServerKey
Location: JSON body field "signature_key"
Implementation:
raw := orderID + statusCode + grossAmount + serverKey
expected := sha512.Sum512([]byte(raw))
valid := constantTimeCompare(hex.EncodeToString(expected[:]), signatureKey)Algorithm: Token comparison
Location: Header "x-callback-token"
Implementation:
callbackToken := r.Header.Get("x-callback-token")
valid := hmac.Equal([]byte(callbackToken), []byte(configuredToken))All signature comparisons use constant-time algorithms:
func constantTimeCompare(a, b string) bool {
if len(a) != len(b) {
return false
}
var result byte
for i := 0; i < len(a); i++ {
result |= a[i] ^ b[i]
}
return result == 0
}All incoming requests are validated:
| Field | Validation |
|---|---|
| merchant_id | UUID format |
| amount | Positive integer, max 999999999 |
| currency | ISO 4217, 3 characters |
| order_id | Alphanumeric, max 50 chars |
| provider | Enum: midtrans, xendit |
| Input | Sanitization |
|---|---|
| Order ID | Regex: ^[a-zA-Z0-9_-]+$ |
| External ID | Regex: ^[a-zA-Z0-9_-]+$ |
| Amount | Parsed as int64, validated range |
All JSON payloads are validated before parsing:
if !json.Valid(body) {
return fmt.Errorf("invalid JSON payload")
}PayLink uses Go's standard library for cryptographic operations:
| Reason | Explanation |
|---|---|
| Portability | No CGO dependencies required |
| Security | Well-audited standard library |
| Simplicity | No external build requirements |
| Performance | Sufficient for most workloads |
Used for general signature operations:
func ComputeHMAC(key, data string) string {
h := hmac.New(sha256.New, []byte(key))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}For high-throughput scenarios (>100k webhook verifications/second), an optional C++ module is available:
- Location:
crypto_cpp/ - Build: CMake + OpenSSL
- Integration: CGO wrapper
When to use C++:
- Batch signature verification
- High-frequency webhook processing
- CPU-bound crypto operations
All secrets are loaded from environment variables:
MIDTRANS_SERVER_KEY=your-server-key
XENDIT_API_KEY=your-api-key
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx| Environment | Recommendation |
|---|---|
| Development | .env file (gitignored) |
| CI/CD | GitHub Secrets |
| Production | Secret manager (Vault, AWS Secrets Manager) |
- Never commit secrets to git
- Never log secrets
- Never include secrets in error messages
- Never expose secrets in API responses
PayLink follows OWASP security guidelines:
- Parameterized SQL queries via database/sql
- Input validation before use
- No dynamic query construction
- API key required for all sensitive endpoints
- Keys stored as hashes
- Rate limiting recommended
- Secrets never logged
- API keys masked in responses
- HTTPS required in production
- Environment-based configuration
- No default credentials
- Secure defaults
- JSON-only API (no HTML rendering)
- Proper Content-Type headers
- JSON validation before parsing
- Type-safe unmarshaling
- Minimal dependencies
- Regular dependency audit (
go mod verify)
- Structured JSON logging
- Request/response logging
- Error tracking
- Metrics endpoint
Before deploying to production:
- All secrets loaded from environment/secret manager
- HTTPS enabled and enforced
- API key authentication active
- Rate limiting configured
- Input validation enabled
- Webhook signatures verified
- Logging configured (secrets masked)
- Monitoring/alerting set up
- Dependency audit completed
If you discover a security vulnerability, please report it via:
- Email: [email protected]
- Do NOT create public GitHub issues for security vulnerabilities
Document maintained by PayLink Security Team