Bir web uygulamasına kötü amaçlı komut dosyaları ekleme özelliği olan siteler arası komut dosyası çalıştırma (XSS), on yılı aşkın bir süredir en büyük web güvenliği açıklarından biri olmuştur.
İçerik Güvenliği Politikası (CSP), 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 üst bilgisini 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 atlanabilirler. Bu nedenle sayfayı genellikle XSS'ye maruz bırakan, yaygın olarak kullanılan ana makine izin verilenler listesi tabanlı CSP'ler yerine, XSS'yi azaltmak için nonce'lara veya karmalara dayalı bir CSP'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.
Anahtar terim: Karma işlevi, bir giriş değerini karma adı verilen sıkıştırılmış sayısal bir 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.
Nonce veya hash değerine dayanan İçerik Güvenliği Politikası genellikle katı CSP olarak adlandırılır. Bir uygulamada katı bir CSP kullanıldığında, HTML yerleştirme hataları bulan saldırganlar genellikle bunları, tarayıcıyı güvenlik açığı içeren bir belgedeki kötü amaçlı komut dosyalarını yürütmeye zorlamak için kullanamazlar. Bunun nedeni, katı CSP'nin yalnızca sunucuda oluşturulan doğru tek seferlik değere sahip karma oluşturma işlemi uygulanmış komut dosyalarına veya komut dosyalarına izin vermesidir. Böylece 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 bir İGP varsa muhtemelen siteler arası üzerinde etkili olmayacaktır. Bu CSP türüne izin verilenler listesi CSP adı verilir. Çok fazla özelleştirme gerektirirler ve saldırganlar tarafından atlanabilirler.
Kriptografik tek seferlik rastgele sayılara veya karmalara dayanan katı CSP'ler bu tehlikelerden kaçınır.
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:
Temel olmayan katı CSP
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
![](https://web.dev/static/articles/strict-csp/image/er4BaGCJzBwDaESFKfZd.jpg?authuser=5&hl=tr)
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, bunun gibi bir CSP'yi "katı" ve dolayısıyla güvenli hale getirir:
- Site geliştiricisinin kullanıcının tarayıcısında yürütmeye güvendiği
<script>
etiketlerini belirtmek için nonces'nonce-{RANDOM}'
veya karma değerleri'sha256-{HASHED_INLINE_SCRIPT}'
kullanır. 'strict-dynamic'
'i, 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ı CSP dağıtma çabasını azaltır. Bu işlem aynı zamanda çoğu üçüncü taraf JavaScript kitaplığı ve widget'ının kullanımını da kaldırır.- URL izin verilenler listelerine dayanmadığı için yaygın CSP atlamalarından etkilenmez.
- Satır içi etkinlik işleyiciler veya
javascript:
URI'ler gibi güvenilmeyen satır içi komut dosyalarını engeller. - Flash gibi tehlikeli eklentileri devre dışı bırakmak için
object-src
politikasını kısıtlar. <base>
etiketlerinin yerleştirilmesini engellemek içinbase-uri
politikasını 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 CSP benimseyin
Katı bir CSP benimsemek için şunları yapmanız gerekir:
- Uygulamanızın nonce veya karma tabanlı bir CSP ayarlamasına karar verin.
- Katı CSP yapısı bölümünden CSP'yi kopyalayın ve uygulamanız genelinde bir yanıt başlığı olarak ayarlayın.
- CSP ile uyumlu olmayan kalıpları kaldırmak için HTML şablonlarını ve istemci tarafı kodu yeniden düzenleyin.
- CSP'nizi dağıtın.
Sitenizin bir İGP'ye sahip olup olmadığını ve XSS'e karşı etkili olacak kadar katı olup olmadığını kontrol etmek için bu süreç boyunca Lighthouse
(--preset=experimental
işaretli sürüm 7.3.0 ve üzeri) En İyi Uygulamalar denetimini kullanabilirsiniz.
![Yaptırım modunda hiç CSP bulunamadığına dair Lighthouse raporu uyarısı.](https://web.dev/static/articles/strict-csp/image/lighthouse-report-warning-74f66e2ef76dc.png?authuser=5&hl=tr)
1. Adım: Nonce veya karma tabanlı bir CSP'ye ihtiyacınız olup olmadığına karar verin
İki katı CSP türünün işleyiş şekli şöyledir:
Nonce tabanlı CSP
Nonce tabanlı bir CSP ile çalışma zamanında rastgele bir sayı oluşturur, bu sayıyı İGP'nize dahil eder ve sayfanızdaki her komut dosyası etiketiyle ilişkilendirirsiniz. Bir saldırgan, sayfanıza kötü amaçlı bir komut dosyası ekleyemez veya mevcut bir komut dosyasını çalıştıramaz, çünkü bu komut dosyası için doğru rastgele sayıyı tahmin etmesi gerekir. Bu yalnızca sayı tahmin edilemezse çalışır ve her yanıt için çalışma zamanında yeni oluşturulur.
Sunucuda oluşturulan HTML sayfaları için nonce tabanlı bir CSP kullanın. Bu sayfalarda, her yanıt için yeni bir rastgele sayı oluşturabilirsiniz.
Karma tabanlı CSP
Karma tabanlı CSP'de her satır içi komut dosyası etiketinin karması CSP'ye eklenir. Her komut dosyasının farklı bir karma değeri vardır. Bir saldırgan, sayfanıza kötü amaçlı bir komut dosyası ekleyemez veya mevcut bir komut dosyasını çalıştıramaz. Çünkü bir komut dosyasının çalışması için karmasının 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, sunucu tarafı oluşturma olmadan statik olarak sunulan ve Angular, React veya diğer çerçevelerle oluşturulan tek sayfalık web uygulamaları için karma tabanlı bir CSP kullanabilirsiniz.
2. Adım: Katı bir CSP belirleyin ve komut dosyalarınızı hazırlayın
CSP ayarlarken birkaç seçeneğiniz vardır:
- Yalnızca rapor modu (
Content-Security-Policy-Report-Only
) veya yaptırım modu (Content-Security-Policy
). Salt rapor modunda, CSP henüz kaynakları engellemediğinden sitenizdeki hiçbir öğe bozulmaz ancak hataları görebilir ve engellenmiş olabilecek her şey için rapor alabilirsiniz. Yerel olarak, CSP'nizi ayarlarken bu önemli değildir, çünkü her iki mod da tarayıcı konsolundaki hataları gösterir. Herhangi bir durumda yaptırım modu, taslak CSP bloklarınızın kaynaklarını bulmanıza yardımcı olabilir çünkü bir kaynağı engellemek sayfanızın bozuk görünmesine neden olabilir. Salt rapor modu, işlemin ilerleyen aşamalarında en çok işinize yarar (5. adıma bakın). - Başlık veya HTML
<meta>
etiketi. Yerel geliştirmede<meta>
etiketi, İGP'nizde ince ayar yapmak ve sitenizin nasıl etkilendiğini hızlıca görmek için daha kullanışlı olabilir. Ancak:- Daha sonra, CSP'nizi üretimde dağıtırken HTTP başlığı olarak ayarlamanızı öneririz.
- CSP meta etiketleri salt rapor modunu desteklemediğinden CSP'nizi yalnızca rapor modunda ayarlamak isterseniz 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 rastgele sayı oluşturma
Tek seferlik rastgele sayı, sayfa başına yalnızca bir kez kullanılan rastgele bir sayıdır. Nonce tabanlı bir CSP, yalnızca saldırganların tek seferlik değeri tahmin edememesi durumunda XSS'yi hafifletebilir. CSP tek seferlik rastgele sayısı:
- Kriptografik olarak güçlü rastgele değer (ideal olarak 128 bit uzunluğunda)
- Her yanıt için yeni oluşturulan
- Base64 kodlu
Sunucu tarafı çerçevelerde CSP tek seferlik rastgele sayısının nasıl ekleneceğiyle ilgili bazı örnekleri burada bulabilirsiniz:
- Django (piton)
- 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 ekleyin
nonce tabanlı bir CSP'de her <script>
öğesi, CSP başlığında belirtilen rastgele tek seferlik rastgele değerle eşleşen bir nonce
özelliğine sahip olmalıdır. Tüm komut dosyaları aynı tek seferlik rastgele sayıya sahip olabilir. İ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 şöyledir:
'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}'
.
Kaynaklı komut dosyalarını dinamik olarak yükleme
Tarayıcılarda CSP karmaları yalnızca satır içi komut dosyaları için desteklendiğinden tüm üçüncü taraf komut dosyalarını bir satır içi komut dosyası kullanarak dinamik olarak yüklemeniz gerekir. Kaynaklı komut dosyalarına ilişkin karmalar tarayıcılar arasında iyi desteklenmez.
![](https://web.dev/static/articles/strict-csp/image/B2YsfJDYw8PRI6kJI7Bs.jpg?authuser=5&hl=tr)
<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>Bu komut dosyasının çalışmasını sağlamak için satır içi komut dosyasının karmasını hesaplamanız ve
{HASHED_INLINE_SCRIPT}
yer tutucusunu değiştirerek bunu 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. Bunu uygulamalı olarak 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>Yalnızca satır içi komut dosyalarına karma oluşturma işlemi uygulanabildiğinden CSP bu komut dosyalarını engeller.
Komut dosyası yüklenirken 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
özelliğini 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 ş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üldüğünde dokümanın hazır olmasını istiyorsanız komut dosyalarını eklemeden önce
DOMContentLoaded
etkinliğinin gerçekleşmesini bekleyin. Bu durum, komut dosyalarının indirilmeye yeterince erken başlamaması nedeniyle bir performans sorununa neden oluyorsa, sayfada ön yükleme etiketlerini daha erken kullanın. -
defer = true
, hiçbir şey yapmaz. Böyle bir 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ı kodu yeniden düzenleyin
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. Yani, XSS hatası bulan bir saldırgan, bu tür HTML yerleştirebilir ve kötü amaçlı JavaScript yürütebilir. Nonce veya hash tabanlı CSP'ler bu tür işaretlemelerin kullanımını yasaklar.
Siteniz bu kalıplardan herhangi birini kullanıyorsa bunları daha güvenli alternatifler halinde yeniden düzenlemeniz gerekir.
Önceki adımda CSP'yi etkinleştirdiyseniz CSP uyumsuz bir kalıbı her engellediğinde konsolda CSP ihlallerini görebilirsiniz.
![Chrome geliştirici konsolundaki CSP ihlal raporları.](https://web.dev/static/articles/strict-csp/image/csp-violation-reports-th-ff599b24b17be.jpg?authuser=5&hl=tr)
Çoğu durumda çözüm oldukça basittir:
Satır içi etkinlik işleyicilerini yeniden düzenleme
<span id="things">A thing.</span> <script nonce="${nonce}"> document.getElementById('things').addEventListener('click', doThings); </script>CSP, JavaScript kullanılarak kaydedilen etkinlik işleyicilere izin verir.
<span onclick="doThings();">A thing.</span>CSP, satır içi etkinlik işleyicilerini engeller.
javascript:
URI'lerini yeniden düzenleyin
<a id="foo">foo</a> <script nonce="${nonce}"> document.getElementById('foo').addEventListener('click', linkClicked); </script>CSP, JavaScript kullanılarak kaydedilen etkinlik işleyicilere izin verir.
<a href="javascript:linkClicked()">foo</a>CSP, javascript'i engeller: URI'lar.
eval()
öğesini JavaScript'inizden kaldırın
Uygulamanız, JSON dizesi 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 da daha hızlı bir işlemdir.
Tüm eval()
kullanımlarını kaldıramıyorsanız yine de katı bir nonce tabanlı CSP ayarlayabilirsiniz ancak 'unsafe-eval'
CSP anahtar kelimesini kullanmanız gerekir. Bu da politikanızın biraz daha az güvenli olmasına neden olur.
Bu katı CSP codelab'inde bu tür yeniden düzenlemelerle ilgili bunları ve daha fazla örneği bulabilirsiniz:
4. Adım (İsteğe bağlı): Eski tarayıcı sürümlerini desteklemek için yedekler ekleyin
Daha eski tarayıcı sürümlerini desteklemeniz gerekiyorsa:
strict-dynamic
kullanımı, Safari'nin önceki sürümleri için yedek olarakhttps:
öğesinin eklenmesini gerektirir. Bunu yaptığınızda:strict-dynamic
özelliğini destekleyen tüm tarayıcılarhttps:
yedeğini yoksayar. Bu nedenle bu, politikanın gücünü azaltmaz.- Eski tarayıcılarda, harici kaynaklı komut dosyaları yalnızca HTTPS kaynağından gelirlerse yüklenebilir. Bu, katı bir CSP'den daha az güvenli olsa da
javascript:
URI'larının yerleştirilmesi gibi bazı yaygın XSS nedenlerini önler.
- Çok eski tarayıcı sürümleriyle (4 yıl ve üzeri) uyumluluğu sağlamak için
unsafe-inline
ürününü yedek olarak ekleyebilirsiniz. CSP tek seferlik rastgele sayısı veya karma değeri mevcutsa son tarayıcıların tümüunsafe-inline
yönergesini 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 doğruladıktan sonra, CSP'nizi hazırlık ortamına, ardından üretim ortamınıza dağıtabilirsiniz:
- (İsteğe bağlı)
Content-Security-Policy-Report-Only
başlığını kullanarak CSP'nizi yalnızca rapor modunda dağıtın. Yalnızca rapor modu, üretimde yeni bir CSP gibi zarar verebilecek değişiklikleri, İGP kısıtlamalarını uygulamaya başlamadan önce test etmek için kullanışlıdır. Salt rapor modunda, CSP'niz uygulamanızın davranışını etkilemez ancak tarayıcı, CSP'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 neyin bozulduğunu görebilirsiniz. Daha fazla bilgi için Reporting API sayfasına bakın. - İGP'nizin son kullanıcılarınız için sitenizi kesintiye uğratmayacağından emin olduğunuzda
Content-Security-Policy
yanıt başlığını kullanarak İGP'nizi dağıtın. CSP'nizi<meta>
etiketinden daha güvenli olduğu için sunucu tarafı 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. CSP çoğu durumda javascript:
URI'leri gibi tehlikeli kalıpları reddederek saldırı yüzeyini önemli ölçüde azaltır. Bununla birlikte, kullandığınız CSP türüne ('strict-dynamic'
olan veya olmayan tek seferlik rastgele sayılar, karmalar) göre CSP'nin uygulamanızı korumadığı durumlar da vardır:
- Bir komut dosyasını tek tek girerseniz ancak
<script>
öğesinin gövdesine veyasrc
parametresine doğrudan bir ekleme yapılır. - Bağımsız değişkenlerin değerlerine göre
script
DOM düğümü oluşturan tüm kitaplık işlevleri dahil olmak üzere, dinamik olarak oluşturulan komut dosyalarının (document.createElement('script')
) konumlarına yerleştirme yapılıyorsa. Buna jQuery'nin.html()
'sinin yanı sıra jQuery < 3.0'daki.get()
ve.post()
gibi bazı yaygın API'ler dahildir. - Eski AngularJS uygulamalarında şablon yerleştirme olup olmadığı. AngularJS şablonuna veri ekleyebilen saldırganlar, bu şablonu rastgele JavaScript çalıştırmak için kullanabilir.
- Politika
'unsafe-eval'
içeriyorsaeval()
,setTimeout()
ve nadiren kullanılan başka 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ı: Sağlamlaştırma ve Azaltma Arasında Başarılı Bir Sorun bölümünde bulabilirsiniz.
Daha fazla bilgi
- CSP Artık Kaldı, Yaşasın CSP! "Beyaz Listelerin Güvenliği ve İçeriğin Geleceği" Güvenlik Politikası Hakkında
- CSP Değerlendirici
- LocoMoco Konferansı: İçerik Güvenliği Politikası: Sağlamlaştırma ile risk azaltma arasındaki karmaşa
- Google I/O konuşması: Modern Platform Özellikleriyle Web Uygulamalarının Güvenliğini Sağlama