Bir web uygulamasına kötü amaçlı komut dosyaları yerleştirme özelliği olan 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.
İç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 karma (örneğin, 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 uygulama katı bir CSP kullandığında, HTML ekleme hataları bulan saldırganlar genellikle bunları kullanarak tarayıcıyı, güvenlik açığı olan bir dokümandaki kötü amaçlı komut dosyalarını 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 İGP varsa bu İGP 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 bunun gibi bir CSP'yi "katı" ve dolayısıyla güvenli hale getirir:
- Sitenin geliştiricisinin, kullanıcının tarayıcısında çalışmak için güvendiği
<script>
etiketlerini belirtmek için nonce'nonce-{RANDOM}'
veya'sha256-{HASHED_INLINE_SCRIPT}'
karmalarını kullanır. - Güvenilir bir komut dosyasının oluşturduğu komut dosyalarının yürütülmesine otomatik olarak izin vererek
'strict-dynamic'
'i, tek seferlik veya karma tabanlı bir CSP'yi dağıtmaya yönelik çabayı azaltacak şekilde 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. - Flash gibi tehlikeli eklentileri devre dışı bırakmak için
object-src
ürününü kısıtlar. 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 önler.
Katı bir İGP 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
Ortalama olmayan bir CSP ile çalışma zamanında rastgele bir sayı oluşturur, bu sayıyı CSP'nize dahil eder 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 işlem yalnızca sayı tahmin edilemiyorsa çalışır ve her yanıt için çalışma zamanında yeni oluşturulur.
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 tabanlı 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 olması 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 veya diğerleri gibi çerçevelerle oluşturulmuş 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:
- Salt rapor modu (
Content-Security-Policy-Report-Only
) veya yaptırım modu (Content-Security-Policy
). Yalnızca rapor modunda İGP henüz kaynakları engellemez. Bu nedenle sitenizdeki hiçbir şey çalışmaz ancak engellenmiş olabilecek tüm hatalarla ilgili raporlar görebilir ve raporlar alabilirsiniz. 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ın bozuk görünmesine neden olabileceğinden, yaptırım modu, taslak CSP'nizdeki 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.<meta>
etiketi, yerel gelişim için İGP'nizde ufak değişiklikler yapıp sitenizi nasıl etkilediğini hızlıca görmek açısından daha kullanışlı olabilir. Ancak:- Daha sonra, CSP'nizi üretime dağıtırken bunu bir HTTP başlığı olarak ayarlamanızı öneririz.
- CSP'nizi yalnızca rapor modunda ayarlamak isterseniz CSP meta etiketleri salt rapor modunu desteklemediğinden bunu bir 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 rastgele sayı oluşturma
Tek seferlik rastgele 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ısı:
- Kriptografik olarak güçlü bir rastgele değer (ideal olarak 128 bitten uzun)
- Her yanıt için yeni oluşturulur
- Base64 olarak kodlanmış
Sunucu tarafı çerçevelere CSP tek seferlik rastgele sayı eklemeyle ilgili bazı örnekleri aşağıda bulabilirsiniz:
- Django (python)
- Ekspres (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 anahtara dayalı bir CSP'de her <script>
öğesinin, CSP başlığında belirtilen rastgele tek seferlik anahtar değeriyle eşleşen bir nonce
özelliği olmalıdır. Tüm komut dosyalarında aynı tek seferlik İ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 ü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 bir şekilde 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ği, bar
önce yüklense bile foo
öğesinin bar
öncesinde yürütülmesini sağlamak için s.async = false
öğesini ekler. 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ı, async
komut dosyalarında olduğu gibi yalnızca komut dosyaları yürütülürken durur. Ancak bu snippet'te şunu unutmayın:
-
Komut dosyalarından biri veya her ikisi, dokümanın indirilmesi tamamlanmadan ö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 (>,
> 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.
Siteniz bu kalıplardan herhangi birini kullanıyorsa bunları daha güvenli alternatifler olacak şekilde yeniden düzenlemeniz 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 >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 dizesi serileştirmelerini JS nesnelerine dönüştürmek için eval()
kullanıyorsa bu gibi örnekleri JSON.parse()
olarak yeniden düzenlemeniz gerekir. Bu işlem de 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.
Aşağıdaki katı CSP codelab'inde bunları ve bu gibi yeniden düzenlemeyle ilgili daha fazla örnek bulabilirsiniz:
4. Adım (İsteğe bağlı): Eski tarayıcı sürümlerini desteklemek için yedek ekleyin
Eski tarayıcı sürümlerini desteklemeniz gerekiyorsa:
strict-dynamic
kullanımı, Safari'nin önceki sürümleri için yedek olarakhttps:
eklenmesini gerektirir. Bunu yaptığınızda:strict-dynamic
özelliğini destekleyen tüm tarayıcılarhttps:
yedeğini yok sayar. Bu nedenle bu, 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üvenli olsa da
javascript:
URI'lerinin yerleştirilmesi gibi bazı yaygın XSS nedenlerini engeller.
- Ç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: İGP'nizi dağıtın
CSP'nizin yerel geliştirme ortamınızda geçerli komut dosyalarını engellemediğini onayladıktan sonra, CSP'nizi önce hazırlık ve 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. CSP'nizi<meta>
etiketinden daha güvenli olduğu için sunucu tarafında HTTP üstbilgisi 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 rastgele kapatırsanız ancak doğrudan ilgili
<script>
öğesinin gövdesine veyasrc
parametresine bir ekleme yapılırsa. - 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'
içeriyorsaeval()
,setTimeout()
ve nadiren kullanılan diğer birkaç API'ye eklemeler.
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ı: Sağlamlaştırma ve Azaltma Arasındaki Başarılı Bir Kargaşa bölümünde 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