Bir web uygulamasına kötü amaçlı komut dosyaları yerleştirme olanağı olan siteler arası komut dosyası çalıştırma (XSS), on yıldan uzun süredir en büyük web güvenlik 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. Bir CSP'yi yapılandırmak için Content-Security-Policy
HTTP üstbilgisini bir web sayfasına ekleyin ve kullanıcı aracısının bu sayfa için hangi kaynakları yükleyebileceğini kontrol eden değerleri ayarlayın.
Bu sayfada, çoğu yapılandırmada atlanabilir oldukları için sayfayı genellikle XSS'ye açık bırakan, yaygın olarak kullanılan ana makine izin verilenler listesi tabanlı İGP'ler yerine XSS'yi azaltmak için tek seferlik değerlere veya karma oluşturma işlemlerine dayalı bir İGP'nin nasıl kullanılacağı açıklanmaktadır.
Anahtar terim: Tek seferlik, bir <script>
etiketini güvenilir olarak işaretlemek için kullanabileceğiniz, yalnızca bir kez kullanılan rastgele bir sayıdır.
Temel 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 <script>
etiketini güvenilir olarak işaretlemek için bir karma oluşturma işlemi (ör. SHA-256) kullanabilirsiniz.
Tek seferlik rastgele sayılara veya karma oluşturma işlemlerine dayalı bir İçerik Güvenliği Politikası genellikle katı CSP olarak adlandırılır. Bir uygulamada katı bir CSP kullanıldığında, HTML ekleme kusurlarını bulan saldırganlar genellikle bu kusurları kullanarak tarayıcıyı, güvenlik açığı olan bir belgede kötü amaçlı komut dosyaları yürütmeye zorlayamaz. Bunun nedeni, katı CSP'nin yalnızca karma oluşturma işlemi uygulanmış komut dosyalarına veya sunucuda doğru tek seferlik değere sahip komut dosyalarına izin vermesidir. Bu sayede saldırganlar, belirli bir yanıt için doğru tek seferlik değeri bilmeden komut dosyasını çalıştıramaz.
Neden katı bir CSP kullanmalısınız?
Sitenizde script-src www.googleapis.com
gibi görünen bir CSP varsa muhtemelen siteler arası saldırılara karşı etkili değildir. Bu tür bir CSP'ye izin verilenler listesi CSP'si denir. Çok fazla özelleştirme gerektirirler ve saldırganlar tarafından atlanabilirler.
Şifreleme tek seferlik anahtarlarına veya karma oluşturma işlemlerine dayalı katı CSP'ler bu tür tuzaklar oluşturmaz.
Katı CSP yapısı
Temel ve katı bir İçerik Güvenliği Politikası, aşağıdaki HTTP yanıt başlıklarından birini kullanır:
Tek seferlik anahtara dayalı katı İGP
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Karma oluşturmaya dayalı katı İGP
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Aşağıdaki özellikler, bu gibi bir CSP'yi "katı" ve dolayısıyla güvenli hale getirir:
- Site geliştiricisinin kullanıcının tarayıcısında çalıştırılmasına güvendiği
<script>
etiketlerini belirtmek için tek seferlik'nonce-{RANDOM}'
veya karma oluşturma'sha256-{HASHED_INLINE_SCRIPT}'
değerleri kullanır. - Güvenilir bir komut dosyasının oluşturduğu komut dosyalarının yürütülmesine otomatik olarak izin vererek nonce veya karma tabanlı bir CSP dağıtma çabasını azaltmak için
'strict-dynamic'
değerini ayarlar. Bu işlem, çoğu üçüncü taraf JavaScript kitaplığının ve widget'ının kullanımını da engeller. - URL izin verilenler listelerine dayalı olmadığından yaygın CSP 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. object-src
, Flash gibi tehlikeli eklentileri devre dışı bırakacak şekilde kısıtlanır.base-uri
etiketlerinin eklenmesi için<base>
etiketlerini engellemek üzerebase-uri
'ü kısıtlar. Bu, saldırganların göreli URL'lerden yüklenen komut dosyalarının konumlarını değiştirmesini engeller.
Sıkı bir İGP'yi benimseyin
Katı bir İGP uygulamak için:
- Uygulamanızın nonce tabanlı mı yoksa karma oluşturma tabanlı mı bir CSP ayarlamasına karar verin.
- Katı CSP yapısı bölümündeki CSP'yi kopyalayıp uygulamanızda yanıt üstbilgisi olarak ayarlayın.
- CSP ile uyumlu olmayan kalıpları kaldırmak için HTML şablonlarını ve istemci tarafı kodunu yeniden yapılandırın.
- İGP'nizi dağıtın.
Sitenizde CSP'nin olup olmadığını ve XSS'ye karşı etkili olacak kadar katı olup olmadığını kontrol etmek için bu süreçte Lighthouse (--preset=experimental
işaretiyle 7.3.0 ve sonraki sürümler) En İyi Uygulamalar denetimini kullanabilirsiniz.
1. Adım: Nonce veya karma oluşturma tabanlı bir CSP'ye ihtiyacınız olup olmadığına karar verin
İki tür katı CSP'nin işleyiş şekli:
Tek seferlik sayı tabanlı CSP
Tek kullanımlık sayıya dayalı bir CSP'de çalışma zamanında rastgele bir sayı oluşturur, bu sayıyı CSP'nize ekler ve sayfanızdaki her komut dosyası etiketiyle ilişkilendirirsiniz. Saldırgan, sayfanıza kötü amaçlı bir komut dosyası dahil edemez veya çalıştıramaz. Bunun için, komut dosyasının doğru rastgele sayısını tahmin etmesi gerekir. Bu yöntem yalnızca sayı tahmin edilemiyorsa ve her yanıt için çalışma zamanında yeni oluşturuluyorsa işe yarar.
Sunucuda oluşturulan HTML sayfaları için tek seferlik rastgele sayı tabanlı bir CSP kullanın. Bu sayfalarda her yanıt için yeni bir rastgele sayı oluşturabilirsiniz.
Karma oluşturmaya dayalı CSP
Karma oluşturmaya dayalı bir CSP için her satır içi komut dosyası etiketinin karması CSP'ye eklenir. Her komut dosyasının farklı bir karması vardır. Bir saldırgan, sayfanıza kötü amaçlı bir komut dosyası ekleyemez veya çalıştıramaz. Bunun nedeni, komut dosyasının çalışması için karma değerinin CSP'nizde bulunması gerektiğidir.
Statik olarak sunulan veya önbelleğe alınması gereken HTML sayfaları için karma oluşturmaya dayalı bir CSP kullanın. Örneğin, Angular, React gibi çerçevelerle oluşturulan ve sunucu tarafı oluşturma olmadan statik olarak sunulan tek sayfalık web uygulamaları için karma oluşturmaya dayalı bir CSP kullanabilirsiniz.
2. adım: Katı bir CSP ayarlayın ve komut dosyalarınızı hazırlayın
CSP ayarlama konusunda birkaç seçeneğiniz vardır:
- Yalnızca raporlama modu (
Content-Security-Policy-Report-Only
) veya yaptırım modu (Content-Security-Policy
). Yalnızca raporlama modunda CSP henüz kaynakları engellemez. Bu nedenle, sitenizde hiçbir şey bozulmaz ancak engellenecek öğeler için raporlar alabilir ve hataları görebilirsiniz. Yerel olarak CSP'nizi ayarlarken bu durum pek önemli değildir çünkü her iki mod da hataları tarayıcı konsolunda gösterir. Bir kaynağın engellenmesi sayfanızı bozuk gösterebileceğinden, yaptırım modu, taslak CSP'nizin engellediği kaynakları bulmanıza yardımcı olabilir. Yalnızca rapor modu, sürecin ilerleyen aşamalarında en kullanışlı hale gelir (5. Adım'a bakın). - Başlık veya HTML
<meta>
etiketi. Yerel geliştirme için CSP'nizde ayarlamalar yapmak ve bunun sitenizi nasıl etkilediğini hızlıca görmek üzere<meta>
etiketi daha kullanışlı olabilir. Ancak:- Daha sonra, CSP'nizi üretime dağıtırken HTTP üst bilgisi olarak ayarlamanızı öneririz.
- CSP'nizi yalnızca rapor modunda ayarlamak istiyorsanız CSP meta etiketleri yalnızca rapor modunu desteklemediğinden bunu başlık olarak ayarlamanız gerekir.
Uygulamanızda aşağıdaki Content-Security-Policy
HTTP yanıt üst bilgisini ayarlayın:
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
CSP için tek seferlik bir sayı oluşturma
Tek seferlik sayı, sayfa yükleme başına yalnızca bir kez kullanılan rastgele bir sayıdır. Tek seferlik değere dayalı CSP, yalnızca saldırganlar tek seferlik değeri tahmin edemiyorsa XSS'yi azaltabilir. CSP tek seferlik rastgele sayı:
- Kriptografik olarak güçlü bir rastgele değer (ideal olarak 128 bitten uzun)
- Her yanıt için yeni oluşturulur.
- Base64 kodlu
Sunucu tarafı çerçevelere CSP tek seferlik rastgele sayı eklemeyle ilgili 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 ekleme
Tek seferlik rastgele değere dayalı CSP'de her <script>
öğesinin, CSP başlığında belirtilen rastgele tek seferlik değerle eşleşen bir nonce
özelliği olmalıdır. Tüm komut dosyalarında aynı tek seferlik İlk adım, CSP'nin izin vermesi için bu özellikleri tüm komut dosyalarına eklemektir.
Uygulamanızda aşağıdaki Content-Security-Policy
HTTP yanıt üst bilgisini 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>
<script src="https://example.org/foo.js"></script> <script src="https://example.org/bar.js"></script>
Komut dosyası yüklemeyle ilgili dikkat edilmesi gereken noktalar
Satır içi komut dosyası örneğinde, bar
önce yüklenirse bile foo
'un bar
'den önce yürütülmesini sağlamak için s.async = false
eklenir. Bu snippet'te, komut dosyaları dinamik olarak eklendiğinden s.async = false
, komut dosyaları yüklenirken ayrıştırıcıyı engellemez. Ayrıştırıcı, yalnızca async
komut dosyalarında olduğu gibi komut dosyalarının yürütülmesi sırasında durur. Ancak bu snippet'te şunu unutmayın:
-
Komut dosyalarından biri veya ikisi de doküman indirilmeden önce yürütülebilir. Belgenin, komut dosyaları çalıştırıldığında hazır olmasını istiyorsanız komut dosyalarını eklemeden önce
DOMContentLoaded
etkinliğini bekleyin. Bu, komut dosyalarının yeterince erken indirilmemesi nedeniyle performans sorununa yol açıyorsa sayfanın daha başında önyükleme etiketleri kullanın. -
defer = true
hiçbir işlem 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 yapılandırın
Komut dosyalarını çalıştırmak için satır içi etkinlik işleyiciler (onclick="…"
, onerror="…"
gibi) ve JavaScript URI'leri (<a href="javascript:…">
) kullanılabilir. Bu, XSS hatası bulan bir saldırganın bu tür HTML'leri yerleştirebileceği ve kötü amaçlı JavaScript çalıştırabileceği anlamına gelir. Tek seferlik veya karma oluşturma tabanlı CSP, bu tür işaretlemelerin kullanılmasını yasaklar.
Sitenizde bu kalıplardan herhangi biri kullanılıyorsa bunları daha güvenli alternatiflerle yeniden yapılandırmanız gerekir.
CSP'yi önceki adımda etkinleştirdiyseniz CSP uyumlu olmayan bir deseni her engellediğinde konsolda CSP ihlallerini görebilirsiniz.
Çoğu durumda, çözüm basittir:
Satır içi etkinlik işleyicileri yeniden yapılandırın
<span id="things">A thing.</span> <script nonce="${nonce}"> document.getElementById('things').addEventListener('click', doThings); </script>
<span onclick="doThings();">A thing.</span>
javascript:
URI'lerini yeniden düzenleme
<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 örnekleri JSON.parse()
olarak yeniden yapılandırmanız gerekir. Bu yöntem aynı zamanda daha hızlıdır.
eval()
'nin tüm kullanımlarını kaldıramıyorsanız yine de katı bir tek seferlik değere dayalı CSP ayarlayabilirsiniz ancak 'unsafe-eval'
CSP anahtar kelimesini kullanmanız gerekir. Bu da politikanızın güvenliğini biraz azaltır.
Bu ve bu tür yeniden yapılandırmanın daha fazla örneğini bu katı CSP kod laboratuvarında bulabilirsiniz:
4. Adım (İsteğe bağlı): Eski tarayıcı sürümlerini desteklemek için yedek çözümler ekleyin
Eski tarayıcı sürümlerini desteklemeniz gerekiyorsa:
strict-dynamic
'ü kullanmak için Safari'nin önceki sürümleri için yedek olarakhttps:
eklemeniz gerekir. Bunu yaptığınızda:strict-dynamic
'ü destekleyen tüm tarayıcılarhttps:
yedek seçeneğini yoksayar. Bu nedenle, bu durum politikanın gücünü azaltmaz.- 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'lerinin enjeksiyonu gibi bazı yaygın XSS nedenlerini önler.
- Çok eski tarayıcı sürümleriyle (4 yıldan eski) uyumluluk sağlamak için yedek olarak
unsafe-inline
ekleyebilirsiniz. CSP tek seferlik rastgele sayı veya karma oluşturma mevcutsa tüm son tarayıcılarunsafe-inline
içeriğini 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
CSP'nizin yerel geliştirme ortamınızdaki meşru komut dosyalarını engellemediğini onayladıktan sonra CSP'nizi aşamalı olarak, ardından üretim ortamınıza dağıtabilirsiniz:
- (İsteğe bağlı)
Content-Security-Policy-Report-Only
üstbilgisini kullanarak CSP'nizi yalnızca raporlama modunda dağıtın. Yalnızca rapor modu, CSP kısıtlamalarını uygulamaya başlamadan önce üretimde yeni bir CSP gibi önemli değişiklikleri test etmek için kullanışlıdır. Yalnızca rapor modunda İGP'niz uygulamanızın davranışını etkilemez ancak tarayıcı, İGP'nizle uyumlu olmayan kalıplarla karşılaştığında yine de konsol hataları ve ihlal raporları oluşturur. Böylece, son kullanıcılarınız için nelerin bozulacağını görebilirsiniz. Daha fazla bilgi için Reporting API başlıklı makaleyi inceleyin. - CSP'nizin son kullanıcılarınız için sitenizi bozmayacağından emin olduğunuzda
Content-Security-Policy
yanıt üstbilgisini kullanarak CSP'nizi dağıtın.<meta>
etiketinden daha güvenli olduğu için CSP'nizi sunucu tarafında bir HTTP üst bilgisi kullanarak ayarlamanızı öneririz. Bu adımı tamamladıktan sonra CSP'niz uygulamanızı XSS'den korumaya başlar.
Sınırlamalar
Katı bir CSP, genellikle XSS'yi azaltmaya yardımcı olan güçlü bir ek güvenlik katmanı sağlar. Çoğu durumda CSP, 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 (nonce, karma oluşturma, 'strict-dynamic'
ile veya olmadan) bağlı olarak CSP'nin uygulamanızı korumadığı durumlar da vardır:
- Bir komut dosyasını tek seferlik olarak atarsanız ancak doğrudan gövdeye veya söz konusu
<script>
öğesininsrc
parametresine bir ekleme varsa. - Dinamik olarak oluşturulan komut dosyalarının (
document.createElement('script')
) konumlarına, bağımsız değişkenlerinin değerlerine görescript
DOM düğümü oluşturan tüm kitaplık işlevleri dahil olmak üzere enjeksiyon varsa. Buna jQuery'nin.html()
gibi bazı yaygın API'leri ve jQuery 3.0'dan önceki sürümlerdeki.get()
ve.post()
dahildir. - Eski AngularJS uygulamalarında şablon ekleme varsa. Bir AngularJS şablonuna kod ekleyebilen bir saldırgan, bu şablonu rastgele JavaScript yürütmek için kullanabilir.
- Politika
'unsafe-eval'
,eval()
,setTimeout()
ve nadiren kullanılan birkaç diğer API'ye yönelik enjeksiyonlar içeriyorsa.
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 durumlar hakkında daha fazla bilgiyi İçerik Güvenliği Politikası: Güçlendirme ve Azaltma Arasında Başarılı Bir Karmaşa başlıklı makalede bulabilirsiniz.
Daha fazla bilgi
- CSP Öldü, Yaşasın CSP! Beyaz Listelerin Güvensizliği ve İçerik Güvenliği Politikasının Geleceği Hakkında
- CSP Değerlendiricisi
- LocoMoco Konferansı: İçerik Güvenliği Politikası - Güçlendirme ve azaltma arasında başarılı bir karmaşa
- Google I/O konuşması: Modern Platform Özellikleri ile Web Uygulamalarını Güvence altına Alma