Ara katmanlar
security_headers, use_cors, rate_limit, trust_proxy ve CSRF.
Uygulama seviyesinde bir kez yazılır, her isteğe uygulanır:
with $app
.trust_proxy(true)
.security_headers({content_security_policy: "default-src 'self'"})
.use_cors({origins: ["https://ornek.com"], credentials: true})
.rate_limit({limit: 60, window: 60, redis: $redis})
end withGüvenlik başlıkları#
security.headers({
content_security_policy: "default-src 'self'",
frame_options: "SAMEORIGIN",
hsts_days: 180
})Varsayılan olarak yazılanlar:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-originCSP ve HSTS istenmeden yazılmaz. Doğru CSP uygulamaya göre değişir; uydurma bir değer ya sayfayı bozar ya da hiçbir şey korumaz. HSTS ise HTTPS'e geçmemiş bir siteyi tarayıcı tarafında erişilemez kılar — ve geri alınması sürenin dolmasını beklemeyi gerektirir.
trust_proxy#
$app.trust_proxy(true)$req.ip varsayılan olarak proxy başlığını (X-Forwarded-For) okumaz. Okusaydı istemci kendi adresini uydurabilir ve IP'ye dayanan her sınır — hız sınırı dahil — anlamsız hale gelirdi.
trust_proxy(true) yalnızca gerçekten bir ters proxy arkasındaysanız açılmalıdır; o zaman başlığı sizin nginx'iniz yazıyordur.
Hız sınırı#
$app.rate_limit({limit: 60, window: 60, redis: $redis})Dakikada 60 istek. Sınır route'tan önce çalışır; aşılmışsa işleyici hiç çalışmaz.
Sayaç IP başınadır. redis verilmezse sayaç bellekte tutulur — tek süreçte çalışan bir uygulama için yeterli, birden fazla süreç varsa her biri kendi sayacını tutar ve gerçek sınır kat kat yükselir.
CORS#
$app.use_cors({origins: ["https://ornek.com"], credentials: true})credentials: true ile origins: ["*"] birlikte kullanılamaz; tarayıcılar bu bileşimi zaten reddeder. Kimlik taşıyan isteklerde izin verilen kaynakları tek tek yazmanız gerekir.
CSRF#
Token oturumda durur ve oturum boyunca değişmez — her istekte yenilenseydi aynı anda açık iki sekme birbirinin tokenini geçersiz kılardı.
# Formda
$doc.body.add(`
<form method="POST" action="/gonder">
` + security.csrf.field() + `
<button>Gönder</button>
</form>
`)# İşleyicide
if not security.csrf.check($req) then
$res.status(403)
$res.send("geçersiz istek")
return
end ifcheck önce _csrf form alanına, sonra X-CSRF-Token başlığına bakar: form gönderimleri ilkini, tarayıcıdan atılan istekler ikincisini kullanır. Karşılaştırma sabit süredir.
| Çağrı | Nereye |
|---|---|
security.csrf.token() | ham değer |
security.csrf.field() | form içine gizli alan |
security.csrf.meta() | belge başlığına meta etiketi |
security.csrf.check($req) | doğrulama |
Zengin metin#
sanitize_html yalnızca gerçekten HTML saklamak zorunda olduğunuz yer içindir — bir zengin metin editörünün çıktısı gibi.
$doc.body.add(security.sanitize_html($yazi.govde))İzin listesiyle çalışır: p b strong em i u s a img ul ol li h1-h4 code pre blockquote hr br. Bunun dışındaki her etiket metne döner, her öznitelik düşer. script ve style içerikleriyle birlikte silinir. href/src yalnızca http, https, mailto, tel ve göreceli adresleri kabul eder — javascript: ve data: birer kod çalıştırma yoludur.
SQL tarafı#
SQL için kaçışlama işlevi yok: değerler zaten parametre olarak gidiyor. Parametrelerin kapsamadığı tek yer tablo ve sütun adlarıdır:
$sutun = security.identifier($req.get("sirala"), ["ad", "fiyat", "stok"])
$db.query("SELECT * FROM urunler ORDER BY " + $sutun)identifier kaçışlamaz, izin listesinden seçer. Eşleşme yoksa listenin ilk öğesi döner; çağıran her durumda geçerli bir ad alır.
$db.query("SELECT * FROM urunler WHERE ad LIKE ?",
"%" + security.escape_like($arama) + "%")escape_like, % ve _ karakterlerini kaçışlar. Bu bir enjeksiyon değil ama kullanıcının yazdığı % bütün kayıtları getirirdi.