feat: EKAP imzalama anahtari rotasyona girince kendini toparlasin
Anahtar sabit kodluydu (EKAP_R8FACT_KEY ile gecilebiliyordu). EKAP anahtari
donemsel degistiriyor; degistigi anda ihale havuzu tamamen karariyor ve birinin
elle yeni anahtari bulup env'e yazmasi gerekiyordu.
Artik kimlik dogrulama hatasindan sonra anahtar portalin kendi paketinden
kesfediliyor. Zincir canli EKAP'ta dogrulandi:
/ekap/search → <script src="main.<hash>.js">
main.<hash>.js → webpack chunk haritasi; 2076 ozel olarak "common" adiyla
yayimlaniyor: y=>(2076===y?"common":y)+"."+{...}
common.<hash>.js → r8fact:"<32 baytlik anahtar>"
Kesfedilen deger su anki sabit anahtarla birebir ayni cikti — zincir dogru.
- Kesif YALNIZCA yedek: normalde sabit/env anahtar kullanilir, kesif ancak
401/403/5xx sonrasi ve istek basina BIR KEZ denenir (gercek sunucu hatasinda
her denemede portalden uc dosya indirmeyelim)
- 5xx de tetikliyor: EKAP gecersiz anahtari bazen 401, bazen 500 ile bildiriyor
(imza cozulemeyince sifre cozme katmani patliyor); yalnizca 401'e bakmak
rotasyonun yarisini kacirirdi
- Basari 1 sa, basarisizlik 5 dk onbellekleniyor
- Anahtar 32 bayt degilse kullanilmiyor — sema degismis demektir, korlemesine
denemek yerine mevcut anahtarla devam edilir
- SPA fallback tuzagi: bilinmeyen yol HTTP 200 + index.html donduruyor; JS
beklenen isteklerde HTML gelirse hata veriliyor (sessiz basarisizlik olmasin)
Sahte rotasyon testi: bozuk anahtarla EKAP 500 dondu → kesif devreye girdi →
dogru anahtar bulundu → tekrar denendi → arama basarili (860 ms).
Bulgu kaynagi: saidsurucu/ihale-mcp taramasinda gorulen imza semasi ve
firatbadur/ihaletakip-api'nin belgeledigi rotasyon davranisi. Ilgili depo
lisanssiz oldugu icin kod kopyalanmadi, uygulama sifirdan yazildi.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+25
-3
@@ -11,12 +11,21 @@
|
||||
*/
|
||||
|
||||
import crypto from 'node:crypto';
|
||||
import { anahtarKesfet } from './ekap-key.mjs';
|
||||
|
||||
const BASE = 'https://ekapv2.kik.gov.tr';
|
||||
const DEFAULT_KEY = 'pfS7Xdn3YVkOzs3V79XUc91SD47mQD0g';
|
||||
|
||||
// EKAP anahtarı döndürdüğünde portalın kendi paketinden keşfedilen değer.
|
||||
// Yalnızca kimlik doğrulama hatasından sonra dolar; normalde null kalır.
|
||||
let kesfedilenAnahtar = null;
|
||||
|
||||
function aktifAnahtar() {
|
||||
return kesfedilenAnahtar || process.env.EKAP_R8FACT_KEY || DEFAULT_KEY;
|
||||
}
|
||||
|
||||
function aesKey() {
|
||||
return Buffer.from(process.env.EKAP_R8FACT_KEY || DEFAULT_KEY, 'utf8');
|
||||
return Buffer.from(aktifAnahtar(), 'utf8');
|
||||
}
|
||||
|
||||
function aesB64(plain, iv) {
|
||||
@@ -51,6 +60,7 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
export async function ekapPost(path, body, { retries = 2, timeout = 30000 } = {}) {
|
||||
let lastErr;
|
||||
let anahtarDenendi = false;
|
||||
for (let i = 0; i <= retries; i++) {
|
||||
try {
|
||||
const res = await fetch(BASE + path, {
|
||||
@@ -61,8 +71,20 @@ export async function ekapPost(path, body, { retries = 2, timeout = 30000 } = {}
|
||||
});
|
||||
const raw = await res.text();
|
||||
if (!res.ok) {
|
||||
// 401/1202 = timestamp kayması → yeni başlıkla tekrar dene
|
||||
if ((res.status === 401 || res.status >= 500) && i < retries) {
|
||||
// 401/1202 = timestamp kayması → yeni başlıkla tekrar dene.
|
||||
// Anahtar rotasyonu da buradan geçiyor ve EKAP bunu bazen 401,
|
||||
// bazen 500 ile bildiriyor (imza çözülemeyince şifre çözme katmanı
|
||||
// patlıyor) — bu yüzden 5xx de anahtar yenilemeyi tetikliyor.
|
||||
if ((res.status === 401 || res.status === 403 || res.status >= 500) && i < retries) {
|
||||
if (!anahtarDenendi) {
|
||||
anahtarDenendi = true; // istek başına bir kez: gerçek sunucu
|
||||
// hatasında her denemede portalden üç dosya indirmeyelim
|
||||
const yeni = await anahtarKesfet();
|
||||
if (yeni && yeni !== aktifAnahtar()) {
|
||||
console.warn('[ekap] imzalama anahtarı değişmiş — portalden yeni anahtar alındı');
|
||||
kesfedilenAnahtar = yeni;
|
||||
}
|
||||
}
|
||||
await sleep(500 * (i + 1));
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user