---
title: 'İş Mantığı Açıkları'
source: 'https://academia.sh/tr/kurslar/guvenli-kodlama/is-mantigi-aciklari'
course: 'Güvenli Kodlama'
language: tr
updated: '2026-08-17T18:06:42+00:00'
license: 'CC BY-SA 4.0'
---

# İş Mantığı Açıkları

İş mantığı açığının akış sırası olarak modellenmesi: beş adımlı bir itiraz ve iade akışında dört adımlık altı yüz yirmi beş yolun beş yüz biri adım başına yetkilendirmede kural dışı sonuç verir, üç yüz altmış dokuzu ödeme yapar ve yüz on üçü kotayı aşar; durum makinesi bu sayıları sıfıra indirip ödeme yapan yolu bire düşürür, geçişi bildirilmemiş altıncı adım eklendiğinde adım başına kurulumda kural dışı yol bin yüz yetmiş ikiye çıkarken durum makinesinde sıfır kalır ve dizi uzunluğu büyüdükçe kural dışı yolların payı yüzde altmıştan yüzde doksana yükselir.

Önceki ders yetki sorusunu tek bir istek üzerinde sordu ve üç sayı verdi: kaç uç nesne
düzeyinde denetim yapıyor, kaç istek yanlış geçiyor, merkezî kapı kaçını kapatıyor. O
ölçümün tamamı tek istek varsayımına dayanıyordu. Bir isteğin tek başına doğru olması ise
bir kuralın korunduğu anlamına gelmez.

Bir sistemin kurallarının bir bölümü tek isteklerde değil, **istek dizilerinde** yaşar. Bir
iade bir kez ödenir, bir indirim bir dönemde bir kez uygulanır, bir onay ödemeden önce
gelir. Bu cümlelerin hiçbiri tek bir ucun içine yazılamaz; hepsi adımların **sırası** ve
**tekrarı** hakkındadır. **İş mantığı açığı** bu kuralların denetlenmediği yerde doğar:
her adım tek tek yetkilidir, dizinin kendisi yetkisizdir.

## Kural Adımların Sırasında

Ölçüm için bir akış adım kümesine indirgenir. Her adımın iki özelliği yazılır: etkisi
(ödeme, indirim) ve durum makinesinde hangi önceki durumlardan sonra gelebileceği. İkinci
alan boş bırakılırsa adım hiçbir durumda çalışamaz — bu, önceki dersteki öntanımlı retin
akış karşılığıdır.

İki kurulum karşılaştırılır. **Adım başına yetkilendirme** her adımı kendi başına
yetkilendirir ve sırayı hiç sormaz; bu, uç başına yetki denetimi yapan bir servisin
davranışıdır. **Durum makinesi** her adımdan önce mevcut durumu sınar ve izinli olmayan
adımı çalıştırmaz.

Aşağıdaki akış bir **modeldir**: gerçek bir ödeme, gerçek bir tutar ve gerçek bir para
birimi yoktur; tutarlar kurgu birimdir.

```js
// akis/model.mjs — abone portalinin fatura itiraz ve iade akisi (model): gercek bir odeme,
// gercek bir tutar ve gercek bir para birimi yok. Tutarlar kurgu birimdir.

// KH13: her adim iki sey tasir — etkisi (odeme, indirim) ve durum makinesinde hangi onceki
// durumlardan sonra gelebilecegi. `izin` bos ise adim hicbir durumda calisamaz.
export const ETKI = {
  "basvur":         { izin: ["yok"],                     sonra: "basvuruldu" },
  "incele":         { izin: ["basvuruldu"],              sonra: "incelendi" },
  "onayla":         { izin: ["incelendi"],               sonra: "onaylandi" },
  "iade-ode":       { izin: ["onaylandi"],               sonra: "kapandi", odeme: 100 },
  "indirim-uygula": { izin: ["basvuruldu", "incelendi"], sonra: null,      indirim: 1 },
};
export const ADIM = Object.keys(ETKI);
export const KOTA = 1;                       // indirim kotasi: donem basina bir kez

// KH14: iki kurulum ayni adim dizisini kosar. "adim-basina" kurulumunda her adim kendi
// yetkisini tasir ve sira sorulmaz; "durum-makinesi" kurulumunda izinli olmayan adim atlanir.
export function kosu(dizi, kurulum, etki = ETKI) {
  const d = { durum: "yok", odeme: 0, indirim: 0 };
  for (const ad of dizi) {
    const e = etki[ad];
    if (kurulum === "durum-makinesi") {
      if (!e.izin.includes(d.durum)) continue;
      if (e.indirim && d.indirim >= KOTA) continue;
    }
    d.odeme += e.odeme ?? 0;
    d.indirim += e.indirim ?? 0;
    if (e.sonra) d.durum = e.sonra;
  }
  return d;
}

// KH15: yol kumesi, verilen adim kumesinden uretilen sabit uzunluktaki butun dizilerdir.
export function diziler(adimlar, uzunluk) {
  let l = [[]];
  for (let i = 0; i < uzunluk; i++) l = l.flatMap((d) => adimlar.map((a) => [...d, a]));
  return l;
}
```

```js
// akis/olcum.mjs — adimlarin atlanabildigi ya da tekrarlanabildigi kac yol var, durum
// makinesi kacini kapatiyor, kaci disarida kaliyor.
import { ETKI, ADIM, KOTA, kosu, diziler } from "./model.mjs";
const yaz = (g, ...s) => console.log(s.map((v, i) =>
  (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join(""));
const KOD = { b: "basvur", i: "incele", o: "onayla", d: "iade-ode", n: "indirim-uygula" };

const A = [-10, 10, 12, 12, 14];
console.log("1. dort adimlik ornek diziler (b=basvur i=incele o=onayla d=iade-ode n=indirim)");
yaz(A, "dizi", "AB odeme", "AB indirim", "DM odeme", "DM indirim");
for (const kisa of ["biod", "bdio", "bidd", "bddd", "bnnn"]) {
  const dizi = [...kisa].map((c) => KOD[c]);
  const a = kosu(dizi, "adim-basina"), m = kosu(dizi, "durum-makinesi");
  yaz(A, [...kisa].join(" "), a.odeme, a.indirim, m.odeme, m.indirim);
}

// KH16: bir yol, adim-basina kurulumda durum makinesinden daha comert bir sonuc veriyorsa
// kural disidir. Asagidaki sayim butun yollari gercekten kosturur.
const UZUNLUK = 4;
const olc = (adimlar, etki, uzunluk = UZUNLUK) => {
  const yol = diziler(adimlar, uzunluk);
  const s = { yol: yol.length, odeyen: [0, 0], fazla: [0, 0], kota: [0, 0],
    disi: [0, 0], ayni: 0 };
  for (const d of yol) {
    const k = [kosu(d, "adim-basina", etki), kosu(d, "durum-makinesi", etki)];
    for (let i = 0; i < 2; i++) {
      if (k[i].odeme > 0) s.odeyen[i]++;
      if (k[i].odeme > 100) s.fazla[i]++;
      if (k[i].indirim > KOTA) s.kota[i]++;
      if (k[i].odeme > k[1].odeme || k[i].indirim > k[1].indirim) s.disi[i]++;
    }
    if (k[0].odeme === k[1].odeme && k[0].indirim === k[1].indirim) s.ayni++;
  }
  return s;
};
const t = olc(ADIM, ETKI);
const B = [-34, 16, 16];
console.log(`\n2. uzunluk ${UZUNLUK} dizilerin tamami (${ADIM.length} adim, ${t.yol} yol)`);
yaz(B, "olcum", "adim-basina", "durum-makinesi");
for (const [ad, alan] of [["odeme yapan yol", "odeyen"], ["100 biriminden cok odeyen", "fazla"],
  ["indirim kotasini asan", "kota"], ["kural disi sonuc veren", "disi"]])
  yaz(B, ad, t[alan][0], t[alan][1]);
console.log(`iki kurulumun ayni sonucu verdigi yol: ${t.ayni} / ${t.yol}` +
  ` (%${((t.ayni / t.yol) * 100).toFixed(1)})`);

// 3. bildirilmemis yeni adim: gecisi tanimlanmamis oldugu icin durum makinesi onu calistirmaz
const GENIS = { ...ETKI, "ek-odeme": { izin: [], sonra: null, odeme: 100 } };
const g = olc(Object.keys(GENIS), GENIS);
const C = [-28, 7, 8, 14, 13];
console.log("\n3. gecisi bildirilmemis yeni adim (ek-odeme) eklendiginde");
yaz(C, "kurulum", "adim", "yol", "odeme yapan", "kural disi");
for (const [ad, s, i, n] of [["adim-basina", t, 0, 5], ["durum-makinesi", t, 1, 5],
  ["adim-basina, yeni adim", g, 0, 6], ["durum-makinesi, yeni adim", g, 1, 6]])
  yaz(C, ad, n, s.yol, s.odeyen[i], s.disi[i]);

// 4. dizi uzunlugu buyudukce yol kumesi ve kural disi yol sayisi
const D = [-9, 9, 16, 18, 16];
console.log(`\n4. dizi uzunlugu buyudukce (${ADIM.length} adim)`);
yaz(D, "uzunluk", "yol", "AB kural disi", "AB kural disi %", "DM kural disi");
for (let u = 2; u <= 6; u++) {
  const s = olc(ADIM, ETKI, u);
  yaz(D, u, s.yol, s.disi[0], ((s.disi[0] / s.yol) * 100).toFixed(1), s.disi[1]);
}
```

```
1. dort adimlik ornek diziler (b=basvur i=incele o=onayla d=iade-ode n=indirim)
dizi        AB odeme  AB indirim    DM odeme    DM indirim
b i o d          100           0         100             0
b d i o          100           0           0             0
b i d d          200           0           0             0
b d d d          300           0           0             0
b n n n            0           3           0             1

2. uzunluk 4 dizilerin tamami (5 adim, 625 yol)
olcum                                  adim-basina  durum-makinesi
odeme yapan yol                                369               1
100 biriminden cok odeyen                      113               0
indirim kotasini asan                          113               0
kural disi sonuc veren                         501               0
iki kurulumun ayni sonucu verdigi yol: 124 / 625 (%19.8)

3. gecisi bildirilmemis yeni adim (ek-odeme) eklendiginde
kurulum                        adim     yol   odeme yapan   kural disi
adim-basina                       5     625           369          501
durum-makinesi                    5     625             1            0
adim-basina, yeni adim            6    1296          1040         1172
durum-makinesi, yeni adim         6    1296             1            0

4. dizi uzunlugu buyudukce (5 adim)
uzunluk        yol   AB kural disi   AB kural disi %   DM kural disi
2               25              15              60.0               0
3              125              90              72.0               0
4              625             501              80.2               0
5             3125            2686              86.0               0
6            15625           14075              90.1               0
```

Sayılar **ölçüm** sınıfındadır; girdileri KH13–KH16'dır.

## Beş Dizi, Üç Kusur Türü

Birinci tablo beş örnek diziyi iki kurulumda koşturuyor. İlk satır beklenen akıştır:
başvuru, inceleme, onay, ödeme. İki kurulum da 100 birim ödüyor.

İkinci satır **adım atlamadır**: ödeme, onaydan önce geliyor. Adım başına yetkilendirmede
100 birim ödeniyor, çünkü ödeme adımının kendisi yetkilidir ve önündeki adımların
tamamlanıp tamamlanmadığı sorulmuyor. Durum makinesinde aynı dizi 0 ödüyor.

Üçüncü ve dördüncü satırlar **adım tekrarıdır**: aynı ödeme adımı iki ve üç kez geçiyor,
sonuç 200 ve 300 birim. Bu iki dizide ödeme adımı her seferinde tek başına yetkilidir;
eksik olan, adımın **bir kez** çalışabileceği bilgisidir.

Beşinci satır **kota aşımıdır**. İndirim adımı üç kez uygulanıyor ve adım başına
yetkilendirmede indirim 3'e çıkıyor. Durum makinesinde 1'de kalıyor, çünkü kota da bir durum
bilgisidir: indirim uygulandıktan sonra ikinci uygulama izinli değildir.

Üç kusur türünün ortak yanı, hiçbirinin yetki denetimini kırmamasıdır. Her adım doğru rolle,
doğru nesne üzerinde, doğru ölçütle çağrıldı. Kırılan şey akışın kendisidir.

Bu, önceki dersteki ölçümün niçin yetmediğini de gösteriyor. Yetki matrisi on dört ucun
beşinde eksik ölçüt buldu; burada bütün ölçütler yerinde olsa bile 501 yol kural dışı sonuç
veriyor. İki ölçüm birbirinin yerine geçmez: biri **kimin hangi nesneye** dokunabildiğini,
öteki **hangi sırayla** dokunabildiğini denetler. Bir uç için "yetki denetimi tamam" demek,
o ucun bir dizinin ortasında çağrılmasına karşı hiçbir şey söylemez.

## Altı Yüz Yirmi Beş Yolun Beş Yüz Biri

İkinci tablo bütün yol kümesini sayar. Beş adımdan üretilen dört adımlık dizi sayısı
**625**'tir ve her biri iki kurulumda gerçekten koşturulur.

Yol kümesinin sabit uzunlukta üretilmesi bilinçli bir seçimdir. Gerçek bir istemci istediği
adımı istediği sayıda çağırabilir; dört adımlık pencere, bu serbestliğin sayılabilir bir
kesitidir. Kesit büyütüldüğünde sonuç değişmez, yalnız sayılar büyür — dördüncü tablo bunu
gösteriyor. Ölçümün geçerliliği pencerenin genişliğine değil, aynı pencerenin iki kurulumda
da kullanılmasına dayanır.

Adım başına yetkilendirmede **369 yol** ödeme yapıyor; durum makinesinde **1**. Aradaki fark
368 yoldur ve hepsi onaysız ödemedir. **113 yol** 100 birimden çok ödüyor — bunlar ödeme
adımının iki ya da üç kez geçtiği dizilerdir. Aynı sayı indirim kotası için de çıkıyor: 113
yol kotayı aşıyor.

Toplam sayı son satırdadır: **501 yol** durum makinesinden daha cömert bir sonuç veriyor —
yol kümesinin %80,2'si. İki kurulumun aynı sonucu verdiği yol yalnız 124, yani %19,8. Bu
oran, sırayı denetlemeyen bir akışta "doğru davranan" yolların azınlıkta olduğunu söylüyor:
kural dışı sonuç istisna değil, çoğunluktur.

Sayının bu kadar büyük olması adım kümesinin küçüklüğüne karşın şaşırtıcı değildir. Yol
sayısı adım sayısının uzunluk kuvvetidir; beş adım ve dört uzunluk 625 verir, altı adım ve
dört uzunluk 1.296. Akışa her yeni adım eklendiğinde denetlenmesi gereken dizi sayısı
katlanarak artar. Bu yüzden kural dizi başına yazılamaz; **durum başına** yazılmalıdır.

Dördüncü tablo bu büyümeyi ölçüyor. Dizi uzunluğu 2'den 6'ya çıktığında yol sayısı 25'ten
15.625'e, adım başına kurulumda kural dışı yol 15'ten 14.075'e çıkıyor. Asıl bilgi son
sütunda değil, oran sütununda: kural dışı yolların payı %60,0'tan **%90,1'e** yükseliyor.
Dizi uzandıkça doğru davranan yolların payı düşer, çünkü doğru sıra tektir ve yanlış
sıraların sayısı uzunlukla birlikte katlanır. Durum makinesi sütunu beş satırın beşinde de
sıfırdır — geçiş tablosu dizi uzunluğundan bağımsızdır.

Bu, sırayı sınama yoluyla korumanın niçin yetmediğini de söylüyor. Dört adımlık dizilerin
tamamını sınayan bir sınama kümesi 625 durum içerir ve beş adımda 3.125'e çıkar; beş satırlık
bir geçiş tablosu ise değişmez. Kural dışı yol sayısını sıfırda tutan şey sınama sayısı
değil, kararın yerleştiği yerdir.

## Durum Makinesi Ne Kapatıyor, Neye Mal Oluyor

Durum makinesi 625 yolun 624'ünü kapatır ve kural dışı yol sayısını sıfıra indirir. Kapatma
mekanizması bir denetim listesi değildir: her adımın izinli önceki durumları yazılıdır ve
bu listede olmayan her geçiş reddedilir. Beş satırlık bir tablo, 501 kural dışı yolu birden
kapatır.

Üçüncü tablo yeni adım sorusunu yanıtlar. Geçişi bildirilmemiş altıncı bir adım —
`ek-odeme` — eklendiğinde adım başına kurulumda yol sayısı 1.296'ya, ödeme yapan yol
1.040'a, kural dışı yol **1.172**'ye çıkıyor. Durum makinesinde aynı adım hiçbir yol açmıyor:
ödeme yapan yol 1'de, kural dışı yol 0'da kalıyor. `izin` listesi boş olduğu için adım hiçbir
durumdan sonra çalışmıyor.

Bedeli de buradadır ve yazılmalıdır. Bildirilmemiş adım **hiç çalışmaz**; yeni özellik
geçiş tablosuna eklenene kadar ölü kalır. Aynı bedel meşru esneklikte de görülür: indirim
adımı yalnız `basvuruldu` ve `incelendi` durumlarında izinlidir, dolayısıyla onaydan sonra
indirim uygulamak isteyen meşru bir iş kabul edilmez. Bu isteği karşılamak için tabloya bir
satır eklemek gerekir ve o satır bilinçli bir karardır — kod incelemesinde görülebilir,
sınanabilir ve geri alınabilir.

Bedelin ölçüsü de tablodadır. Durum makinesinde ödeme yapan yol sayısı 1'dir; adım başına
kurulumda 369. Bu 368 yolun tamamı kapatılmıştır ve kullanıcının akışı tamamlamak için
izleyebileceği tek bir sıra kalmıştır. Bir akışta meşru esneklik varsa — belgenin inceleme
öncesinde de eklenebilmesi, indirimin iki farklı noktada uygulanabilmesi — bu esneklik
geçiş tablosunda satır olarak görünmek zorundadır. Tabloda olmayan esneklik ürün kararı
değil, kapatılmış bir yoldur.

Bir ayrıntı gerçekleştirimde önemlidir. Yukarıdaki modelde izinli olmayan adım **sessizce
atlanıyor**; gerçek bir serviste atlamak yerine ret döndürmek gerekir. Sessiz atlama,
çağıranın adımın çalıştığını sanmasına yol açar ve bu, kapatılan açığın yerine bir tutarlılık
hatası koyar. Durum makinesinin değeri kararı vermesindedir; kararın bildirilmesi ayrı bir
sorumluluktur.

## Özet

- İş mantığı açığı tek bir isteğin yetkisinde değil, adım dizisinin sırasında ve tekrarında
  doğar; her adım tek tek yetkiliyken dizinin kendisi yetkisiz olabilir.
- Üç kusur türü ölçüldü: adım atlama (onaysız ödeme), adım tekrarı (200 ve 300 birim ödeme)
  ve kota aşımı (indirimin üç kez uygulanması).
- Beş adımdan üretilen 625 dört adımlık yolun 369'u ödeme yapıyor, 113'ü 100 birimden çok
  ödüyor, 113'ü kotayı aşıyor ve 501'i (%80,2) kural dışı sonuç veriyor.
- Durum makinesi kural dışı yolu sıfıra, ödeme yapan yolu bire indirir; kapatma mekanizması
  beş satırlık bir geçiş tablosudur, dizi başına yazılmış bir denetim değil.
- Geçişi bildirilmemiş altıncı adım eklendiğinde adım başına kurulumda kural dışı yol 501'den
  1.172'ye çıkarken durum makinesinde 0 kalır; dizi uzunluğu 2'den 6'ya çıktığında kural dışı
  yolların payı %60,0'tan %90,1'e yükselirken durum makinesi sütunu sıfırda kalır.
- İzinli olmayan adım sessizce atlanmaz, reddedilir; sessiz atlama açığın yerine bir
  tutarlılık hatası koyar.

## Sonraki Adım

Bu dersin bütün ölçümü tek bir varsayıma dayanıyor: adımlar **birbiri ardına** çalışıyor.
Durum makinesi, ödeme adımına geldiğinde durumun `onaylandi` olduğunu okuyor, kararı veriyor
ve durumu `kapandi` yapıyor. Okuma ile yazma arasında geçen sürede başka hiçbir şeyin
olmadığı varsayılıyor. İki istek aynı anda geldiğinde bu varsayım düşer: ikisi de durumu
`onaylandi` okuyabilir, ikisi de kararı geçebilir ve ikisi de ödeyebilir. Sonraki ders
eşzamanlı istek modelinde aynı kontrolün kaç kez geçtiğini sayar ve kilit ile tek yazarlı
kuyruğun kaçını kapattığını ölçer.
