Signature Validation
Signature Validation adalah lapisan keamanan tambahan menggunakan HMAC-SHA256 untuk memverifikasi integritas dan keaslian setiap request. Fitur ini opsional dan dapat diaktifkan per merchant oleh admin.
Cara Kerja
1. timestamp = unix timestamp saat ini (integer, dalam detik)
2. body_string = JSON string persis dari request body
3. string_to_sign = body_string + ":" + timestamp
4. signature = Base64( HMAC-SHA256( secret_key, string_to_sign ) )
Catatan penting:
body_stringharus identik byte-for-byte dengan body yang dikirim dalam request. Jangan pretty-print atau ubah urutan field JSON.
Signature Secret
secret_key adalah nilai terpisah dari X-API-Key. Nilai ini:
- Tidak ditampilkan di CMS
- Diberikan langsung oleh tim support saat aktivasi fitur Signature Validation
- Harus disimpan dengan aman dan tidak pernah di-commit ke repositori
Validasi Timestamp
Sistem menolak request jika:
- Timestamp lebih dari 300 detik (5 menit) di masa lalu
- Timestamp lebih dari 60 detik di masa depan
Hal ini mencegah replay attack.
Implementasi per Bahasa
PHP
<?php
$secretKey = "signature_secret_dari_support";
$body = ['product_id' => 'XL5', 'cust_id' => '08123456789', 'req_id' => 'TXN-001'];
$bodyString = json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$timestamp = time();
$toSign = $bodyString . ":" . $timestamp;
$signature = base64_encode(hash_hmac('sha256', $toSign, $secretKey, true));
// Kirim dalam header
// X-Signature: $signature
// X-Timestamp: $timestamp
?>
Node.js
const crypto = require('crypto');
const secretKey = 'signature_secret_dari_support';
const body = { product_id: 'XL5', cust_id: '08123456789', req_id: 'TXN-001' };
const bodyString = JSON.stringify(body); // urutan key harus sama dengan yang dikirim
const timestamp = Math.floor(Date.now() / 1000);
const toSign = `${bodyString}:${timestamp}`;
const signature = crypto
.createHmac('sha256', secretKey)
.update(toSign)
.digest('base64');
Python
import hmac, hashlib, base64, time, json
secret_key = "signature_secret_dari_support"
body = {"product_id": "XL5", "cust_id": "08123456789", "req_id": "TXN-001"}
body_string = json.dumps(body, separators=(',', ':'), ensure_ascii=False)
timestamp = int(time.time())
to_sign = f"{body_string}:{timestamp}"
signature = base64.b64encode(
hmac.new(secret_key.encode(), to_sign.encode(), hashlib.sha256).digest()
).decode()
Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
String secretKey = "signature_secret_dari_support";
String bodyString = "{\"product_id\":\"XL5\",\"cust_id\":\"08123456789\",\"req_id\":\"TXN-001\"}";
long timestamp = System.currentTimeMillis() / 1000;
String toSign = bodyString + ":" + timestamp;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA256"));
String signature = Base64.getEncoder().encodeToString(mac.doFinal(toSign.getBytes("UTF-8")));
Go
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"time"
)
secretKey := "signature_secret_dari_support"
body := map[string]string{
"product_id": "XL5",
"cust_id": "08123456789",
"req_id": "TXN-001",
}
bodyBytes, _ := json.Marshal(body)
timestamp := time.Now().Unix()
toSign := fmt.Sprintf("%s:%d", string(bodyBytes), timestamp)
mac := hmac.New(sha256.New, []byte(secretKey))
mac.Write([]byte(toSign))
signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))
Rust
#![allow(unused)]
fn main() {
use hmac::{Hmac, Mac};
use sha2::Sha256;
use base64::{engine::general_purpose, Engine};
use std::time::{SystemTime, UNIX_EPOCH};
type HmacSha256 = Hmac<Sha256>;
let secret_key = "signature_secret_dari_support";
let body_string = r#"{"product_id":"XL5","cust_id":"08123456789","req_id":"TXN-001"}"#;
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let to_sign = format!("{}:{}", body_string, timestamp);
let mut mac = HmacSha256::new_from_slice(secret_key.as_bytes()).unwrap();
mac.update(to_sign.as_bytes());
let signature = general_purpose::STANDARD.encode(mac.finalize().into_bytes());
}
Tambahkan ke
Cargo.toml:hmac = "0.12",sha2 = "0.10",base64 = "0.22"
Contoh Request dengan Signature
POST /api/v1/merchant/disbursement/payment HTTP/1.1
Host: api-sandbox.alfakios.com
Content-Type: application/json
X-API-Key: apk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-Signature: uW8tV+R6zKpN1234ABCDabcd==
X-Timestamp: 1714435200
{"req_id":"PAY-001","product_id":"DSTF","account_number":"1234567890","amount":"100000"}
Response Error Signature
{
"success": false,
"message": "Invalid signature",
"error": "Signature validation failed"
}
HTTP Status: 401 Unauthorized