Obligatoriska förfrågnings-headers
Varje förfrågan (förutom /v1/health) måste innehålla dessa fyra headers. Om någon saknas eller är ogiltig avvisar servern med HTTP 401.
| Header | Format | Description |
|---|---|---|
KH-Key | kh_live_[A-Z0-9]{32} | Publik nyckelidentifierare. Ingen hemlighet, kan förekomma i loggar. |
KH-Timestamp | Unix-sekunder (10 siffror) | Aktuell tidsstämpel. Toleransfönster +-300s. |
KH-Nonce | 22-44 base64url-tecken | Engångsvärde per förfrågan. Cachad i 600s, därefter återanvändbar. |
KH-Signature | 64 hex | HMAC-SHA256(secret, signing_string), hex-kodad. |
Bygga signing-strängen
Strängen som signeras byggs av fem komponenter, sammanfogade med radbrytning (\n).
signing_string = METHOD + "\n"
+ PATH + "\n"
+ TIMESTAMP + "\n"
+ NONCE + "\n"
+ SHA256_HEX(BODY)
signature = HEX( HMAC_SHA256(secret, signing_string) )
PATH innehåller den fullständiga förfrågningssökvägen inklusive query-string, men utan host eller fragment. BODY är rådatabytenas innehåll; för GET/DELETE-förfrågningar utan body är BODY en tom sträng vars SHA256 är den välkända konstanten e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.
Referensimplementationer
PHP:
$method = 'POST';
$path = '/v1/orders';
$body = json_encode(['product_id' => 42, 'billing_cycle' => 'monthly']);
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$signing = "{$method}\n{$path}\n{$ts}\n{$nonce}\n" . hash('sha256', $body);
$sig = hash_hmac('sha256', $signing, $KH_SECRET);
$ch = curl_init('https://www.kernelhost.com/cp/kh_reseller_api/v1/orders');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"KH-Key: {$KH_KEY}",
"KH-Timestamp: {$ts}",
"KH-Nonce: {$nonce}",
"KH-Signature: {$sig}",
'Idempotency-Key: ' . bin2hex(random_bytes(16)),
],
]);
$res = curl_exec($ch);
Node.js:
import crypto from 'crypto';
const method = 'POST';
const path = '/v1/orders';
const body = JSON.stringify({ product_id: 42, billing_cycle: 'monthly' });
const ts = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomBytes(16).toString('hex');
const bodyHash = crypto.createHash('sha256').update(body).digest('hex');
const signing = `${method}\n${path}\n${ts}\n${nonce}\n${bodyHash}`;
const sig = crypto.createHmac('sha256', KH_SECRET).update(signing).digest('hex');
const res = await fetch('https://www.kernelhost.com/cp/kh_reseller_api/v1/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'KH-Key': KH_KEY,
'KH-Timestamp': ts,
'KH-Nonce': nonce,
'KH-Signature': sig,
'Idempotency-Key': crypto.randomBytes(16).toString('hex'),
},
body,
});
Uppspelningsskydd
En framgångsrikt signerad förfrågan kan INTE skickas igen. Servern lagrar varje nonce i 600s i databasen, en andra förfrågan med samma nonce avvisas med replay_detected. På samma sätt avvisas en förfrågan vars tidsstämpel avviker mer än 300s från servertiden.
Behörighetsmodell
Varje nyckel har en uttrycklig behörighetslista. Rutter kontrollerar den krävda behörigheten, saknas den returneras HTTP 403 forbidden_scope. Skrivande och känsliga behörigheter måste aktiveras uttryckligen vid skapandet av nyckeln.
read:products,read:orders,read:services,read:billing,read:webhooksread:credentials(Läsning av tjänstens inloggningsuppgifter (rotlösenord, FTP, VNC). Genererar en extra granskningspost credentials.read per anrop.)write:orders(Lägga och betala beställningar.)write:services(Köra tjänsteåtgärder (start/stopp/omstart/ominstallation/terminera).)write:webhooks(Ställa in Webhook-URL.)

