İçeriğe geç
academia.sh

Ders 01 / 15

Web Uygulaması Güvenlik Duvarı

Hazır bir kural kümesinin kurgu bir günlük istek kümesinde koşturulması ve üç sayının ayrı ayrı yazılması: durdurulan kötü istek, kaçan kötü istek ve yanlış yere kesilen meşru istek; puan eşiği düşürüldüğünde üçünün nasıl birlikte hareket ettiği, muafiyet yazmanın aynı üç sayıyı hangi bedelle geri getirdiği ve durdurulan hacmin ne kadarının daha ucuz bir aşamada kapatılabileceği.

İçindekiler

Önceki kurs kodu taradı, bileşenleri envanterledi ve yapı çıktısını imzaladı; bütün o denetimler dağıtımdan önce durdu. Uygulama çalışmaya başladığı andan itibaren onu neyin koruduğu ise hiç sorulmadı. Bu konu o soruyu istek katmanında yanıtlıyor ve ilk ders ölçünün kendisini kuruyor.

Çalışma zamanı, bir kalemi yakalamanın en pahalı olduğu aşamadır: kalem üretimdedir, isteği gerçek bir abone göndermektedir ve savunma karar vermek için milisaniyeye sahiptir. Bu yüzden “uygulama katmanı güvenlik duvarı konmalıdır” cümlesi tek başına karar taşımaz. Karar taşıyan şey üç sayıdır ve üçü aynı koşumdan okunmalıdır: kaç kötü istek durduruldu, kaç kötü istek geçti, kaç meşru istek yanlış yere kesildi. Üçüncüsü yazılmadığında ilk ikisi bir şey ölçmez.

Kural Kümesi Neye Bakıyor

Bölgesel ölçüm ağının abone portalı ile saha uygulaması aynı üretim ortamında duruyor. Önlerine bir uygulama katmanı güvenlik duvarı konuyor ve hazır bir kural kümesiyle başlatılıyor: her kural istekte belirli bir belirtiyi arar, eşleştiğinde bir puan yazar, isteğin toplam puanı eşiği aşarsa istek durdurulur. Aşağıdaki her şey node ile kurulan bir modeldir; gerçek trafik üretilmiyor, gerçek bir yük ya da atlatma dizgesi yazılmıyor.

  • US1. On bir istek sınıfı, günlük sayıları, beş uç ve on kural kurgusal ölçüm ağına aittir.
  • US2. Bir isteğin gerçek sınıfı (meşru ya da kötü) modelde bilinir. Üretimde bu sınıf ancak inceleme sonrası bilinir; modelde bilindiği varsayılıyor ki yanlış kesilen istek sayılabilsin.
  • US3. Bir sınıfın taşıdığı belirti kümesi sabittir; aynı sınıftaki her istek aynı puanı alır. Gerçekte puan istek başına dağılır, bu dağılım eşik seçimini yumuşatır, yönünü değiştirmez.
  • US4. Bir kural, aradığı etiketlerin hepsi varsa eşleşir. Belirti etiketleri bir söz dizimini değil, savunmanın gördüğü yüzeysel bir özelliği adlandırır.
  • US5. “Daha erken kapatma aşaması” sütunu her kötü sınıf için elle işaretlendi: kalem tasarımda mı, kodda mı kapatılabilirdi, yoksa hiçbir erken aşamada kapatılamaz mı.
// istek.mjs — bir gunluk istek kumesi ve hazir kural kumesi; model, gercek trafik degil
// [ad, gercek sinif, gunluk sayi, belirti etiketleri, uc, daha erken kapatma asamasi]
export const SINIF = [
  ['abone sorgu formu', 'mesru', 84000, [], '/portal/sorgu', '-'],
  ['saha uygulamasi toplu yukleme', 'mesru', 5400, ['uzun-govde', 'kodlanmis-alan'], '/saha/yukleme', '-'],
  ['rapor filtresi, serbest metin', 'mesru', 2600, ['tirnak', 'anahtar-sozcuk'], '/portal/rapor', '-'],
  ['fatura ekinde dosya adi', 'mesru', 900, ['yol-ayraci'], '/portal/fatura', '-'],
  ['ic izleme yoklamasi', 'mesru', 1440, ['arac-imzasi'], '/saglik', '-'],
  ['sorgu alaninda birlestirme', 'kotu', 320, ['tirnak', 'anahtar-sozcuk', 'yorum-imi'], '/portal/sorgu', 'kod'],
  ['form alaninda betik etiketi', 'kotu', 210, ['etiket-acilisi', 'olay-niteligi'], '/portal/sorgu', 'kod'],
  ['ek adinda dizin yukselme', 'kotu', 96, ['yol-ayraci', 'ust-dizin'], '/portal/fatura', 'kod'],
  ['ortu altinda tirnak dizisi', 'kotu', 260, ['tirnak', 'anahtar-sozcuk'], '/portal/rapor', 'kod'],
  ['bilesende yamasi cikmamis acik', 'kotu', 40, ['bicimsiz-govde'], '/saha/yukleme', 'yok'],
  ['is akisi disi toplu indirme', 'kotu', 1200, [], '/portal/rapor', 'tasarim'],
];

// [kural adi, aranan etiketler (hepsi bulunmali), puan]
export const KURAL = [
  ['tirnak ve anahtar sozcuk', ['tirnak', 'anahtar-sozcuk'], 3],
  ['yorum imi', ['yorum-imi'], 2],
  ['etiket acilisi', ['etiket-acilisi'], 3],
  ['olay niteligi', ['olay-niteligi'], 2],
  ['yol ayraci', ['yol-ayraci'], 2],
  ['ust dizin', ['ust-dizin'], 3],
  ['bicimsiz govde', ['bicimsiz-govde'], 4],
  ['bilinen arac imzasi', ['arac-imzasi'], 2],
  ['kodlanmis alan', ['kodlanmis-alan'], 2],
  ['uzun govde', ['uzun-govde'], 1],
];

// muaf = [uc, kural adi] ciftleri; o ucta o kural hic puan yazmaz
export const puan = (s, muaf = []) => KURAL.reduce((t, [ad, ara, p]) =>
  t + (ara.every((e) => s[3].includes(e)) && !muaf.some(([u, k]) => u === s[4] && k === ad) ? p : 0), 0);

export function olc(esik, muaf = []) {
  let yakalanan = 0, kacan = 0, yanlis = 0;
  for (const s of SINIF) {
    const durdu = puan(s, muaf) >= esik;
    if (s[1] === 'kotu') durdu ? (yakalanan += s[2]) : (kacan += s[2]);
    else if (durdu) yanlis += s[2];
  }
  return { yakalanan, kacan, yanlis };
}

Kural kümesinin hazır gelmesi, kuralların bu uygulamayı tanımadığı anlamına gelir. Kurallar söz dizimi düzeyinde bir yüzeye bakar ve o yüzey meşru işin de geçtiği bir yüzeydir: rapor filtresine serbest metin yazan bir abone ile sorgu alanına birleştirme deneyen bir istek aynı iki belirtiyi taşıyabilir. Ayrımı kural yapamaz, çünkü ayrım isteğin biçiminde değil niyetinde ve bağlamındadır; kuralın gördüğü tek şey biçimdir.

Üç Sayı Birlikte Hareket Eder

Eşik, kural kümesinin tek gerçek ayarıdır. Aşağıdaki koşumda kural kümesi, istek kümesi ve puanlar sabit kalıyor; değişen tek şey eşiktir.

// kosum.mjs — ayni gunluk kume, degisen tek sey esik
import { SINIF, KURAL, puan, olc } from './istek.mjs';

const s = (x, n) => String(x).padStart(n);
const kotu = SINIF.filter((k) => k[1] === 'kotu');
const T = (l) => l.reduce((t, k) => t + k[2], 0);
console.log(`${SINIF.length} istek sinifi, ${KURAL.length} kural; gunde ${T(SINIF)} istek` +
  ` (${T(kotu)} kotu / ${T(SINIF) - T(kotu)} mesru)\n`);
console.log(`${'istek sinifi'.padEnd(31)}${s('sinif', 6)}${s('gunluk', 7)}${s('puan', 5)}` +
  `${s('uc', 15)}${s('erken asama', 13)}`);
for (const k of SINIF)
  console.log(`${k[0].padEnd(31)}${s(k[1], 6)}${s(k[2], 7)}${s(puan(k), 5)}${s(k[4], 15)}${s(k[5], 13)}`);

console.log(`\n${'esik'.padEnd(6)}${s('yakalanan', 11)}${s('kacan', 8)}${s('yanlis kesilen', 16)}` +
  `${s('kesilen/yakalanan', 19)}`);
for (const e of [6, 5, 4, 3, 2]) {
  const r = olc(e);
  console.log(`${String(e).padEnd(6)}${s(r.yakalanan, 11)}${s(r.kacan, 8)}${s(r.yanlis, 16)}` +
    `${s(r.yakalanan ? (r.yanlis / r.yakalanan).toFixed(1) : '-', 19)}`);
}
11 istek sinifi, 10 kural; gunde 96466 istek (2126 kotu / 94340 mesru)

istek sinifi                    sinif gunluk puan             uc  erken asama
abone sorgu formu               mesru  84000    0  /portal/sorgu            -
saha uygulamasi toplu yukleme   mesru   5400    3  /saha/yukleme            -
rapor filtresi, serbest metin   mesru   2600    3  /portal/rapor            -
fatura ekinde dosya adi         mesru    900    2 /portal/fatura            -
ic izleme yoklamasi             mesru   1440    2        /saglik            -
sorgu alaninda birlestirme       kotu    320    5  /portal/sorgu          kod
form alaninda betik etiketi      kotu    210    5  /portal/sorgu          kod
ek adinda dizin yukselme         kotu     96    5 /portal/fatura          kod
ortu altinda tirnak dizisi       kotu    260    3  /portal/rapor          kod
bilesende yamasi cikmamis acik   kotu     40    4  /saha/yukleme          yok
is akisi disi toplu indirme      kotu   1200    0  /portal/rapor      tasarim

esik    yakalanan   kacan  yanlis kesilen  kesilen/yakalanan
6               0    2126               0                  -
5             626    1500               0                0.0
4             666    1460               0                0.0
3             926    1200            8000                8.6
2             926    1200           10340               11.2

Beş satır bir ayar eğrisidir ve her satırı ayrı okumak gerekir. Eşik altıda kural kümesi hiçbir şey durdurmuyor: hiçbir sınıfın toplam puanı altıya ulaşmıyor, yani kurulmuş ama etkisiz bir katman gündelik ölçümde sıfırdan ayırt edilemez. Eşik beşte üç sınıf durur ve altı yüz yirmi altı kötü istek kesilir; yanlış kesilen istek sıfırdır. Eşik dörde inince yalnız kırk istek daha eklenir ve yanlış kesilen yine sıfır kalır — dört, bu kümedeki karşılıksız ayardır.

Asıl karar üçüncü satırdadır. Eşiği üçe indirmek iki yüz altmış kötü isteği daha durdurur ve karşılığında sekiz bin meşru isteği keser. Oran son sütunda yazılı: durdurulan her kötü istek başına 8,6 kesilen meşru istek. Kesilenler de rastgele değildir; saha uygulamasının toplu yüklemesi ile rapor filtresine yazılan serbest metindir, yani sistemin iki gerçek işlevi. Eşik ikide ise durdurulan kötü istek hiç artmaz, kesilen istek iki bin üç yüz kırk artar: sıkılaştırma belirli bir noktadan sonra yalnızca meşru işi kesmeye başlar.

Kaçan sütunu bütün ayarlarda bin ikiden aşağı inmiyor. Bunun nedeni bir eşik seçimi değil, bir görme sınırıdır: iş akışı dışı toplu indirme hiçbir belirti taşımıyor. İstekler biçim olarak kusursuzdur, kimliği doğrulanmıştır, yetkisi vardır; yanlış olan tek şey hacim ve bağlamdır. Söz dizimine bakan bir kural kümesi bunu hiçbir eşikte göremez.

Muafiyet Yazmanın Bedeli

Eşiği üçte tutup yanlış kesmeyi ayrı ayrı kapatmak, üretimde en sık verilen yanıttır: kesilen uç ile onu kesen kural bir muafiyet listesine yazılır ve o uçta o kural puan yazmaz. Üç muafiyet yeterlidir — saha yükleme ucundaki iki kural ile rapor ucundaki tırnak kuralı.

// muafiyet.mjs — esik 3'te yanlis kesmeyi muafiyetle kapatmanin bedeli
import { SINIF, puan, olc } from './istek.mjs';

const s = (x, n) => String(x).padStart(n);
const MUAF = [['/saha/yukleme', 'uzun govde'], ['/saha/yukleme', 'kodlanmis alan'],
  ['/portal/rapor', 'tirnak ve anahtar sozcuk']];

const AYAR = [['esik 4, muafiyet yok', 4, []], ['esik 3, muafiyet yok', 3, []],
  ['esik 3, 3 muafiyet', 3, MUAF]];
console.log(`${'ayar'.padEnd(22)}${s('yakalanan', 11)}${s('kacan', 8)}${s('yanlis kesilen', 16)}${s('bakilan kalem', 15)}`);
for (const [ad, e, m] of AYAR) {
  const r = olc(e, m);
  console.log(`${ad.padEnd(22)}${s(r.yakalanan, 11)}${s(r.kacan, 8)}${s(r.yanlis, 16)}${s(10 + m.length, 15)}`);
}

console.log(`\nmuafiyetin ucta actigi alan:`);
for (const [u, k] of MUAF) {
  const gecen = SINIF.filter((x) => x[4] === u && x[1] === 'kotu' && puan(x) >= 3 && puan(x, MUAF) < 3);
  console.log(`  ${u.padEnd(15)}${k.padEnd(26)}${gecen.length ? `gecen kotu: ${gecen[0][0]} (${gecen[0][2]})` : 'gecen kotu yok'}`);
}
ayar                    yakalanan   kacan  yanlis kesilen  bakilan kalem
esik 4, muafiyet yok          666    1460               0             10
esik 3, muafiyet yok          926    1200            8000             10
esik 3, 3 muafiyet            666    1460               0             13

muafiyetin ucta actigi alan:
  /saha/yukleme  uzun govde                gecen kotu yok
  /saha/yukleme  kodlanmis alan            gecen kotu yok
  /portal/rapor  tirnak ve anahtar sozcuk  gecen kotu: ortu altinda tirnak dizisi (260)

Birinci ve üçüncü satır üç sayının üçünde de aynıdır: 666, 1.460, 0. Yani üç muafiyetle sıkılaştırılmış eşik, hiç muafiyet yazılmamış gevşek eşikle tam olarak aynı sonucu veriyor. Fark yalnız son sütundadır — bakılan kalem sayısı ondan on üçe çıkmıştır. Üç kalem daha, üç ayrı gerekçe, her uç değişikliğinde yeniden gözden geçirilecek üç satır.

Alt liste bunun nedenini veriyor. Muafiyet bir kuralı bir uçta kapatır ve o uçtan geçen her isteği kapsar. Rapor ucundaki tırnak muafiyeti meşru filtre metnini kurtarırken aynı uçtan geçen iki yüz altmış kötü isteği de birlikte kurtardı: üç puanlık kural o uçta artık hiç yazmıyor. Saha yükleme ucundaki iki muafiyet bu yan etkiyi üretmedi, çünkü o uçtaki tek kötü sınıf başka bir kuralla yakalanıyor. İkisi arasındaki fark önceden bilinmez; ancak muafiyetin kapsadığı istek kümesi sayıldığında görülür. Muafiyet listesi bu yüzden bir ayar değil, bir envanterdir: her satırın hangi uçta hangi kuralı susturduğu ve o uçtan başka nelerin geçtiği ayrıca izlenmediği sürece liste zamanla kural kümesinin kendisinden daha belirleyici olur.

Durdurulanın Nereye Ait Olduğu

İlk iki sütun bu katmanın ne yaptığını söylüyor. Üçüncü sütun kursun asıl sorusudur: durdurulan hacim, daha ucuz bir aşamada zaten kapatılabilir miydi.

// asama.mjs — durdurulan kalemin kaci daha ucuz bir asamada kapatilabilirdi
import { SINIF, puan } from './istek.mjs';

const s = (x, n) => String(x).padStart(n);
const kotu = SINIF.filter((k) => k[1] === 'kotu');
const ASAMA = ['kod', 'tasarim', 'yok'];

console.log(`${'esik'.padEnd(6)}${ASAMA.map((a) => s(a, 10)).join('')}${s('yalniz burada %', 17)}`);
for (const e of [5, 4, 3]) {
  const d = kotu.filter((k) => puan(k) >= e);
  const t = d.reduce((x, k) => x + k[2], 0);
  const pay = ASAMA.map((a) => d.filter((k) => k[5] === a).reduce((x, k) => x + k[2], 0));
  console.log(`${String(e).padEnd(6)}${pay.map((p) => s(p, 10)).join('')}` +
    `${s(t ? (100 * pay[2] / t).toFixed(1) : '-', 17)}`);
}

console.log(`\nhicbir esikte durdurulmayan kotu sinif:`);
for (const k of kotu.filter((x) => puan(x) < 2))
  console.log(`  ${k[0].padEnd(31)}${s(k[2], 7)}  belirti yok, erken asama: ${k[5]}`);
console.log(`\nyalniz calisma zamaninda kapatilabilen sinif:`);
for (const k of kotu.filter((x) => x[5] === 'yok'))
  console.log(`  ${k[0].padEnd(31)}${s(k[2], 7)}  puan ${puan(k)}, esik 4'te durur`);
esik         kod   tasarim       yok  yalniz burada %
5            626         0         0              0.0
4            626         0        40              6.0
3            886         0        40              4.3

hicbir esikte durdurulmayan kotu sinif:
  is akisi disi toplu indirme       1200  belirti yok, erken asama: tasarim

yalniz calisma zamaninda kapatilabilen sinif:
  bilesende yamasi cikmamis acik      40  puan 4, esik 4'te durur

Tablo katmanın gerçek payını gösteriyor. Eşik dörtte durdurulan altı yüz altmış altı isteğin altı yüz yirmi altısı — yüzde doksan dördü — kod aşamasında kapatılabilir sınıflardan geliyor: sorgu birleştirme parametreli sorguyla, betik etiketi çıktı kodlamasıyla, dizin yükselme yol doğrulamasıyla kapanır ve bunların üçü de bir defa yazılıp bütün istekler için geçerli olur. Bu katman aynı kalemleri her gün, her istekte yeniden yakalıyor.

Yüzde altı ise başka bir şey. Bileşende yaması çıkmamış bir açık için erken aşama yoktur: kalem ne tasarımda vardı ne kodda yazıldı, bağımlılık envanterinde göründüğü an da bir düzeltme sürümü mevcut değildi. O kırk isteği durduran tek şey bu katmandır ve kural kümesinin varlık nedeni tam olarak budur. Katmanın ölçüsü toplam durdurma sayısı değil, yalnız burada durdurulabilen sayıdır.

Alt liste ise ters yönü söylüyor. İş akışı dışı toplu indirme hiçbir eşikte durmuyor; bu kalem ne kurallarla ne eşikle yakalanır, çünkü yetkilendirme ve iş akışı kararıdır, yani tasarım aşamasına aittir. Kural kümesi bu kalemi hiç görmediği hâlde günlük gösterge tablosunda altı yüz altmış altı durdurma yazacaktır. Katmanın kurulu olması, kapsadığı kalem sınıfını genişletmez.

Özet

  • Bir çalışma zamanı savunması üç sayıyla ölçülür ve üçü aynı koşumdan okunur: durdurulan kötü istek, kaçan kötü istek, yanlış yere kesilen meşru istek. Üçüncüsü yazılmadığında ilk ikisi bir şey ölçmez.
  • Eşik 5’ten 4’e inince durdurulan istek 626’dan 666’ya çıktı ve yanlış kesilen sıfır kaldı; 3’e inmek 260 istek daha durdurup 8.000 meşru isteği kesti (yakalanan başına 8,6 kesilen), 2’ye inmek hiçbir kazanç eklemeden 2.340 kesme daha ekledi.
  • Eşik 3 ile üç muafiyet, eşik 4 ile hiç muafiyetin verdiği üç sayının aynısını verdi (666, 1.460, 0); tek fark bakılan kalemin 10’dan 13’e çıkmasıdır. Muafiyetlerden biri aynı uçtan geçen 260 kötü isteği de kapsadı.
  • Kaçan istek hiçbir eşikte 1.200’ün altına inmedi: iş akışı dışı toplu indirme hiçbir belirti taşımıyor. Söz dizimine bakan kural biçimi görür, bağlamı görmez.
  • Eşik 4’te durdurulan 666 isteğin 626’sı kod aşamasında bir kez kapatılabilecek sınıflardan geliyor. Yalnız çalışma zamanında kapatılabilen pay günde 40 istek, yani yüzde 6’dır — yaması çıkmamış bileşen açığı.

Sonraki Adım

Bu dersin kural kümesi tek bir isteğe bakıp karar verdi: belirti var mı, puan eşiği aşıyor mu. Kaçan bin iki yüz istek ise tam olarak bu yüzden kaçtı — hiçbiri tek başına yanlış değildi. İş akışı dışı toplu indirmenin yanlışlığı isteğin içinde değil, aynı istemciden gelen istek sayısında ve zamanlamasındadır. Bunu görmek için savunmanın bir isteği değil bir sayacı okuması gerekir: pencere başına kaç istek, hangi anahtara bağlı, hangi eşikle. Sonraki ders o sayacı üç ayrı pencere biçiminde koşturuyor ve aynı üç sayıyı orada soruyor — özellikle ikincisini, çünkü hız sınırının en pahalı yanı durdurduğu kötü istek değil, meşru tepe yükünden kestiği istektir.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat