İçeriğe geç
LexerLangv0.1.0

Ara katmanlar

security_headers, use_cors, rate_limit, trust_proxy ve CSRF.

Uygulama seviyesinde bir kez yazılır, her isteğe uygulanır:

LexerLang
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 with

Güvenlik başlıkları#

LexerLang
security.headers({
    content_security_policy: "default-src 'self'",
    frame_options: "SAMEORIGIN",
    hsts_days: 180
})

Varsayılan olarak yazılanlar:

Metin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin

CSP 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#

LexerLang
$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ı#

LexerLang
$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#

LexerLang
$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ı.

LexerLang
# Formda
$doc.body.add(`
    <form method="POST" action="/gonder">
        ` + security.csrf.field() + `
        <button>Gönder</button>
    </form>
`)
LexerLang
# İşleyicide
if not security.csrf.check($req) then
    $res.status(403)
    $res.send("geçersiz istek")
    return
end if

check ö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.

LexerLang
$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:

LexerLang
$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.

LexerLang
$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.

Sırada#

Üretim.