CSP (Content Security Policy) Nedir?
Web sayfası ve uygulamalarında, farklı bir kaynakta barındırılan dosyaları (css, js, font, vb.) çağırdığımızda istemesek de bazı olası güvenlik problemleri için de bir kapı aralamış olabiliriz.
Sürece bir de kullanıcıların sayfaya veri ekleyebilecekleri (formlar, vb.) senaryoları dahil ettiğimizde risk düzeyi daha da artmakta. Bu gibi durumları önlemek adına girdi ve çıktıların denetimi neredeyse bir zorunluluk haline gelmekte. Peki, bu süreci nasıl yönetmeli ve web sayfasını veya uygulamanın hangi kaynakları güvenli kabul edeceğine nasıl karar vereceğiz?
Content Security Policy (CSP)
Adından da anlaşılacağı üzere, Content Security Policy (CSP) bir içerik güvenliği ile ilgili kurallar barındıran bir güvenlik politikası. Bu kurallar bütünü sayesinde Cross-Site Scripting (XSS) saldırıları gibi yaygın güvenlik problemlerine karşı bir koruma oluşturmak amacıyla geliştirilmekte1 2 3. Elbette CSP'i bütüncül bir çözüm olarak ele almak doğru olmayacaktır. Çünkü, bu politika için tarayıcı uyumluluğu bir ön koşuldur4. CSP sürekli geliştirilen bir kurallar bütününe sahip olduğu için farklı sürümlere sahip. Dolayısıyla, tarayıcı desteği ile ilgili kontrolleri CSP25 ve CSP36 üzerinden yapmakta fayda var. Yine bu uyum çerçevesinde bazı tarayıcı uzantılar (extension) ve yer imleri (bookmarklets) ile ilgili sorunlarla karşılaşılması muhtemel7.
CSP'de istisna tanımında whitelist (beyaz liste) olarak ifade edebileceğimiz yönergeler kullanılmaktadır. Bu sayede, yalnızca izin verilen kaynaklar belirtilen direktifler doğrultusunda işlemler gerçekleştirebilirler, kurallar dışında kalan kaynaklar ve kaynak tipleri ise çalışırılmazlar. İstisna tanımlarını ve direktirleri HTTP response'ları ile beraber dönen CSP talimatları ile belirtmek yeterli olacaktır2 3.
Direktif (Directive) | Direktif Türü (Directives Types) | Kontrollü kaynak türü (Controlled resource type) |
---|---|---|
child-src |
fetch | Dokümanlar (frames), [Shared]Workers |
connect-src |
fetch | Script URL işlemleri |
default-src |
fetch | Tüm kaynaklar (fallback) |
font-src |
fetch | Fontlar |
frame-src |
fetch | deprecated. frames |
img-src |
fetch | Görseller |
manifest-src |
fetch | Manifest dosyaları |
media-src |
fetch | Medya dosyaları (audio, video) |
prefetch-src |
fetch | prefetched ya da prerendered URL işlemleri |
object-src |
fetch | Plug-in formatları (object, embed) |
script-src |
fetch | Script dosyaları |
script-src-elem |
fetch | Script istekleri ve blokları |
script-src-attr |
fetch | Satır içi olay işleyiciler (Inline event handlers) |
style-src |
fetch | Stylesheet dosyaları |
style-src-elem |
fetch | Satır içi style tanımları |
style-src-attr |
fetch | Satır içi style özellik tanımları |
worker-src |
fetch | [Shared]Workers |
base-uri |
document | Base element |
plugin-types |
document | Plug-in türleri (application/x-shockwave-flash, pplication/pdf, vb.) |
sandbox |
document | Response kontolü. Diğer direktiflerden farklı olarak sayfanın kendisini ele alır. Popupları engellemek, formları durdurmak, javascript işlemlerini engellemek gibi amaçlarla kullanılabilir8 |
form-action |
navigation | Form action'ları |
frame-ancestors |
navigation | frame, object, embed |
navigate-to |
navigation | URL tanımları (a, form, window.location, window.open, vb.) |
report-uri |
reporting | deprecated |
report-to |
reporting | direktif raporlama9. Belirtilen direktiflerin ihlali durumunda bir JSON nesnesi gösterilen URL'e POST edilir |
Tabloda görüldüğü üzere CSP direktirleri ile script ve style dosyalarının yanı sıra, embed, iframe işlemleri de kontrol edilebilmekte6. İlgili tablodaki sütun tanımlarını içeren kullanımı şu şekilde özetleyebiliriz: Content-Security-Policy: <Direktif> <Kontrollü Kaynaklar>
. Elbette bu tanımlama sunucu konfigürasyon tanımlarının yanı sıra back-end ve/veya meta etiketleri ile de yapılabilir10 11.
Yukarıdaki açıklama ve tablo temel alınarak şu şekilde basit bir örnek tanımlama yapabiliriz; Content-Security-Policy: script-src 'self' https://apis.google.com
.
// PHP
header("Content-Security-Policy: script-src 'self' https://apis.google.com");
// JavaScript
app.use(function (req, res, next) {
res.setHeader(
'Content-Security-Policy',
"script-src 'self' https://apis.google.com"
);
next();
});
<!-- HTML //-->
<meta http-equiv="Content-Security-Policy" content="script-src 'self' https://apis.google.com" />
Meta kullanımında frame-ancestors
, sandbox
, report-uri
direktifleri tanımlanamaz.
Bu CSP tanımı içerisinde yer alan self
web sayfamızda yalnızca kendi kaynağımızdan (origin) ve https://apis.google.com
kaynağıdan çağırılan script'lerin çalıştırılmasına izin vermektedir. Tanım içerisinde inline-script
yer almadığı için olay tetikleyiciler aracılığı ile script çalıştırmak mümkün olmayacaktır.
Elbette self
dışında kullanılabilecek farklı kaynak tanımları da mevcut. Bu tanımlar '...'
(tek tırnak) içerisinde tanımlanırlar. Aksi durumda, değer bir alan adı tanımı gibi değerlendirilir.
self
- Alt alan adları (subdomain) da dahil, mevcut origin ile eşleşir.
none
- Hiçbir kaynağa izin verilmeyeceğini belirtir. Örneğin,
font-src: 'none'
değeri hiçbir kaynaktan font eklenemeyeceğini ifade eder. unsafe-inline
- Inline JavaScript ve CSS kullanımlarına izin verir.
unsafe-eval
eval()
gibi string-to-JavaScript metotlarının kullanımına izin verir.
Direktif kullanımında eğer bir değer belirtilmezse, ilgili tanım *
kullanımında olduğu gibi tüm kaynaklara izin verecektir. Örneğin, style-src
için bir değer belirtilmemişse style-src:
ön tanımlı olarak *
değerini almış kabul edilir ve tüm kaynaklardan style dosyalarının yüklenmesine izin verir.
default-src
tabloda da belirtildiği üzere, tüm kaynakları kapsayan bir direktif. Bu sayede, -src
ile biten pek çok direktifi tek satırda kontrol etmek mümkün. Örneğin, default-src
için kaynak olarak 'self'
tanım yaparsak aşağıdaki gibi bir sonuç elde etmiş oluruz.
// Content-Security-Policy: default-src 'self'
Content-Security-Policy: connect-src 'self';
font-src 'self';
frame-src 'self';
img-src 'self';
manifest-src 'self';
media-src 'self';
prefetch-src 'self';
object-src 'self';
script-src-elem 'self';
script-src-attr 'self';
style-src-elem 'self';
style-src-attr 'self';
worker-src 'self'
Elbette bu tanımlama değişkenlik gösterebilir. Örneğin, default-src
için kaynak olarak 'self'
ve sonrasında sonrasında farklı bir direktif(ler) için farklı bir kaynağı değer olarak verebiliriz12.
// Content-Security-Policy: default-src 'self'; script-src-elem https://example.com
Content-Security-Policy: connect-src 'self';
font-src 'self';
frame-src 'self';
img-src 'self';
manifest-src 'self';
media-src 'self';
prefetch-src 'self';
object-src 'self';
script-src-elem https://example.com;
script-src-attr 'self';
style-src-elem 'self';
style-src-attr 'self';
worker-src 'self'
Aynı direktif tanımları arasından, verilen son değer uygulanır.
Elbette yapılabilecek işlemler sadece bunlar değil. Örneğin, CSP2 ile birlikte nonce ve hash gibi dinamik tanımlamalar yapamk5 ve CSP3 ile birlikte strict-dynamic özelliklerinden de faydalanmak mümkün6 13.
Direktif Tanımlama
Birden fazla direktifi ;
(noktalı virgül) ile, bir direktifin alacağı çoklu değerler ise tek boşluk (space) ile birbirinden ayrılır; script-src 'self' https://example.com; style-src https://www.example.com
.
Ek olarak, direktif değerlerini gruplandırarak da tanımlamak mümkün. Elbette gruplandırmaların kendi içerisinde istisnalar da oluşturabileceğini ve bu nedenle güvenlik sorunlarının oluşabileceğini unutmamak gerekir7 14.
// Sadece hostname tanımı
script-src example.com
// Sadece scheme tanımı
script-src https:
script-src data:
// Wilcard tanımı
script-src www.example.com:*
script-src *.example.com:*
Örnek Kullanım
Google Tag Manager Live ve Preview mode için farklı kaynak kullanımlarına ihtiyaç duymakta. Genel olarak, stabil bir şekilde çalışabilmesi için inline script, inline eval() ve inline styles kullanımlarına ihtiyaç duymaktadır. Aşağıdaki örnekler çeşitli kaynaklardan derlenmiştir15 16 17 18 19. İlgili kodları zaman içerisinde geribildirimlerle genişletmeye ve güncellemeye devam edeceğim.
// Google Analytics
script-src 'self' https://www.google-analytics.com;
img-src https://www.google-analytics.com www.google-analytics.com https://stats.g.doubleclick.net;
connect-src https://www.google-analytics.com www.google-analytics.com https://stats.g.doubleclick.net;
// Google Tag Manager
script-src 'unsafe-eval' 'unsafe-inline' https://tagmanager.google.com https://www.googletagmanager.com;
style-src 'unsafe-inline' https://tagmanager.google.com https://fonts.googleapis.com;
img-src 'unsafe-inline' https://ssl.gstatic.com;
// Google Ads & Remarketing
script-src: https://www.googleadservices.com https://googleads.g.doubleclick.net https://www.google.com;
img-src: https://googleads.g.doubleclick.net https://www.google.com;
frame-src: https://bid.g.doubleclick.net;
// Facebook Pixel
script-src: https://connect.facebook.net;
img-src: https://www.facebook.com;
child-src: https://www.facebook.com https://staticxx.facebook.com;
connect-src: https://www.facebook.com/tr;
form-action: https://connect.facebook.net;
// Google Fonts
style-src: https://fonts.googleapis.com;
font-src: https://fonts.gstatic.com;
// Hotjar
img-src: http://*.hotjar.com https://*.hotjar.com http://*.hotjar.io https://*.hotjar.io;
script-src: http://*.hotjar.com https://*.hotjar.com http://*.hotjar.io https://*.hotjar.io 'unsafe-eval' 'unsafe-inline';
connect-src: http://*.hotjar.com:* https://*.hotjar.com:* http://*.hotjar.io https://*.hotjar.io wss://*.hotjar.com;
frame-src: https://*.hotjar.com http://*.hotjar.io https://*.hotjar.io;
font-src: http://*.hotjar.com https://*.hotjar.com http://*.hotjar.io https://*.hotjar.io;
// Disqus
default-src 'none' ;
script-src 'self' disqus.com disqus-csp.disqus.com c.disquscdn.com;
style-src 'self' c.disquscdn.com;
img-src 'self' referrer.disqus.com c.disquscdn.com;
connect-src links.services.disqus.com;
child-src disqus.com;
frame-src disqus.com;
upgrade-insecure-requests;
block-all-mixed-content;
Eğer report-uri
kullanacaksanız JSON çıktısı için Hookbin20 veya Pipedream21 servislerinden faydalanabilirsiniz.
Son olarak
W3C tarafından paylaşılan kaynaklarda pek çok örnek yer almakta. Ancak, elbette özel çözümlere ihtiyaç duyacağınız çok fazla senaryo ile karşılaşacağınızdan eminim. Bu gibi durumlarda kullanılmak üzere Google tarafından paylaşılan csp-evaluator ile doğrulama işlemlerini gerçekleştirebilirsiniz.
- Content Security Policy (CSP). MDN web docs ↩
- Content Security Policy. Wikipedia ↩ ↩
- Mike West, Joseph Medley. (2020). Content Security Policy ↩ ↩
- Can I use 'Content Security Policy'? ↩
- W3C Recommendation. Mike West, Adam Barth, Dan Veditz. (2016). Content Security Policy Level 2 ↩ ↩
- Draft. Mike West. (2018). Content Security Policy Level 3 ↩ ↩ ↩
- Ziyahan Albeniz. (2016). CSP - Content Security Policy ↩ ↩
- CSP: sandbox. MDN web docs ↩
- Douglas Creager, Ian Clelland, Mike West, Ilya Grigorik, Paul Meyer. (2020). Reporting API ↩
- Adding a CSP header with htaccess ↩
- Haydar Can. (2018). CSP – Content Security Policy ↩
- CSP: default-src. MDN web docs ↩
- Troy Hunt. (2017). Locking Down Your Website Scripts with CSP, Hashes, Nonces and Report URI ↩
- Ziyahan Albeniz. (2018). CSP Şakaya Gelmez, Büyük Bir Ciddiyetle Set Edeceksin Şöyle Değil Mesela… ↩
- Simo Ahava. (2018). GTMTips: Google Tag Manager Content Security Policy ↩
- Logan Gordon. (2017). Using Google Analytics And Google Tag Manager With Content Security Policy ↩
- Using Google Tag Manager with a Content Security Policy. Google Tag Manager ↩
- Joseph Pinder. (2020). Facebook Pixel Is Slowing Down Your Website (And How To Fix It, Securely) ↩
- Testing Disqus CSP ↩
- Hookbin. Capture and Inspect HTTP Request ↩
- Pipedream Nedir? Nasıl Kullanılır? ve Pipedream. The fastest way to integrate APIs and run code ↩