API ve webhook
CPalius, modüllerin dış dünyaya iki yolla açılmasını sağlar: REST API (dış sistem sizden okur/yazar) ve webhook (siz dış sisteme haber verirsiniz ya da dış sistem size haber verir).
REST API
Tüm API istekleri tek bir geçit üzerinden geçer: /api/*. Bir servis metoduna #[CpApi] eklemek onu uç yapar; denetleyici yazmanız gerekmez.
final class PostsApi
{
#[CpApi(path: '/blog/posts/{id}', methods: ['GET'], public: false, capability: 'blog.api.read')]
public function show(int $id): array
{
return ['id' => $id, 'title' => '…'];
}
#[CpApi(path: '/blog/posts', methods: ['GET'], public: true)]
public function list(): array { /* herkese açık liste */ }
}
| Parametre | Anlamı |
|---|---|
path |
/api sonrası yol (/blog/posts/{id}). /api önekini yazmayın |
methods |
İzin verilen HTTP metotları (varsayılan ['GET']) |
public |
true ise anahtar istenmez (varsayılan false) |
capability |
Gerekli yetenek. Yoldaki {ad} belirteçleri yerine konur; özel uçta boş bırakmak 403 verir (güvenli varsayılan) |
Bir uç hata verirse geçit 500 JSON döndürür ve hatayı module_quarantine.log dosyasına yazar; geçidin kendisi çökmez.
Özel uçlar: API anahtarı
Özel uçlar X-CP-API-KEY başlığı ister. Fail-closed çalışır: geçerli anahtar ve çözümlenmiş yetenek olmadan istek reddedilir.
curl -H "X-CP-API-KEY: cpk_…" https://site.example/api/blog/posts/12
Anahtarlar AACP → Otomasyon → API anahtarları ekranından oluşturulur (yetki: system.api.manage). Anahtarın özellikleri:
- Etiket ve yetenek listesi: anahtar yalnızca verilen yetenekleri kullanabilir.
*joker asla geçerli bir anahtar yetkisi değildir. - IP izin listesi (isteğe bağlı): doluysa yalnızca bu istemci IP'leri kullanabilir. IP, güvenilir vekil sunucular dikkate alınarak (
getClientIp) bulunur; hamX-Forwarded-Forkullanılmaz. - Kiracı (isteğe bağlı): çok kiracılı kurulumda anahtarı bir kiracıya bağlar.
- Aç/kapa: silmeden devre dışı bırakılabilir.
Veritabanında yalnızca anahtarın SHA-256 özeti ve son 4 karakteri saklanır. Düz anahtar oluşturulduğu anda bir kez gösterilir; kaybederseniz yeni üretin.
Oran sınırı
Sabit pencereli (60 sn) sınırlayıcı:
| Kova | Sınır |
|---|---|
| Anahtarlı istek | dakikada 60 |
| Anonim (public uç) | dakikada 20 |
| Gelen hook'lar | dakikada 30 |
Önbellek hatasında sınırlayıcı reddeder (fail-closed).
Idempotency
Değiştiren metotlar (POST, PUT, PATCH, DELETE) için istemci bir Idempotency-Key başlığı gönderebilir. Aynı anahtarla gelen tekrar istek, ikinci kez işlem yapmak yerine ilk yanıtı döndürür (24 saat). Aynı anahtar farklı bir gövdeyle gelirse 409 döner. Ağ kopması sonrası güvenle yeniden deneme içindir.
Erişim kaydı
Her API erişimi (eylem, anahtar, yol, durum, süre ms, istemci IP) denetim kaydına eklenir; düz anahtar asla yazılmaz.
Giden webhook'lar
Bir olay olduğunda dış bir URL'ye imzalı bir POST gönderin. AACP → Otomasyon → Webhook'lar ekranından (yetki: system.webhooks.manage) abonelik oluşturulur: etiket, URL ve olay listesi (en çok 32; olay adı modul.olay biçiminde).
Gizli anahtar
Abonelik oluşturulurken ya da döndürülürken whsec_ önekli bir imza gizli anahtarı üretilir; yalnızca o anda gösterilir. Saklanan şey şifrelenmiş halidir ve son 4 karakterdir.
Zarf ve imza
Gövde sabit bir zarftır; X-CP-Webhook-Signature başlığı zaman damgalı bir HMAC taşır. Alıcı tarafta doğrulama:
$ok = $signer->verify($request->headers->get('X-CP-Webhook-Signature'), $rawBody, $secret, time());
// 300 saniyeden eski zaman damgası reddedilir (tekrar saldırısına karşı)
Ham gövde üzerinde doğrulama yapın; JSON'u yeniden serileştirip imzalamayın.
Teslimat
WebhookEventEmitter::emit() yalnızca kuyruğa koyar; HTTP isteği çağıran süreçte asla yapılmaz. Gerçek gönderimi kuyruk işçisi yapar, 5 saniye zaman aşımıyla, yanıtın yalnızca ilk 2 KB'ını okuyarak. Başarısız teslimat geri çekilmeyle tekrar denenir. Bkz. Hook, cron, kuyruk.
$this->events->emit('shop.order.paid', ['order_id' => 42], resource: 'order');
SSRF koruması
Webhook, saldırganın sunucuyu iç ağa istek attırması için bir araç olabilir. Bu yüzden iki bağımsız katman vardır:
- Yazma anında: URL kaydedilirken (
SsrfGuard) şema, kimlik bilgisi, ana makine ve çözülen IP denetlenir. Yalnızca https; özel, link-local ve bulut metadata adresleri reddedilir. - Gönderim anında: DNS iki an arasında değişebilir (DNS rebinding), bu yüzden istemci çözümlemeyi yeniden doğrular.
Gelen webhook'lar
Dış bir sistem (ödeme sağlayıcı, CI) size haber verecekse modül bir gelen uç tanımlar:
final class StripeInbound implements InboundWebhookHandlerInterface
{
public function endpointId(): string { return 'stripe'; }
public function handle(array $payload): void
{
// İmza ÇEKİRDEKTE doğrulandı; bu kod doğrulanmış yük alır.
}
}
Uç, /hooks/<endpoint> altında yayınlanır. Çekirdek modül kodu çalışmadan önce imzayı doğrular, gövdeyi 64 KB ile sınırlar, oran sınırını uygular ve işi kuyruğa atar; işleyici kuyruk işçisinde çalışır. Böylece yavaş ya da hatalı bir işleyici gönderen sistemi bekletmez.
Güvenlik özeti
| Risk | Önlem |
|---|---|
| Anahtar sızıntısı | Yalnızca özet saklanır; yetenek kısıtı; IP izin listesi; aç/kapa |
| Yetki yükselmesi | * joker yasak; boş yetenek = 403 |
| Aşırı istek | Oran sınırı, fail-closed |
| Çift işlem | Idempotency-Key, 24 saat |
| SSRF | Yazmada ve gönderimde ayrı kontrol; yalnız https |
| Sahte webhook | HMAC + zaman damgası toleransı |
| Yavaş işleyici | Kuyruk; HTTP iş parçacığında teslimat yok |