Gizli anahtarları koddan çıkarmak: yönetim ve döndürme
- Koda gömülen sırrı git geçmişinde bulmayı, gitleaks ile taramayı, gizli anahtar yöneticisini, sızıntıda doğru müdahale sırasını ve döndürmeyi öğrenirsin.
- Sızan bir anahtarın izini git log ve gitleaks ile sür. Sırrı Vault'tan okumayı ve geçmişi temizlemeyi öğren. CI'da kalıcı anahtar yerine OIDC kullan.
Puan bütün etkinlikleri cevaplayınca hesaba yazılır. Bu dersi tamamlayınca +50 XP alacaksın. Giriş yapmadan ilerlersen puan yalnız bu tarayıcıda durur.
Altay Kargo'da bir geliştirici, ödeme entegrasyonunu hızlıca denemek için API anahtarını ayar dosyasına yazar. Yirmi dakika sonra anahtarı ortam değişkenine taşır ve yeni bir commit atar.
Dosya artık temiz görünür. Oysa anahtar hâlâ depodadır, üstelik her klonda ve her yedekte durur. Bu ders o yirmi dakikanın izini sürer.
Önce hangi sırlara sahip olduğunu çıkarır, gömülü sırrı git geçmişinde bulursun. Sonra sırrı çalışma zamanında bir gizli anahtar yöneticisinden alırsın. Ardından taramayla erken yakalar, sızıntıya doğru sırayla müdahale eder ve döndürmeyi otomatiğe bağlarsın.
Sır envanteri ve gömülü sırrın izi
Sır (secret), bir sisteme erişim ya da imza yetkisi veren her değerdir. API anahtarı, veritabanı parolası, JWT imza anahtarı, bulut erişim anahtarı, webhook imza sırrı ve SSH dağıtım anahtarı en yaygın örneklerdir. Her sır için dört soruyu yanıtlayan bir envanter tut. Sahibi kim?
Ömrü ne kadar? Nerede duruyor? Sızarsa nasıl iptal edilir? İptal yolunu önceden bilmek, olay anında dakikalar kazandırır.
Envanterde olmayan bir sırrı döndüremezsin. Altay Kargo'nun envanterinde ödeme anahtarının sahibi ödeme ekibidir. Anahtar 90 günde bir döner ve sağlayıcının panelinden tek tıkla iptal edilir.
Bir sırrı kaynak koduna yazmak ilk bakışta pratik görünür, ama kalıcı bir iz bırakır. Kod git deposuna gider ve git geçmişi kalıcıdır. Dosyadan silinen sır eski committe okunmaya devam eder. Aynı kod derleme kayıtlarına, konteyner katmanlarına, çatallanan (fork) kopyalara ve kod inceleme araçlarına da kopyalanır.
Sır bir kez koda girdiğinde onu görmüş her yeri saymak neredeyse imkânsızdır. Bu durum özel (private) depolar için de geçerlidir. Depoya erişmiş her eski çalışan ve her yedek, aynı sırrı görmüş olabilir. Sırrın izini bulmak için git'in kazma (pickaxe) aramasını kullanırsın.
$ git log -p -S 'sk_live_' --all commit 811dc20cb47a95df8095a0d7e4c754d7912b2670 Author: Mert Kaya <[email protected]> Date: Sat Mar 14 17:05:31 2026 +0300 Anahtarı ortam değişkenine taşı - API_ANAHTARI = "sk_live_9f3ac1b7d2e04af6c8b1" + API_ANAHTARI = os.environ["API_ANAHTARI"] commit 7da2895d03bb63a0ace51b345f7205204094d890 Author: Mert Kaya <[email protected]> Date: Sat Mar 14 16:42:07 2026 +0300 Ödeme entegrasyonu için hızlı test + API_ANAHTARI = "sk_live_9f3ac1b7d2e04af6c8b1"
Komutu parça parça oku. -S, verilen dizgenin geçme sayısını değiştiren commit'leri bulur. Yani sırrın eklendiği ve silindiği iki commit'i gösterir. --all yalnız bulunduğun dalı değil, bütün dalları tarar.
-p her commit'in farkını (diff) da basar. Çıktı yeni commit'ten eskiye doğru akar. Alttaki 7da2895 commit'i anahtarı ekler, üstteki 811dc20 onu siler. Silme commit'i sırrı yok etmez; git show 7da2895 komutu anahtarı bugün de gösterir.
Tarihler olay zaman çizelgesini verir. Anahtar 16.42'de depoya girdi. İptal edilene kadar geçen her dakika sızıntı penceresine eklenir.
Derinleş:Git neden unutmaz
Git içeriği adresine göre saklar. Her dosya sürümü bir blob nesnesidir ve adı, içeriğinin özetidir. Commit bir ağaç (tree) nesnesine, ağaç da bloblara işaret eder. Dosyayı silip commit atınca git yeni bir ağaç üretir.
Eski commit, eski ağaç ve anahtarı taşıyan blob yerinde kalır, çünkü geçmiş onlara hâlâ bağlıdır. Klon alan herkes bu nesnelerin tam kopyasını indirir. Depo barındırma hizmetleri çatalları çoğu zaman ortak bir nesne ağında tutar. Açık bir çekme isteği (pull request) de kendi referansıyla eski commit'leri yaşatır.
Geçmişi yeniden yazmak senin kopyanı ve sunucudaki dalları temizler. Başkasının klonunu, çatalını ya da önbelleğe alınmış görünümü temizlemez. Sızan bir sırrın tek güvenilir çaresi bu yüzden iptaldir.
Etkinlik 1 / 8
Altay Kargo'nun yapılandırma dosyasında gömülü gizli anahtarı bul.
Sırrı çalışma zamanında al
Koda gömülü bir anahtarı değiştirmek yeni bir kod dağıtımı ister. Bu adım yavaş ve riskli görüldüğü için ertelenir, sır yıllarca aynı kalır. Doğru desen, sırrı koda hiç yazmamak ve çalışma zamanında almaktır. Kaynak bir ortam değişkeni ya da gizli anahtar yöneticisidir (secret manager).
Yerel geliştirmede kullanılan .env dosyası da git'e girmez, .gitignore listesinde durur. Yanlışlıkla commit edilen bir .env, koda gömülü sırla aynı sonucu doğurur. Üretimde değerleri dağıtım sistemi konteyner açılırken enjekte eder. Değer hiçbir noktada diske ya da imaja yazılmaz.
Gizli anahtar yöneticisi sırları merkezde saklar, erişimi yetkiye göre sınırlar ve her okumayı kaydeder. Vault, AWS Secrets Manager, GCP Secret Manager ve Azure Key Vault bilinen örneklerdir. Uygulama açılışta kendi kimliğiyle yöneticiye bağlanır ve ihtiyacı olan sırrı ister.
Değeri yalnız bellekte tutar, diske yazmaz. Burada bir tuzak var: uygulama yöneticiye statik bir parolayla bağlanırsa sorun bir kat yukarı taşınır. Buna ilk sır sorunu (secret zero) denir.
Çözüm, platformun verdiği kimliktir: bulutta IAM rolü, Kubernetes'te servis hesabı. Erişim kaydı hangi servisin hangi sırrı ne zaman okuduğunu gösterir. Sızıntıdan sonra etkilenen bileşeni bu kayıttan bulursun.
$ vault kv get -mount=secret jwt-imza-anahtari ======== Secret Path ======== secret/data/jwt-imza-anahtari ======= Metadata ======= Key Value --- ----- created_time 2026-09-01T08:12:44.918273Z custom_metadata <nil> deletion_time n/a destroyed false version 3 ==== Data ==== Key Value --- ----- deger oiMrE+T1zXD7uH0HW+RL/opz4ChvSJBRnp0E76zbizQ= $ vault kv get -mount=secret -field=deger jwt-imza-anahtari oiMrE+T1zXD7uH0HW+RL/opz4ChvSJBRnp0E76zbizQ= $ aws secretsmanager get-secret-value --secret-id jwt-imza-anahtari --query SecretString --output text oiMrE+T1zXD7uH0HW+RL/opz4ChvSJBRnp0E76zbizQ=
Çıktıyı bölüm bölüm oku. Secret Path, KV motorunun 2. sürümünde sırrın gerçek yolunu gösterir; araya data eklenir. Metadata bölümünde version 3 yazar.
Sır iki kez döndürülmüştür ve eski sürümler geri okunabilir. Data bölümü değerin kendisidir. -field=deger yalnız değeri basar, betikte kullanmak için budur.
AWS komutunda --query SecretString yanıttan yalnız sırrı seçer, --output text de onu tırnaksız yazar. İki araç da değeri terminale açık yazar. Bu komutları ekran paylaşımında ya da günlüğü tutulan bir CI adımında çalıştırma.
Uygulama aynı değeri kendi SDK'sıyla okur ve yalnız bellekte tutar. -mount bayrağı Vault'un kendi yardım metninin önerdiği yazımdır. Eski secret/jwt-imza-anahtari yazımı da çalışır ve araya data'yı kendisi ekler.
Etkinlik 2 / 8
Uygulamaları sır yönetimi açısından güvenli ve güvensiz olarak ayır.
Önce bir seçeneğe, sonra aşağıdaki uygun kutuya bas. İstersen seçeneği kutuya sürükleyip bırak. Yerini değiştirmek için aynı işlemi tekrarla.
0/4 seçenek yerleşti.
Etkinlik 3 / 8
Vault'ta secret/jwt-imza-anahtari yolundaki sırrın güncel sürümünü okuyan komutu yaz.
Taramayla erken yakala
Sızıntıyı en ucuz yakaladığın yer, geliştiricinin kendi makinesidir. Tarama üç noktaya konur. Birincisi commit öncesi kancadır (pre-commit hook); sır daha commit'e girmeden yakalanır.
İkincisi CI'dır; her push'ta bütün değişiklik taranır. Üçüncüsü, depo geçmişinin tamamı üzerinde bir kerelik taramadır. gitleaks bu iş için yaygın bir açık kaynak araçtır.
Kurallarını düzenli ifade ve rastgelelik ölçüsü (entropi) üzerine kurar. Depo barındırma tarafında da bir katman vardır. GitHub'ın kullanıcı düzeyindeki push koruması, genel depolara sır gönderimini varsayılan olarak durdurur. Kullanıcı bu engeli aşabilir, bu yüzden tek katman olarak yetmez.
$ gitleaks git -v . Finding: API_ANAHTARI = "sk_live_9f3ac1b7d2e04af6c8b1" Secret: sk_live_9f3ac1b7d2e04af6c8b1 RuleID: stripe-access-token Entropy: 4.307355 File: config/ayarlar.py Line: 5 Commit: 7da2895d03bb63a0ace51b345f7205204094d890 Author: Mert Kaya Email: [email protected] Date: 2026-03-14T13:42:07Z Fingerprint: 7da2895d03bb63a0ace51b345f7205204094d890:config/ayarlar.py:stripe-access-token:5 INF 3 commits scanned. WRN leaks found: 1 $ gitleaks dir -v . INF no leaks found
Bulguyu alan alan oku. Finding, sırrın geçtiği satırdır. Secret yalnız yakalanan değeri gösterir. RuleID hangi kuralın tetiklendiğini söyler; sk_live_ öneki bir ödeme sağlayıcısının anahtar biçimidir.
Entropy, dizgenin ne kadar rastgele göründüğünü ölçer. Rastgele anahtarlar yüksek, sıradan kelimeler düşük değer alır. File ve Line yeri, Commit, Author ve Date zamanı verir. Fingerprint, bulgunun benzersiz kimliğidir.
Asıl ders son komuttadır. gitleaks dir yalnız çalışma dizinini tarar ve temiz der, çünkü dosya bugün temizdir. gitleaks git ise geçmişi tarar ve sızıntıyı bulur. Bulgu olursa araç 1 çıkış koduyla döner ve CI adımı düşer.
Her bulgu gerçek sızıntı değildir. Önce dosyaya bak: üretim yapılandırması mı, test ya da belge mi? Sonra değere bak: yüksek entropili bir anahtar mı, BURAYA_YAZ gibi bir yer tutucu mu? Özel anahtar kuralı neredeyse her zaman gerçektir.
Emin değilsen sağlayıcıda anahtarın etkin olup olmadığına bak. Yanlış pozitifi inceledikten sonra iki yolla susturursun. Parmak izini depo kökündeki .gitleaksignore dosyasına eklersin ya da satıra gitleaks:allow yorumu koyarsın.
Depoda durması gereken yapılandırma varsa SOPS kullan. SOPS dosyadaki değerleri şifreler, anahtar adlarını açık bırakır. Böylece farklar okunur, sır okunmaz.
Etkinlik 4 / 8
Altay Kargo deposunun bütün git geçmişini gitleaks ile, her bulguyu ayrıntılı basarak tara. Depo bulunduğun dizindir.
Etkinlik 5 / 8
Tarama raporundan kısaltılmış satırlar aşağıda. İptal ve döndürme gerektiren gerçek bulguları işaretle.
Şüpheli satırlara tıkla. Birden fazla olabilir.
Sızıntıya müdahale: iptal, kapsam, temizlik
Genel bir depoya giden sır için saat dakikalarla işler. Otomatik tarayıcılar yayımlanan commit'leri sürekli izler ve sır kalıbına benzeyen dizgeleri toplar. Doğru müdahale dört adımdır ve sırası değişmez. Birincisi sırrı sağlayıcıda iptal etmek ve yenisini üretmektir.
Yeni sır yöneticiye girer, servisler onu okur. İkincisi kapsamı belirlemektir. Sağlayıcının erişim günlüğünde sızıntı penceresine bakarsın: commit anından iptal anına kadar anahtarı kim, nereden kullandı? Üçüncüsü geçmişi temizlemektir.
Dördüncüsü önlemdir: tarama kancası, push koruması ve olay kaydı. Sıra önemlidir, çünkü geçmiş temizliği klonlara ve çatallara ulaşmaz. İptal ise sırrı her yerde bir anda değersiz kılar.
$ git-filter-repo --sensitive-data-removal --invert-paths --path config/uretim.env $ git push --force --mirror origin
Komutları sırayla oku. --sensitive-data-removal bayrağı için git-filter-repo'nun en az 2.47 sürümü gerekir; sürümünü önce denetle. --invert-paths ile --path birlikte, verilen dosyayı bütün commit'lerden çıkarır. Geriye kalan commit'lerin kimlikleri değişir.
Zorla ayna itme (push --force --mirror) sunucudaki dalları yeniden yazar. Ekipteki herkes kendi dalını yeni geçmişin üstüne taşır (rebase), birleştirme (merge) yapmaz. Eski geçmişi taşıyan tek bir merge commit'i sırrı geri getirebilir. GitHub belgeleri iki uyarı daha yapar.
Çatallardaki kopyalar senin kontrolünde değildir. Önbelleğe alınmış görünümler ve açık çekme istekleri için destek ekibine başvurman gerekir. Bu yüzden temizlik, iptalin yerini tutmaz.
Etkinlik 6 / 8
Bir geliştirici API anahtarını yanlışlıkla genel bir depoya push'ladı, sonra dosyayı silip yeni bir commit attı. Bu yeterli midir?
Etkinlik 7 / 8
Genel depoya sızan bir API anahtarı için müdahale adımlarını doğru sıraya koy.
Bir öğeye odaklan, yukarı ve aşağı ok tuşlarıyla taşı. Home tuşu en üste, End tuşu en alta alır.
- 1Erişim günlüğünde sızıntı penceresindeki kullanımı incele
- 2Tarama kancasını ve push korumasını aç, olay kaydını kapat
- 3Dosyayı git-filter-repo ile geçmişten çıkar, ekibi rebase için uyar
- 4Anahtarı sağlayıcıda iptal et, yenisini üretip yöneticiye koy
Döndürme, kısa ömürlü kimlik ve CI
Döndürme (rotation), sızıntı olmasa bile sırrın düzenli aralıklarla yenilenmesidir. Elle yapılan döndürme ertelenir ve unutulur, bu yüzden takvime ve otomasyona bağlanır. Kesintisiz döndürmede yeni anahtar yöneticiye girer.
Kısa bir geçiş penceresinde iki anahtar da geçerlidir. Bütün servisler yenisine geçince eskisi iptal edilir. Kısa ömürlü kimlik bilgisi bu ihtiyacı büyük ölçüde kaldırır, çünkü kendiliğinden geçersiz olur.
CI/CD boru hattı bu ilkenin en çok işe yaradığı yerdir. Ayarlara konmuş uzun ömürlü bir bulut anahtarı saldırganın birinci hedefidir. OIDC federasyonu bu anahtarı kaldırır. İş akışı çalışırken CI platformundan kısa ömürlü, imzalı bir kimlik jetonu ister.
Bulut tarafındaki rol, jetonun hangi depodan ve daldan geldiğini denetler. Koşul tutarsa yalnız o iş için geçici anahtar verir. Depoda ya da CI ayarlarında çalınacak statik sır kalmaz.
permissions:
id-token: write # OIDC jetonu istemek için
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: arn:aws:iam::111122223333:role/altaykargo-dagitim
aws-region: eu-central-1Satırları oku. id-token: write, iş akışının OIDC jetonu istemesine olanak verir. Bu izin olmadan jeton alınamaz. contents: read, depoya yalnız okuma yetkisi bırakır.
configure-aws-credentials adımı jetonu bulut sağlayıcıya götürür ve role-to-assume ile verilen rolü üstlenir. Rolün güven politikası jetonun yayıncısını, yani token.actions.githubusercontent.com adresini tanır. Jetondaki sub alanında depo ve dal adı yazar. Politika bu alanı yalnız ana dal için kabul ederse, başka bir daldan ya da çekme isteğinden çalışan iş rolü üstlenemez.
Son olarak sık hataları topla. Sırrı günlüğe yazmak (CWE-532) onu günlüğü okuyan herkese dağıtır; hata ayıklama satırları en sık kaynaktır. Dockerfile içinde ENV ya da ARG ile verilen sır son imajda kalır. Docker belgeleri bunun yerine derleme sırrı bağlamayı (RUN --mount=type=secret) önerir.
Ön yüz paketine giren değerler de sır değildir. NEXT_PUBLIC_ ya da VITE_ önekli değişkenler tarayıcıya giden JavaScript'e gömülür. CI'da maskeleme de tek başına yetmez.
Maskeleme yalnız bilinen değerleri gizler; farklı kodlanmış ya da parça parça yazdırılan bir değer maskeden kaçar. Asıl kural, sırrı hiç yazdırmamaktır.
Derinleş:Vaka: geliştirici makinesinden sır toplayan solucan
Eylül 2025'te npm paket kayıt defterinde Shai-Hulud adıyla bilinen, kendini çoğaltan bir solucan yayıldı. CISA'nın 23 Eylül 2025 tarihli uyarısına göre solucan 500'den fazla paketi ele geçirdi. İlk erişimden sonra çalıştığı ortamda sır aradı. Hedefi GitHub kişisel erişim jetonları ile AWS, GCP ve Azure bulut anahtarlarıydı.
Topladığı sırları saldırganın uç noktasına yolladı ve genel bir depoya yükledi. Ardından ele geçirdiği geliştiricinin kimliğiyle npm'e bağlandı. Onun başka paketlerine kod ekleyip yeni sürümler yayımladı. Vaka bu dersin iki ilkesini birlikte gösterir.
Geliştirici makinesinde duran uzun ömürlü her sır, bir bağımlılık zincirinin ucunda dışarı çıkabilir. Sızıntıdan sonra tek güvenilir çare de sırrı iptal edip döndürmektir. CISA'nın önerileri arasında bütün geliştirici kimlik bilgilerini hemen döndürmek ve geliştirici hesaplarında oltalamaya dayanıklı çok faktörlü doğrulama zorunlu tutmak vardı.
Etkinlik 8 / 8
Doğru mu, yanlış mı?
CI/CD sağlayıcısının otomatik maskelemesi günlükte tanıdığı kalıpları gizler; aynı sır Base64 gibi farklı bir kodlamayla yazdırılırsa maskeden kaçabilir.
Okumayı istediğin sırayla sürdürebilirsin. Ders, bütün etkinlikleri cevapladığında tamamlanır.
- Etkinlik 1· Sır envanteri ve gömülü sırrın izi
- Etkinlik 2· Sırrı çalışma zamanında al
- Etkinlik 3· Sırrı çalışma zamanında al
- Etkinlik 4· Taramayla erken yakala
- Etkinlik 5· Taramayla erken yakala
- Etkinlik 6· Sızıntıya müdahale: iptal, kapsam, temizlik
- Etkinlik 7· Sızıntıya müdahale: iptal, kapsam, temizlik
- Etkinlik 8· Döndürme, kısa ömürlü kimlik ve CI