Browser Support
Kötü amaçlı komut dosyalarını bir web uygulamasına yerleştirme imkanı veren siteler arası komut dosyası çalıştırma (XSS), on yılı aşkın süredir en büyük web güvenliği açıklarından biri olmuştur.
İçerik Güvenliği Politikası (İGP), XSS'yi azaltmaya yardımcı olan ek bir güvenlik katmanıdır. CSP yapılandırmak için bir web sayfasına Content-Security-Policy HTTP üstbilgisini ekleyin ve kullanıcı aracısının o sayfa için hangi kaynakları yükleyebileceğini kontrol eden değerleri ayarlayın.
Bu sayfada, XSS'yi azaltmak için tek seferlik rastgele sayılara veya karma değerlere dayalı bir İGP'nin nasıl kullanılacağı açıklanmaktadır. Bu yöntem, çoğu yapılandırmada atlanabildiği için sayfayı genellikle XSS'ye maruz bırakan, yaygın olarak kullanılan ana makine izin verilenler listesine dayalı İGP'lerin yerine kullanılır.
Önemli terim: Tek seferlik rastgele sayı, bir <script> etiketini güvenilir olarak işaretlemek için kullanabileceğiniz, yalnızca bir kez kullanılan rastgele bir sayıdır.
Önemli terim: Karma işlevi, bir giriş değerini karma adı verilen sıkıştırılmış bir sayısal değere dönüştüren matematiksel bir işlevdir. Satır içi bir <script> etiketini güvenilir olarak işaretlemek için karma (ör. SHA-256) kullanabilirsiniz.
Tek seferlik rastgele sayılara veya karma değerlere dayalı bir İçerik Güvenliği Politikası genellikle katı İGP olarak adlandırılır. Bir uygulama katı bir CSP kullandığında, HTML yerleştirme kusurlarını bulan saldırganlar genellikle bu kusurları kullanarak tarayıcıyı, güvenlik açığı bulunan bir dokümanda kötü amaçlı komut dosyaları yürütmeye zorlayamaz. Bunun nedeni, katı CSP'nin yalnızca karma komut dosyalarına veya sunucuda oluşturulan doğru tek seferlik rastgele sayı değerine sahip komut dosyalarına izin vermesidir. Bu sayede saldırganlar, belirli bir yanıt için doğru tek seferlik rastgele sayıyı bilmeden komut dosyasını yürütemez.
Neden katı bir CSP kullanmalısınız?
Sitenizde script-src www.googleapis.com gibi görünen bir CSP varsa siteler arası saldırılara karşı etkili olmayabilir. Bu tür İGP'lere izin verilenler listesi İGP'si denir. Çok fazla özelleştirme gerektirirler ve saldırganlar tarafından atlatılabilirler.
Kriptografik tek seferlik rastgele sayılara veya karma değerlere dayalı katı CSP'ler bu sorunları önler.
Katı CSP yapısı
Temel katı İçerik Güvenliği Politikası, aşağıdaki HTTP yanıt başlıklarından birini kullanır:
Tek seferlik rastgele sayı tabanlı katı CSP
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Karma tabanlı katı CSP
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Aşağıdaki özellikler, bu tür bir CSP'yi "katı" ve dolayısıyla güvenli hale getirir:
- Sitenin geliştiricisinin, kullanıcının tarayıcısında yürütmek için hangi
<script>etiketlerine güvendiğini belirtmek amacıyla tek seferlik rastgele sayılar'nonce-{RANDOM}'veya karma değerler'sha256-{HASHED_INLINE_SCRIPT}'kullanır. - Güvenilir bir komut dosyasının oluşturduğu komut dosyalarının yürütülmesine otomatik olarak izin vererek tek seferlik rastgele sayı veya karma tabanlı bir CSP dağıtma çabasını azaltmak için
'strict-dynamic'değerini ayarlar. Bu sayede, çoğu üçüncü taraf JavaScript kitaplığının ve widget'ının kullanımı da engellenmez. - URL izin verilenler listelerini temel almadığından yaygın İGP atlamalarından etkilenmez.
- Satır içi etkinlik işleyiciler veya
javascript:URI'leri gibi güvenilmeyen satır içi komut dosyalarını engeller. - Flash gibi tehlikeli eklentileri devre dışı bırakmak için
object-srckısıtlaması uygular. <base>etiketlerinin eklenmesini engellemek içinbase-urikısıtlar. Bu sayede, saldırganların göreceli URL'lerden yüklenen komut dosyalarının konumlarını değiştirmesi engellenir.
Katı bir İGP benimseyin
Katı bir İGP benimsemek için şunları yapmanız gerekir:
- Uygulamanızın tek seferlik rastgele sayı veya karma tabanlı bir CSP ayarlayıp ayarlamayacağına karar verin.
- Katı İGP yapısı bölümündeki İGP'yi kopyalayın ve uygulamanızda yanıt başlığı olarak ayarlayın.
- HTML şablonlarını ve istemci tarafı kodunu, İGP ile uyumlu olmayan kalıpları kaldıracak şekilde yeniden düzenleyin.
- CSP'nizi dağıtın.
Sitenizde İGP olup olmadığını ve XSS'ye karşı etkili olacak kadar katı olup olmadığını kontrol etmek için bu süreç boyunca Lighthouse (--preset=experimental işaretli v7.3.0 ve sonraki sürümler) En İyi Uygulamalar denetimini kullanabilirsiniz.
1. Adım: Tek seferlik rastgele sayı veya karma tabanlı bir CSP'ye ihtiyacınız olup olmadığına karar verin
İki tür katı İGP'nin işleyiş şekli aşağıda açıklanmıştır:
Tek seferlik rastgele sayı tabanlı CSP
Tek seferlik rastgele sayı tabanlı İGP ile çalışma zamanında rastgele bir sayı oluşturur, bu sayıyı İGP'nize ekler ve sayfanızdaki her komut dosyası etiketiyle ilişkilendirirsiniz. Saldırgan, sayfanıza kötü amaçlı bir komut dosyası ekleyemez veya çalıştıramaz. Çünkü bu komut dosyası için doğru rastgele bir sayıyı tahmin etmesi gerekir. Bu yöntem yalnızca sayı tahmin edilemiyorsa ve her yanıt için çalışma zamanında yeni bir sayı oluşturuluyorsa işe yarar.
Sunucuda oluşturulan HTML sayfaları için tek seferlik rastgele sayı tabanlı bir CSP kullanın. Bu sayfalar için her yanıta yeni bir rastgele bir sayı oluşturabilirsiniz.
Karma tabanlı CSP
Karma tabanlı bir CSP için her satır içi komut dosyası etiketinin karması CSP'ye eklenir. Her komut dosyasının farklı bir karma değeri vardır. Saldırgan, sayfanıza kötü amaçlı bir komut dosyası ekleyemez veya çalıştıramaz. Çünkü bu komut dosyasının karma değerinin çalıştırılabilmesi için CSP'nizde olması gerekir.
Statik olarak sunulan HTML sayfaları veya önbelleğe alınması gereken sayfalar için karma tabanlı bir CSP kullanın. Örneğin, Angular veya React gibi çerçevelerle oluşturulan ve sunucu tarafı oluşturma olmadan statik olarak sunulan tek sayfalık web uygulamaları için karma tabanlı bir CSP kullanabilirsiniz.
2. adım: Katı bir CSP ayarlayın ve komut dosyalarınızı hazırlayın
CSP ayarlarken birkaç seçeneğiniz vardır:
- Yalnızca raporlama modu (
Content-Security-Policy-Report-Only) veya zorunlu mod (Content-Security-Policy). Yalnızca raporlama modunda CSP henüz kaynakları engellemez. Bu nedenle, sitenizdeki hiçbir şey bozulmaz ancak engellenebilecek her şeyle ilgili hataları görebilir ve rapor alabilirsiniz. Yerel olarak, CSP'nizi ayarlarken her iki mod da tarayıcı konsolundaki hataları gösterdiğinden bu durumun pek bir önemi yoktur. Zorunlu mod, taslak İGP'nizin engellediği kaynakları bulmanıza yardımcı olabilir. Bir kaynağın engellenmesi, sayfanızın bozuk görünmesine neden olabilir. Yalnızca raporlama modu, sürecin ilerleyen aşamalarında daha faydalı olur (5. adıma bakın). - Üstbilgi veya HTML
<meta>etiketi. Yerel geliştirme için<meta>etiketi, İGP'nizi ayarlamak ve sitenizi nasıl etkilediğini hızlıca görmek açısından daha kullanışlı olabilir. Ancak:- Daha sonra, CSP'nizi üretimde dağıtırken HTTP üstbilgisi olarak ayarlamanızı öneririz.
- CSP'nizi yalnızca raporlama moduna ayarlamak istiyorsanız CSP meta etiketleri yalnızca raporlama modunu desteklemediğinden bunu başlık olarak ayarlamanız gerekir.
Uygulamanızda aşağıdaki Content-Security-Policy HTTP yanıt başlığını ayarlayın:
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
CSP için tek seferlik rastgele sayı oluşturma
Tek seferlik rastgele sayı, her sayfa yüklemesinde yalnızca bir kez kullanılan rastgele bir sayıdır. Tek seferlik rastgele sayı tabanlı CSP, yalnızca saldırganlar tek seferlik rastgele sayı değerini tahmin edemezse XSS'yi azaltabilir. İGP tek seferlik rastgele sayısı şu özelliklere sahip olmalıdır:
- Kriptografik olarak güçlü bir rastgele değer (ideal olarak 128+ bit uzunluğunda)
- Her yanıt için yeni oluşturulur.
- Base64 kodlu
Sunucu tarafı çerçevelerine İGP tek seferlik rastgele sayısının nasıl ekleneceğine dair bazı örnekleri aşağıda bulabilirsiniz:
- Django (python)
- Express (JavaScript):
const app = express(); app.get('/', function(request, response) { // Generate a new random nonce value for every response. const nonce = crypto.randomBytes(16).toString("base64"); // Set the strict nonce-based CSP response header const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`; response.set("Content-Security-Policy", csp); // Every <script> tag in your application should set the `nonce` attribute to this value. response.render(template, { nonce: nonce }); });
<script> öğelerine nonce özelliği ekleyin.
Tek seferlik rastgele sayı tabanlı CSP'de her <script> öğesinin, CSP başlığında belirtilen rastgele tek seferlik rastgele sayı değeriyle eşleşen bir nonce özelliği olmalıdır. Tüm komut dosyaları aynı tek seferlik rastgele sayıya sahip olabilir. İlk adım, bu özellikleri tüm komut dosyalarına ekleyerek CSP'nin bunlara izin vermesini sağlamaktır.
Uygulamanızda aşağıdaki Content-Security-Policy HTTP yanıt başlığını ayarlayın:
Content-Security-Policy: script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
Birden fazla satır içi komut dosyası için söz dizimi aşağıdaki gibidir:
'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}'.
Kaynaklı komut dosyalarını dinamik olarak yükleme
Satır içi komut dosyası kullanarak üçüncü taraf komut dosyalarını dinamik olarak yükleyebilirsiniz.
<script>
var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js'];
scripts.forEach(function(scriptUrl) {
var s = document.createElement('script');
s.src = scriptUrl;
s.async = false; // to preserve execution order
document.head.appendChild(s);
});
</script>{HASHED_INLINE_SCRIPT} yer tutucusunu değiştirerek CSP yanıt başlığına eklemeniz gerekir. Karma sayısını azaltmak için tüm satır içi komut dosyalarını tek bir komut dosyasında birleştirebilirsiniz. Bunun nasıl çalıştığını görmek için bu örneğe ve koduna bakın.
<script src="https://example.org/foo.js"></script> <script src="https://example.org/bar.js"></script>
integrity özellikleri olmadığı için bu komut dosyalarını engeller.
Komut dosyası yüklemeyle ilgili dikkat edilmesi gereken noktalar
Satır içi komut dosyası örneği, bar önce yüklense bile foo öğesinin bar öğesinden önce yürütülmesini sağlamak için s.async = false ekler. Bu snippet'te, komut dosyaları dinamik olarak eklendiğinden, komut dosyaları yüklenirken s.async = false ayrıştırıcıyı engellemez. Ayrıştırıcı, yalnızca komut dosyaları yürütülürken durur. Bu, async komut dosyaları için de geçerlidir. Ancak bu snippet'le ilgili olarak
şunları unutmayın:
-
Komut dosyalarından biri veya her ikisi, dokümanın indirilmesi tamamlanmadan önce yürütülebilir. Komut dosyaları yürütülürken belgenin hazır olmasını istiyorsanız komut dosyalarını eklemeden önce
DOMContentLoadedetkinliğini bekleyin. Komut dosyaları yeterince erken indirilmeye başlamadığı için bu durum performans sorununa neden oluyorsa sayfanın daha önceki kısımlarında önceden yükleme etiketleri kullanın. -
defer = truehiçbir şey yapmaz. Bu davranışa ihtiyacınız varsa komut dosyasını gerektiğinde manuel olarak çalıştırın.
3. adım: HTML şablonlarını ve istemci tarafı kodunu yeniden düzenleyin
Satır içi etkinlik işleyiciler (ör. onclick="…", onerror="…") ve JavaScript URI'leri (<a href="javascript:…">), komut dosyalarını çalıştırmak için kullanılabilir. Bu, XSS hatası bulan bir saldırganın bu tür bir HTML ekleyebileceği ve kötü amaçlı JavaScript çalıştırabileceği anlamına gelir. Tek seferlik rastgele sayı veya karma tabanlı bir CSP, bu tür işaretlemelerin kullanılmasını yasaklar.
Siteniz bu kalıplardan herhangi birini kullanıyorsa bunları daha güvenli alternatiflerle yeniden düzenlemeniz gerekir.
Önceki adımda CSP'yi etkinleştirdiyseniz CSP uyumsuz bir kalıbı her engellediğinde konsolda CSP ihlallerini görebilirsiniz.
Çoğu durumda sorun kolayca çözülebilir:
Satır içi etkinlik işleyicileri yeniden düzenleme
<span id="things">A thing.</span>
<script nonce="${nonce}">
document.getElementById('things').addEventListener('click', doThings);
</script><span onclick="doThings();">A thing.</span>
javascript: URI'yi yeniden düzenle
<a id="foo">foo</a>
<script nonce="${nonce}">
document.getElementById('foo').addEventListener('click', linkClicked);
</script><a href="javascript:linkClicked()">foo</a>
JavaScript'inizden eval() öğesini kaldırın
Uygulamanız JSON dize serileştirmelerini JS nesnelerine dönüştürmek için eval() kullanıyorsa bu tür örnekleri JSON.parse() olarak yeniden düzenlemeniz gerekir. Bu yöntem aynı zamanda daha hızlıdır.
eval() kullanımının tamamını kaldıramıyorsanız katı bir tek seferlik rastgele sayı tabanlı İGP ayarlayabilirsiniz ancak 'unsafe-eval' İGP anahtar kelimesini kullanmanız gerekir. Bu da politikanızın güvenliğini biraz azaltır.
Bu ve benzeri yeniden düzenleme örneklerini bu katı CSP codelab'inde bulabilirsiniz:
4. adım (isteğe bağlı): Eski tarayıcı sürümlerini desteklemek için yedekler ekleyin
Browser Support
Eski tarayıcı sürümlerini desteklemeniz gerekiyorsa:
strict-dynamickullanmak için Safari'nin önceki sürümlerinde yedek olarakhttps:eklemeniz gerekir. Bu izni verdiğinizde:strict-dynamicözelliğini destekleyen tüm tarayıcılarhttps:yedek politikasını yoksayar. Bu nedenle, politikanın gücü azalmaz.- Eski tarayıcılarda, harici kaynaklı komut dosyaları yalnızca HTTPS kaynağından geliyorsa yüklenebilir. Bu, katı bir CSP'den daha az güvenlidir ancak yine de
javascript:URI'larının yerleştirilmesi gibi bazı yaygın XSS nedenlerini önler.
- Çok eski tarayıcı sürümleriyle (4 yıldan eski) uyumluluğu sağlamak için yedek olarak
unsafe-inlineekleyebilirsiniz. Tüm yeni tarayıcılar, bir CSP tek seferlik rastgele sayısı veya karma değeri varsaunsafe-inlinedeğerini yoksayar.
Content-Security-Policy:
script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
object-src 'none';
base-uri 'none';
5. adım: CSP'nizi dağıtın
İçerik Güvenliği Politikası'nın yerel geliştirme ortamınızdaki meşru komut dosyalarını engellemediğini onayladıktan sonra İçerik Güvenliği Politikası'nı hazırlık ortamına, ardından üretim ortamınıza dağıtabilirsiniz:
- (İsteğe bağlı)
Content-Security-Policy-Report-Onlybaşlığını kullanarak CSP'nizi yalnızca raporlama modunda dağıtın. Yalnızca raporlama modu, CSP kısıtlamalarını uygulamaya başlamadan önce üretimde yeni bir CSP gibi zarar veren değişiklikleri test etmek için kullanışlıdır. Yalnızca raporlama modunda İGP'niz uygulamanızın davranışını etkilemez ancak tarayıcı, İGP'nizle uyumlu olmayan kalıplarla karşılaştığında konsol hataları ve ihlal raporları oluşturmaya devam eder. Böylece son kullanıcılarınız için nelerin bozulacağını görebilirsiniz. Daha fazla bilgi için Reporting API'ye bakın. - İçerik Güvenliği Politikası'nın son kullanıcılarınız için sitenizi bozmayacağından emin olduğunuzda
Content-Security-Policyyanıt başlığını kullanarak İçerik Güvenliği Politikası'nı dağıtın. CSP'nizi sunucu tarafında bir HTTP üstbilgisi kullanarak ayarlamanızı öneririz. Bu yöntem,<meta>etiketinden daha güvenlidir. Bu adımı tamamladıktan sonra CSP'niz, uygulamanızı XSS'ye karşı korumaya başlar.
Sınırlamalar
Katı bir İGP, genellikle XSS'yi azaltmaya yardımcı olan güçlü bir ek güvenlik katmanı sağlar. Çoğu durumda İGP, javascript: URI'leri gibi tehlikeli kalıpları reddederek saldırı yüzeyini önemli ölçüde azaltır. Ancak kullandığınız CSP türüne (tek seferlik rastgele sayılar, karma değerler, 'strict-dynamic' ile veya olmadan) bağlı olarak CSP'nin uygulamanızı korumadığı durumlar da vardır:
- Bir komut dosyasına nonce eklediğinizde, doğrudan gövdeye veya
<script>öğesininsrcparametresine bir ekleme yapılır. - Dinamik olarak oluşturulan komut dosyalarının konumlarına (
document.createElement('script')) eklemeler varsa. Bu, bağımsız değişkenlerinin değerlerine görescriptDOM düğümleri oluşturan kitaplık işlevlerini de kapsar. Buna jQuery'nin.html()gibi bazı yaygın API'lerinin yanı sıra jQuery < 3.0'daki.get()ve.post()de dahildir. - Eski AngularJS uygulamalarında şablon eklemeleri varsa. Bir AngularJS şablonuna kod yerleştirebilen saldırganlar, bu şablonu rastgele JavaScript yürütmek için kullanabilir.
- Politika
'unsafe-eval'içeriyorsaeval(),setTimeout()ve nadiren kullanılan diğer birkaç API'ye ekleme yapılır.
Geliştiriciler ve güvenlik mühendisleri, kod incelemeleri ve güvenlik denetimleri sırasında bu tür kalıplara özellikle dikkat etmelidir. Bu durumlarla ilgili daha fazla bilgiyi İçerik Güvenliği Politikası: Güçlendirme ve Azaltma Arasındaki Başarılı Bir Karışıklık başlıklı makalede bulabilirsiniz.
Daha fazla bilgi
- CSP Is Dead, Long Live CSP! On the Insecurity of Whitelists and the Future of Content Security Policy
- CSP Evaluator
- LocoMoco Conference: Content Security Policy - A successful mess between hardening and mitigation
- Google I/O konuşması: Modern platform özellikleriyle web uygulamalarının güvenliğini sağlama