---
title: 'Gizli Değerlerin Ayrılması'
source: 'https://academia.sh/tr/kurslar/sunucu-temelleri/gizli-degerlerin-ayrilmasi'
course: 'Sunucu Tarafı Temelleri'
language: tr
updated: '2026-08-17T18:06:52+00:00'
license: 'CC BY-SA 4.0'
---

# Gizli Değerlerin Ayrılması

Yapılandırma ile gizli değer arasındaki sınır çıkış yollarına göre çizilir; anahtar adına ve değerin kendisine dayanan iki katlı maskeleme uygulanır, iç ayrıntının kütükte kalıp istemciye geçmemesi sağlanır ve bir sızıntı denetimi maskelenmemiş alanı çıkış koduyla yakalar.

Önceki dersin şemasında bir değişken kasten yoktu: deponun parolası. Bağlantı noktası ile
parola aynı yerden, ortamdan okunur ve aynı nesnede taşınır; buna karşılık biri açılışta
kütüğe yazılabilir, diğeri yazılamaz. Bu ders o sınırı çizer ve sınırın kod içinde nerede
uygulandığını gösterir.

Konu, gizli bir değeri saklamak değildir — değer zaten süreç belleğindedir ve uygulama onu
kullanmak zorundadır. Konu, o değerin sürecin **çıkış yollarına** ulaşmasını engellemektir:
kütük satırları, hata yanıtları, istemciye dönen gövdeler ve başlıklar.

## Sınır Nerede

Yapılandırma ile gizli değeri ayıran ölçüt, değerin ne olduğu değil, **açığa çıktığında ne
olacağıdır.** Bağlantı noktasının kütükte görünmesi bir sorun değildir; parolanın görünmesi,
kütüğü okuyabilen herkesin depoya erişebilmesi anlamına gelir.

Bu ölçüt üç pratik kural üretir:

- **Gizli değer koda yazılmaz.** Sürüm denetimindeki bir dosyaya giren değer, o deponun
  bütün kopyalarına ve bütün geçmişine girmiştir. Değiştirilmesi gerektiğinde geçmişten
  silinemez.
- **Gizli değer kütüğe yazılmaz.** Kütükler biriktirilir, taşınır ve genelde uygulamanın
  kendisinden daha geniş bir kitleye açıktır.
- **Gizli değer istemciye ulaşmaz.** Hata yanıtı, ayar görünümü ve tanı çıktıları bunun en
  sık kırıldığı yerlerdir.

Kurallar niyet olarak yeterli değildir; koda bağlanmaları gerekir. Aşağıdaki iki modül bunu
yapar.

## İki Katlı Maskeleme

**Maskeleme** (redaction), bir değeri çıktıya yazmadan önce sabit bir imle değiştirmektir.
Uygulanmasının iki yolu vardır ve ikisi de tek başına eksiktir.

Birinci yol **anahtar adına** bakar: alan adı parola, anahtar, jeton gibi bir sözcük
içeriyorsa değeri maskele. Bu yol bildirimseldir ve okunaklıdır, ama gizli değer beklenmedik
bir alana düştüğünde kaçırır — örneğin bir hata iletisinin içine gömülmüş bağlantı
adresinde.

İkinci yol **değerin kendisine** bakar: açılışta kaydedilmiş gizli değerlerin herhangi
birine rastlanırsa, hangi alanda olursa olsun, değiştir. Bu yol beklenmedik yolları kapatır,
ama yalnız açılışta kaydedilen değerleri tanır.

İkisi birlikte uygulanır.

```js
// kaynak/kurulum/gizli.mjs — gizli degerleri kaydeder; kutuk ve yanit yollarinda maskeler
export const MASKE = "***";

const GIZLI_DEGERLER = new Set();
const GIZLI_ANAHTAR = /parola|anahtar|jeton|gizli|authorization|cookie/i;

// Acilista her gizli degerin bir kez kaydedilmesi, deger tabanli maskelemenin kaynagidir.
export const gizliKaydet = (deger) => {
  if (typeof deger === "string" && deger.length >= 6) GIZLI_DEGERLER.add(deger);
};

export const maskele = (deger, anahtar = "") => {
  if (typeof deger === "string") {
    if (GIZLI_ANAHTAR.test(anahtar)) return MASKE;              // 1. anahtar adina gore
    let sonuc = deger;
    for (const gizli of GIZLI_DEGERLER) sonuc = sonuc.split(gizli).join(MASKE);
    return sonuc;                                               // 2. degerin kendisine gore
  }
  if (Array.isArray(deger)) return deger.map((e) => maskele(e));
  if (deger instanceof Error) {
    return { ad: deger.name, ileti: maskele(deger.message), kod: deger.kod ?? null };
  }
  if (deger && typeof deger === "object") {
    return Object.fromEntries(Object.entries(deger).map(([a, d]) => [a, maskele(d, a)]));
  }
  return deger;
};
```

Hata nesnesinin ayrı ele alınması gerekir, çünkü `JSON.stringify` bir `Error` nesnesini boş
nesneye çevirir; ileti ve yığıt izi kaybolur. Buradaki dönüşüm iletiyi korur ama maskeden
geçirir.

Yapılandırma okuyucusu, gizli alanları okurken aynı zamanda kaydeder. Bu tek satır, değer
tabanlı maskelemenin dayandığı kayıttır.

```js
// kaynak/kurulum/ayar.mjs — ortamdan okur; gizli alanlari isaretler ve gizli kutugune kaydeder
import { gizliKaydet } from "./gizli.mjs";

const SEMA = {
  BAGLANTI_NOKTASI: { varsayilan: "8436" },
  DEPO_ADRESI: { varsayilan: "depo://kutuphane@10.0.0.7:5432/odunc" },
  DEPO_PAROLASI: { zorunlu: true, gizli: true },
  IMZALAMA_ANAHTARI: { zorunlu: true, gizli: true },
};

export const ayariOku = (ortam) => {
  const ayar = {}, eksik = [];
  for (const [ad, kural] of Object.entries(SEMA)) {
    const deger = ortam["KUTUPHANE_" + ad] ?? kural.varsayilan;
    if (deger === undefined) { eksik.push("KUTUPHANE_" + ad); continue; }
    if (kural.gizli) gizliKaydet(deger);
    ayar[ad] = deger;
  }
  if (eksik.length > 0) {
    console.error("yapilandirma gecersiz: " + eksik.join(", "));
    process.exit(78);
  }
  return Object.freeze(ayar);
};
```

Şemadaki `gizli` bayrağı, önceki dersin şemasına eklenen tek alandır. Ayrım şemada tanımlanır,
uygulanması ise tek bir yerde toplanır.

## Çıkış Yollarını Kapatmak

Sunucunun kuralı şudur: **kütüğe ve yanıta giden her nesne maskeden geçer.** Bu, iki
fonksiyonun tek noktada tanımlanmasıyla sağlanır; başka hiçbir yerde doğrudan yazma yapılmaz.

```js
// kaynak/http/sunucu.mjs — her cikis yolu maskeden gecer: kutuk, hata yaniti ve ayar gorunumu
import { createServer } from "node:http";
import { ayariOku } from "../kurulum/ayar.mjs";
import { maskele } from "../kurulum/gizli.mjs";

const ayar = ayariOku(process.env);
let sayac = 0;

const kutukle = (kayit) => console.log(JSON.stringify(maskele(kayit)));

const baglan = () => {                       // depoya baglanma denemesi: hata iletisi adresi tasir
  const hata = new Error(
    `depoya baglanilamadi: ${ayar.DEPO_ADRESI} (parola: ${ayar.DEPO_PAROLASI})`);
  hata.kod = "depo_erisilemez";
  throw hata;
};

createServer((istek, yanit) => {
  yanit.sendDate = false;
  const kimlik = `i-${++sayac}`;
  const yol = new URL(istek.url, "http://yerel").pathname;
  const yaz = (durum, govde) => {
    const metin = JSON.stringify(maskele(govde));
    yanit.writeHead(durum, { "content-type": "application/json; charset=utf-8",
      "content-length": Buffer.byteLength(metin), "x-istek-kimligi": kimlik });
    yanit.end(metin);
  };

  try {
    if (yol === "/saglik") return yaz(200, { durum: "ayakta" });
    if (yol === "/ayarlar") return yaz(200, { ayar });          // gizli alanlar maskelenir
    if (yol === "/odunc") { baglan(); return; }
    yaz(404, { hata: "yol_yok", istekKimligi: kimlik });
  } catch (hata) {
    kutukle({ duzey: "hata", istekKimligi: kimlik, yol, hata });  // ic ayrinti yalniz kutukte
    yaz(500, { hata: "ic_hata", istekKimligi: kimlik });          // istemciye yalniz kod
  }
}).listen(Number(ayar.BAGLANTI_NOKTASI), "127.0.0.1",
  () => kutukle({ duzey: "bilgi", olay: "acildi", ayar }));
```

`baglan` fonksiyonu gerçek bir kazayı taklit eder: depoya bağlanan katmanların ürettiği hata
iletileri sık sık bağlantı adresini, bazen de kimlik bilgisini taşır. Bu ileti maskeden
geçmeden kütüğe yazılırsa gizli değer kütükte kalır.

Aynı `catch` bloğu ikinci bir ayrım daha kurar: iç ayrıntı kütüğe, istemciye yalnız bir hata
kodu ve istek kimliği gider. Kimliğin iki tarafta da bulunması, destek isteyen bir üyenin
verdiği numarayla kütükteki satırın bulunmasını sağlar; ayrıntının kendisi yanıta hiç geçmez.

## Sızıntının Denetimi

Yukarıdaki kuralların uygulandığı, okunarak değil çalıştırılarak doğrulanır. Aşağıdaki
program sunucuyu bilinen gizli değerlerle açar, uçlara istek yapar, yanıt gövdelerini,
yanıt başlıklarını ve sürecin yazdığı bütün kütüğü toplar, sonra bu metinlerde gizli
değerleri arar.

```js
// denetim/sizinti.mjs — verilen sunucuyu bilinen gizli degerlerle acar; bu degerlerin yanit
// govdesine, yanit basliklarina ya da kutuge dusup dusmedigini sinar. Bulgu varsa 1 ile cikar.
// Kullanim: node denetim/sizinti.mjs <betik> <baglanti-noktasi>
import { spawn } from "node:child_process";
import { setTimeout as bekle } from "node:timers/promises";

const GIZLILER = {
  KUTUPHANE_DEPO_PAROLASI: "raf-parolasi-9f3a2b4c",
  KUTUPHANE_IMZALAMA_ANAHTARI: "imza-anahtari-7c1d8e5a",
};
const UCLAR = ["/saglik", "/ayarlar", "/odunc"];

const [betik, baglantiNoktasi] = process.argv.slice(2);

const cocuk = spawn("node", [betik], {
  env: { ...process.env, ...GIZLILER, KUTUPHANE_BAGLANTI_NOKTASI: baglantiNoktasi },
});
let kutuk = "";
cocuk.stdout.on("data", (p) => (kutuk += p));
cocuk.stderr.on("data", (p) => (kutuk += p));
await bekle(800);

const bulgular = [];
console.log(`### ${betik} — istemciye donen yanitlar`);
for (const yol of UCLAR) {
  const yanit = await fetch(`http://127.0.0.1:${baglantiNoktasi}${yol}`);
  const govde = await yanit.text();
  const basliklar = [...yanit.headers].map(([a, d]) => `${a}: ${d}`).join("\n");
  console.log(`  ${yanit.status} ${yol}  ${govde}`);
  for (const [ad, deger] of Object.entries(GIZLILER)) {
    if (govde.includes(deger)) bulgular.push(`${yol} yanit govdesi <- ${ad}`);
    if (basliklar.includes(deger)) bulgular.push(`${yol} yanit basliklari <- ${ad}`);
  }
}

await bekle(200);
cocuk.kill();
await bekle(300);
for (const [ad, deger] of Object.entries(GIZLILER)) {
  if (kutuk.includes(deger)) bulgular.push(`kutuk <- ${ad}`);
}

console.log("--- kutuk ---");
console.log(kutuk.trimEnd().split("\n").map((s) => "  " + s).join("\n"));
console.log("--- denetim ---");
for (const bulgu of bulgular) console.log(`  SIZINTI  ${bulgu}`);
console.log(bulgular.length === 0
  ? "  gizli deger hicbir cikis yolunda bulunmadi"
  : `  ${bulgular.length} sizinti bulundu`);
process.exit(bulgular.length === 0 ? 0 : 1);
```

Hiçbir sızıntı bulamayan bir denetim, iki nedenden dolayı sessiz kalabilir: gerçekten sızıntı
yoktur ya da denetim bozuktur. İkisini ayırmak için denetimin bilerek kusurlu bırakılmış bir
örnek üzerinde de çalıştırılması gerekir. Aşağıdaki dosya bu amaçla `denetim/` dizininde
durur — kaynak ağacının dışındadır ve uygulamanın hiçbir yolundan çağrılmaz.

```js
// denetim/kusurlu-ornek.mjs — DENETIM ORNEGI, uretim kodu degildir.
// Maskeleme uygulanmamis bir surumu taklit eder; sizinti denetiminin gercekten
// yakaladigini kanitlamak icin durur ve kaynak agacinin disindadir.
import { createServer } from "node:http";
import { ayariOku } from "../kaynak/kurulum/ayar.mjs";

const ayar = ayariOku(process.env);

createServer((istek, yanit) => {
  yanit.sendDate = false;
  const yol = new URL(istek.url, "http://yerel").pathname;
  const govde = yol === "/ayarlar" ? { ayar } : { durum: "ayakta" };
  const metin = JSON.stringify(govde);
  yanit.writeHead(200, { "content-type": "application/json; charset=utf-8",
    "content-length": Buffer.byteLength(metin) });
  yanit.end(metin);
}).listen(Number(ayar.BAGLANTI_NOKTASI), "127.0.0.1",
  () => console.log(JSON.stringify({ olay: "acildi", ayar })));
```

```bash
#!/usr/bin/env bash
# Sizinti denetimini once gercek sunucuya, sonra bilerek kusurlu birakilmis ornege uygular.
node denetim/sizinti.mjs kaynak/http/sunucu.mjs 8436; echo "cikis kodu=$?"
echo
node denetim/sizinti.mjs denetim/kusurlu-ornek.mjs 8437; echo "cikis kodu=$?"
```

```
### kaynak/http/sunucu.mjs — istemciye donen yanitlar
  200 /saglik  {"durum":"ayakta"}
  200 /ayarlar  {"ayar":{"BAGLANTI_NOKTASI":"8436","DEPO_ADRESI":"depo://kutuphane@10.0.0.7:5432/odunc","DEPO_PAROLASI":"***","IMZALAMA_ANAHTARI":"***"}}
  500 /odunc  {"hata":"ic_hata","istekKimligi":"i-3"}
--- kutuk ---
  {"duzey":"bilgi","olay":"acildi","ayar":{"BAGLANTI_NOKTASI":"8436","DEPO_ADRESI":"depo://kutuphane@10.0.0.7:5432/odunc","DEPO_PAROLASI":"***","IMZALAMA_ANAHTARI":"***"}}
  {"duzey":"hata","istekKimligi":"i-3","yol":"/odunc","hata":{"ad":"Error","ileti":"depoya baglanilamadi: depo://kutuphane@10.0.0.7:5432/odunc (parola: ***)","kod":"depo_erisilemez"}}
--- denetim ---
  gizli deger hicbir cikis yolunda bulunmadi
cikis kodu=0

### denetim/kusurlu-ornek.mjs — istemciye donen yanitlar
  200 /saglik  {"durum":"ayakta"}
  200 /ayarlar  {"ayar":{"BAGLANTI_NOKTASI":"8437","DEPO_ADRESI":"depo://kutuphane@10.0.0.7:5432/odunc","DEPO_PAROLASI":"raf-parolasi-9f3a2b4c","IMZALAMA_ANAHTARI":"imza-anahtari-7c1d8e5a"}}
  200 /odunc  {"durum":"ayakta"}
--- kutuk ---
  {"olay":"acildi","ayar":{"BAGLANTI_NOKTASI":"8437","DEPO_ADRESI":"depo://kutuphane@10.0.0.7:5432/odunc","DEPO_PAROLASI":"raf-parolasi-9f3a2b4c","IMZALAMA_ANAHTARI":"imza-anahtari-7c1d8e5a"}}
--- denetim ---
  SIZINTI  /ayarlar yanit govdesi <- KUTUPHANE_DEPO_PAROLASI
  SIZINTI  /ayarlar yanit govdesi <- KUTUPHANE_IMZALAMA_ANAHTARI
  SIZINTI  kutuk <- KUTUPHANE_DEPO_PAROLASI
  SIZINTI  kutuk <- KUTUPHANE_IMZALAMA_ANAHTARI
  4 sizinti bulundu
cikis kodu=1
```

Çıktının en öğretici satırı kütükteki hata kaydıdır. `parola: ***` yazan bu ileti, hiçbir
alanı "parola" adını taşımadığı hâlde maskelenmiştir. Gizli değer bir hata iletisinin ortasına
gömülüdür; onu yakalayan, açılışta kaydedilmiş değere göre çalışan ikinci kattır. Yalnız
anahtar adına bakan bir maskeleme bu satırı olduğu gibi yazardı.

`/ayarlar` yanıtı ikinci ayrımı gösterir: aynı nesnedeki `DEPO_ADRESI` görünür, iki gizli alan
`***` olmuştur. Ayar görünümünün var olması bir sorun değildir; hangi alanların göründüğünün
şemada tanımlanmış olması onu güvenli kılar.

`/odunc` yanıtı üçüncüsünü verir. İstemci `{"hata":"ic_hata","istekKimligi":"i-3"}` almıştır;
deponun adresi, bağlantının neden kurulamadığı ve hatanın iç kodu yanıtta yoktur. Aynı isteğin
kütük satırı bunların hepsini taşır ve iki kayıt `i-3` ile eşleşir.

Kusurlu örnek üzerindeki çalıştırma denetimin boş bir onay üretmediğini kanıtlar: dört ayrı
çıkış noktası adıyla bildirilmiş ve çıkış kodu `1` olmuştur. Bir yapı adımına bağlandığında
bu kod, maskelemesi unutulmuş bir alanın yayına çıkmasını durdurur.

## Değişimin Maliyeti

Maskeleme tek başına yeterli değildir, çünkü açığa çıkmış bir gizli değerin tek çaresi
**değiştirilmesidir**. Uygulamanın bunu ucuz kılması gerekir: değer yalnız ortamdan okunuyorsa
değişim, ortamı güncelleyip süreçleri yeniden başlatmaktan ibarettir. Aynı değer koda
gömülüyse ya da birden çok yere kopyalanmışsa, değişim bir arama işine dönüşür.

Bunun ölçülebilir karşılığı şudur: gizli bir değerin uygulamada kaç yerde okunduğu sayılabilir
olmalıdır. Yukarıdaki iskelette bu sayı birdir — `ayariOku` fonksiyonu. Değeri kullanan her
kod parçası onu yapılandırma nesnesinden alır; hiçbiri `process.env` değerine kendi başına
bakmaz.

## Özet

- Yapılandırma ile gizli değeri ayıran ölçüt, değerin açığa çıkması hâlinde doğacak sonuçtur;
  gizli değer koda yazılmaz, kütüğe yazılmaz ve istemciye ulaşmaz.
- Maskeleme iki katlıdır: anahtar adına bakan kat bildirimsel ama eksiktir, açılışta
  kaydedilmiş değere bakan kat beklenmedik alanları kapatır; ölçümde hata iletisine gömülü
  parolayı ikinci kat yakalamıştır.
- Hata nesneleri ayrı ele alınır, çünkü doğrudan dizileştirildiklerinde ileti kaybolur;
  dönüşüm iletiyi korur ve maskeden geçirir.
- İç ayrıntı kütükte kalır, istemciye yalnız hata kodu ve istek kimliği gider; aynı kimlik iki
  kayıtta bulunduğu için sonradan eşleştirme yapılabilir.
- Sızıntı denetimi sunucuyu bilinen gizli değerlerle açar, yanıtları ve kütüğü tarar; gerçek
  sunucuda `0`, bilerek kusurlu bırakılmış örnekte dört bulgu ve `1` üretmiştir.

## Sonraki Adım

Bu dersin kütük satırları JSON biçimindeydi ve her satır bir istek kimliği taşıyordu, ama bu
kimlik yalnız iki yerde elle yazıldı. Bir istek zincirin beş halkasından ve üç modülden
geçtiğinde her satırın aynı kimliği taşıması nasıl sağlanır? Düzey eşiği ne işe yarar ve bir
isteğin bütün satırları sonradan hangi sorguyla toplanır? Sonraki ders yapılandırılmış
günlüklemeyi kurar: istek bağlamını zincir boyunca taşır ve tek bir isteğin bütün kayıtlarını
tek komutla toplar.
