Series · 3 partsWhen Access Checks FailPart 1 · You are here
  1. 1CI/CD ve OIDC Güven Sınırı: GitHub Actions'tan Buluta Yetki GeçişiYou are here
  2. 2İmza Geçerli. Token Hâlâ Başka Bir Yere Ait: JWT Doğrulama Sınırı
  3. 3İstek Sunucu Tarafında Kaldı. Kimlik Bilgisi Kalmadı: SSRF ve Cloud Metadata

60 saniyede CI/CD OIDC güven sınırı

Çoğu ekibin yaptığı en iyi CI/CD güvenlik iyileştirmesi, uzun ömürlü bulut erişim anahtarlarını depodan silip yerine OpenID Connect (OIDC) entegrasyonunu getirmektir. İş akışı kısa ömürlü bir kimlik belirteci (identity token) talep eder, bulut sağlayıcı bunu geçici kimlik bilgileriyle takas eder; böylece rotasyona tabi tutulacak, sızdırılacak veya eski derleme günlüklerinde unutulacak hiçbir statik gizli anahtar (secret) kalmaz.

Bu kesinlikle gerçek bir kazanımdır. Ancak tüm gizli anahtar tarayıcılarında (secret scanners) kusursuz görünen yeni bir hata modunu da beraberinde getirir: Kimlik bilgisi ortadan kalkmıştır fakat yeni bir tane üretme hakkı herkesin düşündüğünden çok daha geniştir.

OIDC, güven ihtiyacını boru hattından (pipeline) tamamen silmez. Güveni, claim’lere ve politika koşullarına taşır. Eğer bu koşullar “korumalı production dalındaki incelenmiş dağıtım işi” yerine “bu organizasyondaki herhangi bir iş akışı” diyorsa, boru hattınız tüm organizasyonu kapsayan otomatik kayıt politikasına sahip bir bulut kimlik sağlayıcısına dönüşmüştür.

Statik anahtar hiçbir zaman yetkilendirme sınırı olmadı

Statik bir erişim anahtarı, kimlik doğrulama (authentication) ile yetkilendirmeyi (authorisation) tek bir dizede birleştirir: Bu değere sahip olan herkes ilgili kimliği kullanabilir. OIDC ise bunları birbirinden ayırır. GitHub iş akışını doğrular ve imzalı bir token üretir; AWS ise bu token içindeki claim’lerin belirli bir rolü devralmasına (assume) izin verilip verilmediğine karar verir.

Yapısal yükü taşıyan asıl karar ikinci karardır.

GitHub Actions için bir AWS rol güven politikası (trust policy) normalde iki temel claim’i değerlendirir:

  • aud, resmi AWS credentials action’ı kullanıldığında yaygın olarak sts.amazonaws.com şeklinde hedeflenen token takasını belirtir.
  • sub, token’ı talep eden depo bağlamını tanımlar: İşe bağlı olarak depo adı, dal (branch), etiket (tag), pull request veya ortam (environment).

Rolün izin politikası (permission policy) oturumun ne yapabileceğini belirler. Güven politikası (trust policy) ise hangi iş akışının o oturuma dönüşebileceğini belirler. Canlı ortama bağlı bir role en az yetki (least privilege) izin politikası tanımlamak, yanlış deponun bu rolü devralmasına izin veren gevşek bir güven politikasını telafi edemez. En az yetki prensibi, çoğu denetimin başladığı noktadan tam bir adım önce başlar.

KİMLİĞİ DOĞRULANMIŞ İŞ AKIŞI → CLAIM'LER → GÜVEN KARARI → BULUT OTURUMU iş akışı çalıştırması id-token: write JWT talep edebilir GitHub OIDC aud + sub + ref imzalı claim'ler rol güveni (trust) KOŞULLAR asıl sınır STS oturumu kısa ömürlü BULUT KİMLİĞİ AYNI TOKEN SAĞLAYICI — İKİ ÇOK FARKLI GÜVEN YÜZEYİ repo:ORG/* eşleşen herhangi bir repo bu rolü devralabilir geniş ve kontrolsüz kimlik popülasyonu repo + korumalı ortam tanımlı kaynak, incelenmiş dağıtım sınırı dar ve amaca yönelik kimlik popülasyonu
OIDC sağlayıcı token'ın nereden geldiğini kanıtlar. Rol güven politikası ise bu kaynağın bir bulut kimliğine dönüşme hakkı olup olmadığına karar verir. Kısa ömürlü kimlik bilgileri raf ömrünü azaltır; kesin claim koşulları ise kimlerin bu bilgileri üretebileceğini kısıtlar.

Tek bir joker karakter (wildcard) ihlal birimini değiştirir

AWS; sub koşulu bulunmayan, null olan veya yalnızca tek bir çıplak joker karakterden (*) oluşan yeni bir GitHub güven politikasını reddeder. Bu güvenlik korkuluğu değerlidir ancak kabul edilen her desenin dar kapsamlı olduğu anlamına gelmez. Aşağıdaki koşul sözdizimsel olarak kısıtlı görünür ancak operasyonel olarak devasa bir risk taşır:

{
  "StringEquals": {
    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
  },
  "StringLike": {
    "token.actions.githubusercontent.com:sub": "repo:example-org/*"
  }
}

Bunun anlamı şudur: Güvenlik sınırınız artık tek bir deployment deposu değildir. Bu organizasyon desenine uyan mevcut ve gelecekteki her depo ve bu depolardan herhangi birinin token talep etmesini sağlayabilecek her kullanıcı ve iş akışı bu sınırın içindedir.

Bu hataya düşmek çok kolaydır; çünkü politika merkezi bir kural gibi okunur: “GitHub organizasyonumuz dağıtım yapabilir.” Oysa organizasyonlar arşivlenmiş projeleri, geçiş araçlarını, şirket içi küçük denemeleri, fork’ları ve çok farklı dal koruma kurallarına sahip depoları barındırır. Düşük öneme sahip tek bir depo, iş akışı dağıtım deposuyla aynı sub desenini karşılayabildiği anda canlı ortam için doğrudan bir saldırı vektörü haline gelir.

Güvenilir sınır, sağlayıcının ve bulutun birlikte zorunlu kılabileceği en küçük ve kararlı claim kümesidir: Tam eşleşen depo kimliği, beklenen audience ve korumalı bir dal ya da korumalı bir GitHub ortamı (environment). Bir iş akışında environment tanımlandığında, varsayılan sub biçimi dal yerine o ortamı içerecek şekilde değişir. Bu kozmetik bir fark değildir; güven politikası ile deponun ortam koruma kuralları artık birleşerek tek bir güvenlik kontrolü oluşturur.

Politika yazmadan önce sağlayıcının terminolojisini test edin

İşe internetteki güven politikası örneklerini kopyalayarak başlamayın. Ayırt etmek istediğiniz her iş akışı bağlamının (canlı ortam, ana dal, pull-request işi, etiket yayını ve yeniden kullanılabilir iş akışları) fiilen hangi claim’leri ürettiğini gözlemleyerek başlayın.

Yetkilendirilmiş bir test deposunda, token’ı yalnızca yerel olarak başlığını ve claim’lerini çözecek kadar talep edin. Kodlanmış token’ı loglara yazdırmayın veya canlı bir rolle takas etmeyin:

permissions:
  contents: read
  id-token: write

jobs:
  inspect-claims:
    runs-on: ubuntu-latest
    environment: oidc-lab
    steps:
      - uses: actions/github-script@v8
        with:
          script: |
            const token = await core.getIDToken('sts.amazonaws.com')
            const claims = JSON.parse(Buffer.from(token.split('.')[1], 'base64url'))
            core.info(JSON.stringify({ aud: claims.aud, sub: claims.sub, ref: claims.ref }))

Yalnızca sts:GetCallerIdentity iznine sahip ayrıcalıksız bir laboratuvar rolü kullanın. Pozitif test durumu bu rolü hedeflenen ortamdan devralmalıdır. Negatif test durumları ise bir özellik dalından (feature branch), bir pull request’ten, ikinci bir depodan ve korumalı ortama referans vermeyen bir işten çalıştırıldığında başarısız olmalıdır. Bunlardan herhangi biri başarılı oluyorsa, güven politikası yazarının düşündüğünden çok daha geniştir.

Token’ın tamamını asla ekrana yazdırmayın. Kısa ömrü boyunca bu bir bearer kimlik bilgisidir ve bir değer hızla sona erse dahi loglara yazılması bir sızıntıdır.

İsimler her zaman en kalıcı kimlikler değildir

Depo isimleri OIDC politikasını okunabilir kılar ancak isimler değiştirilebilir, transfer edilebilir ve bazı sistemlerde yeniden kullanılabilir. GitHub’ın güncel OIDC referansı, sahip ve depo ID’lerini içeren değiştirilemez (immutable) bir subject formatı sunar. Eski depolar taşınana kadar önceki isim tabanlı formatı üretmeye devam edebilir.

Bu geçiş çoğu örneğin atladığı bir inceleme sorusu yaratır: Bu depo bugün fiilen hangi subject formatını üretiyor? Yeni şemaya göre yazılmış bir politika eski bir depoyu reddeder. Bulut güveni güncellenmeden yapılan bir geçiş dağıtımı bozar. Kesinti sırasında eklenen geçici bir joker karakter ise her iki formatı sessizce kalıcı hale getirerek rolü eskisinden daha savunmasız bırakabilir.

Subject formatı geçişini bir kimlik geçişi gibi ele alın: Mevcut claim’leri yakalayın, tam eşleşen yeni koşulu hazırlayın, pozitif ve negatif kontrolleri çalıştırın ve ardından eski formatı kaldırın. Geriye dönük uyumluluk kalıcı bir joker karakter değil, ölçülebilir bir geçiş penceresi olmalıdır.

Kanıt matrisi (Evidence matrix)

SinyalNeyi kanıtlarNegatif kontrolSavunucu doğrulaması
Korumalı işten gözlemlenen aud ve subSağlayıcı terminolojisi ve kesin dağıtım kimliği bilinmektedirPull request, özellik dalı ve ikinci depodan claim’leri toplayın; her biri beklenen alanda farklı olmalıdırCanlı claim’leri güven politikası operatörleri ve değerleriyle karşılaştırın
Rol devralma yalnızca onaylı ortamdan başarılı oluyorClaim koşulları hedeflenen kaynak sınırını zorunlu kılıyorAynı rolü en yakın komşu bağlamlardan devralmayı deneyin ve reddedildiğini teyit edinAssumeRoleWithWebIdentity olaylarını depo, subject, rol ve oturum adıyla ilişkilendirin
Organizasyon veya depo joker karakteri belgelenenden daha fazla kimlikle eşleşiyorİhlal birimi tek bir deployment deposundan daha büyüktürDeseni kesin koşullarla değiştirin ve meşru dağıtımın çalıştığını doğrulayınHer wildcard desenini karşılayabilecek her depoyu ve iş akışını envantere alın
Ortam koruması incelenmemiş bir dağıtım işini engelliyorDepo kontrolleri ve bulut güveni birleşerek tek bir sınır oluştururİncelemeci/dal uygunluğunu kaldırın ve token’ın artık canlı yolu karşılayamadığını teyit edinOrtam incelemecilerini, dağıtım dallarını, yöneticileri ve atlatma izinlerini denetleyin
Değiştirilemez subject geçişi yeni formatı kabul edip eskiyi reddediyorUyumluluk süreci ikinci bir kalıcı güven yolu bırakmamıştırGeçişten sonra eski subject formatını kullanan bir token sunun ve reddedildiğini teyit edinSubject formatını, repo ID’sini, owner ID’sini ve politika geçiş tarihini takip edin

Sıkça karşılaştığım ortak örüntü

Ekip bulut erişim anahtarını sildiği için kutlama yapar, rotasyon biletini kapatır ve onun yerine geçen güven politikasının sahipliğini hiçbir zaman kimseye atamaz. Platform mühendisliği iş akışının sahibidir. Bulut güvenliği IAM’in sahibidir. Depo yöneticileri ortamların sahibidir. Kimlik ancak her üç sistem uzlaştığında var olur; dolayısıyla hiçbir sahip tek başına tüm kayıt kuralını görmez.

Bu nedenle kalıcı bulgu “OIDC yanlış yapılandırılmış” değildir. Şudur: “Bu canlı ortam rolü şu ek iş akışı bağlamlarından gelen token’ları kabul ediyor ve sınır tam olarak bu koşul nedeniyle genişliyor.” Bu saldırı yolu da diğer tüm kimlik yükseltme yolları kadar somut ve izlenebilir olmalıdır.

Savunuculara teslim edilmesi gerekenler

Çözümlenmiş güven popülasyonu. Her StringLike desenini bugün fiilen eşleştiği depolara, dallara, etiketlere, pull-request işlerine ve ortamlara dönüştürün.

Claim sözleşmesi. Beklenen sağlayıcıyı, audience’ı, subject formatını, depo kimliğini ve dağıtım bağlamını kaydedin. Depo transferlerinden, ortam değişikliklerinden veya format geçişlerinden sonra bu sözleşmeyi yeniden test edin.

Negatif senaryolar. Başarılı bir canlı dağıtım yalnızca erişilebilirliği kanıtlar; tekilliği (münhasırlığı) kanıtlamaz. En yakın güvenilmeyen bağlamlar için otomatik ret testlerini hazır tutun; çünkü güven politikasının yeniden kontrolsüzce genişlediğini tespit edecek olanlar yalnızca bu testlerdir.

Boru hattı artık bir bulut kimlik bilgisi saklamıyor. Çok daha kritik bir şeyi saklıyor: O kimlik bilgisini kimin isteyebileceğine karar veren güvenlik politikasını.

Kaynaklar ve güncellik

Bu teknik not ne kadar güncel?

Kontrol edilen kaynaklar16 Nisan 2026

En son kaynak incelemesi, içerik güncellemesi veya yayın tarihi gösterilmektedir.

İnceleme DurumuYazar incelemesi tamamlandı

Yazar teknik incelemeyi tamamladı. Bu etiket tek başına laboratuvar yeniden üretimi iddiası taşımaz.