Yeni Sanitizer API, sayfaya güvenli bir şekilde eklenecek rastgele dizeler için sağlam bir işlemci geliştirmeyi amaçlar.
Uygulamalar her zaman güvenilmeyen dizelerle çalışır ancak bu içeriği bir HTML belgesinin parçası olarak güvenli bir şekilde oluşturmak zor olabilir. Yeterli özen olmadan, yanlışlıkla kötü amaçlı saldırganların istismar edebileceği siteler arası komut dosyası (XSS) fırsatları oluşturabilirsiniz.
Yeni Sanitizer API teklifi, bu riski azaltmak amacıyla sayfaya güvenli bir şekilde eklenecek rastgele dizeler için sağlam bir işlemci oluşturmayı amaçlar. Bu makalede API tanıtılmakta ve kullanımı açıklanmaktadır.
// Expanded Safely !!
$div.setHTML(`<em>hello world</em><img src="" onerror=alert(0)>`, new Sanitizer())
Kullanıcı girişine çıkış karakteri ekleme
Kullanıcı girişi, sorgu dizeleri, çerez içerikleri vb. DOM'ye eklenirken dizeler doğru şekilde kod dışına alınmalıdır. .innerHTML
aracılığıyla DOM manipülasyonuna özel olarak dikkat edilmelidir. Burada, kod dışına alınmamış dizeler tipik bir XSS kaynağıdır.
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
$div.innerHTML = user_input
Yukarıdaki giriş dizesinde HTML özel karakterlerinden çıkış yaparsanız veya bunu .textContent
kullanarak genişletirseniz alert(0)
yürütülmez. Ancak, kullanıcı tarafından eklenen <em>
aynı zamanda olduğu gibi dize olarak da genişletildiğinden, HTML'deki metin süslemesini korumak için bu yöntem kullanılamaz.
Burada yapılacak en iyi şey çıkış yapmak değil, temizleştirmek.
Kullanıcı girişi temizleniyor
Çıkış yapma ve temizleme arasındaki fark
Çıkış yöntemi, özel HTML karakterlerinin HTML Öğeleri ile değiştirilmesini ifade eder.
Arındırma, anlam açısından zararlı olan kısımların (komut dosyası yürütme gibi) HTML dizelerinden kaldırılmasını ifade eder.
Örnek
Yukarıdaki örnekte <img onerror>
, hata işleyicinin yürütülmesine neden olur, ancak onerror
işleyici kaldırılırsa <em>
sağlam şekilde kalarak DOM'de güvenli bir şekilde genişletilebilir.
// XSS 🧨
$div.innerHTML = `<em>hello world</em><img src="" onerror=alert(0)>`
// Sanitized ⛑
$div.innerHTML = `<em>hello world</em><img src="">`
Doğru şekilde temizleme işlemi için giriş dizesini HTML olarak ayrıştırmak, zararlı kabul edilen etiket ve özellikleri çıkarmak ve zararsız olanları tutmak gerekir.
Önerilen Sanitizer API spesifikasyonu, tarayıcılar için standart bir API gibi işleme imkanı sunmayı amaçlar.
Sanitizer API'si
Sanitizer API aşağıdaki şekilde kullanılır:
const $div = document.querySelector('div')
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
$div.setHTML(user_input, { sanitizer: new Sanitizer() }) // <div><em>hello world</em><img src=""></div>
Ancak { sanitizer: new Sanitizer() }
varsayılan bağımsız değişkendir. Bu, aşağıdaki gibi olabilir.
$div.setHTML(user_input) // <div><em>hello world</em><img src=""></div>
setHTML()
öğesinin Element
üzerinde tanımlandığını belirtmek isteriz. Element
yöntemi olduğu için ayrıştırılacak bağlam yeterince açıklayıcıdır (bu durumda <div>
), ayrıştırma işlemi dahili olarak bir kez yapılır ve sonuç doğrudan DOM'ye genişletilir.
Temizlik işleminin sonucunu dize olarak almak için setHTML()
sonuçlarında .innerHTML
kullanabilirsiniz.
const $div = document.createElement('div')
$div.setHTML(user_input)
$div.innerHTML // <em>hello world</em><img src="">
Yapılandırma aracılığıyla özelleştir
Sanitizer API, varsayılan olarak komut dosyası yürütülmesini tetikleyecek dizeleri kaldıracak şekilde yapılandırılmıştır. Bununla birlikte, bir yapılandırma nesnesi aracılığıyla temizleme işlemine kendi özelleştirmelerinizi de ekleyebilirsiniz.
const config = {
allowElements: [],
blockElements: [],
dropElements: [],
allowAttributes: {},
dropAttributes: {},
allowCustomElements: true,
allowComments: true
};
// sanitized result is customized by configuration
new Sanitizer(config)
Aşağıdaki seçenekler, temizleme sonucunun belirtilen öğeyi nasıl ele alacağını belirtir.
allowElements
: Dezenfektanın tutması gereken öğelerin adları.
blockElements
: Dezenfektanın kaldırması ve alt öğelerini tutması gereken öğelerin adları.
dropElements
: Dezenfektanın kaldırılması gereken öğelerin adları ve alt öğeleri.
const str = `hello <b><i>world</i></b>`
$div.setHTML(str)
// <div>hello <b><i>world</i></b></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowElements: [ "b" ]}) })
// <div>hello <b>world</b></div>
$div.setHTML(str, { sanitizer: new Sanitizer({blockElements: [ "b" ]}) })
// <div>hello <i>world</i></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowElements: []}) })
// <div>hello world</div>
Dezenfektanın, belirtilen özelliklere izin verip vermeyeceğini aşağıdaki seçeneklerle de kontrol edebilirsiniz:
allowAttributes
dropAttributes
allowAttributes
ve dropAttributes
mülklerinde özellik eşleşme listeleri beklenir. Bu listeler, anahtarları özellik adı, değerler ise hedef öğelerin listeleri veya *
joker karakteridir.
const str = `<span id=foo class=bar style="color: red">hello</span>`
$div.setHTML(str)
// <div><span id="foo" class="bar" style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["span"]}}) })
// <div><span style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["p"]}}) })
// <div><span>hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {"style": ["*"]}}) })
// <div><span style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({dropAttributes: {"id": ["span"]}}) })
// <div><span class="bar" style="color: red">hello</span></div>
$div.setHTML(str, { sanitizer: new Sanitizer({allowAttributes: {}}) })
// <div>hello</div>
allowCustomElements
, özel öğelere izin verme veya reddetme seçeneğidir. İzin verildiği takdirde, öğeler ve özelliklerle ilgili diğer yapılandırmalar geçerli olmaya devam eder.
const str = `<custom-elem>hello</custom-elem>`
$div.setHTML(str)
// <div></div>
const sanitizer = new Sanitizer({
allowCustomElements: true,
allowElements: ["div", "custom-elem"]
})
$div.setHTML(str, { sanitizer })
// <div><custom-elem>hello</custom-elem></div>
API yüzeyi
DomPurify ile karşılaştırma
DOMPurify, temizlik işlevi sunan meşhur bir kitaplıktır. Sanitizer API ile DOMPurify arasındaki temel fark, DOMPurify'ın temizleme işleminin sonucunu bir dize olarak döndürmesidir. Bu sonucu, .innerHTML
aracılığıyla bir DOM öğesine yazmanız gerekir.
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
const sanitized = DOMPurify.sanitize(user_input)
$div.innerHTML = sanitized
// `<em>hello world</em><img src="">`
DOMPurify, tarayıcıda Sanitizer API kullanılmadığında yedek olarak kullanılabilir.
DOMPurify uygulamasının bazı dezavantajları vardır. Bir dize döndürülürse giriş dizesi DOMPurify ve .innerHTML
tarafından iki kez ayrıştırılır. Bu iki kez ayrıştırma işlemi, işleme süresini kaybetse de ikinci ayrıştırmanın sonucunun ilkinden farklı olduğu durumlarda ortaya çıkan ilginç güvenlik açıklarına da yol açabilir.
HTML'nin bağlamın da ayrıştırılması gerekir. Örneğin, <td>
, <table>
dilinde anlam ifade ederken <div>
dilinde anlamlı değildir. DOMPurify.sanitize()
, dizeleri yalnızca bağımsız değişken olarak aldığı için ayrıştırma bağlamının tahmin edilmesi gerekiyordu.
Sanitizer API, DOMPurify yaklaşımından daha iyi bir şekilde gelişmiş hâle gelmiştir. Aynı zamanda, çift ayrıştırma ihtiyacını ortadan kaldırmak ve ayrıştırma bağlamını netleştirmek için tasarlanmıştır.
API durumu ve tarayıcı desteği
Sanitizer API, şu anda standart hale getiriliyor ve Chrome da bu API'yi uygulama aşamasında.
Step | Durum |
---|---|
1. Açıklayıcı oluşturun | Tamamlandı |
2. Spesifikasyon taslağı oluştur | Tamamlandı |
3. Geri bildirim alma ve tasarımda yineleme yapma | Tamamlandı |
4. Chrome kaynak denemesi | Tamamlandı |
5. Başlat | M105 sürümünde gönderim yapma amacı |
Mozilla: Bu teklifi prototip oluşturmaya değer ve etkin bir şekilde uygulamaktadır.
WebKit: Yanıtı WebKit posta listesinde görebilirsiniz.
Sanitizer API'yi etkinleştirme
about://flags
veya KSA seçeneği aracılığıyla etkinleştirme
Chrome
Chrome, Sanitizer API'yi uygulama sürecinde. Chrome 93 veya sonraki sürümlerde about://flags/#enable-experimental-web-platform-features
işaretini etkinleştirerek bu davranışı deneyebilirsiniz. Chrome Canary ve Yeni geliştirilenler kanalının önceki sürümlerinde bu özelliği --enable-blink-features=SanitizerAPI
aracılığıyla etkinleştirebilir ve hemen deneyebilirsiniz. Chrome'u işaretlerle çalıştırma talimatlarına göz atın.
Firefox
Firefox da deneysel bir özellik olarak Sanitizer API'yi uyguluyor. Etkinleştirmek için about:config
içinde dom.security.sanitizer.enabled
işaretini true
olarak ayarlayın.
Özellik algılama
if (window.Sanitizer) {
// Sanitizer API is enabled
}
Geri bildirim
Bu API'yi deneyip geri bildirimlerinizi bizimle paylaşırsanız seviniriz. Sanitizer API GitHub sorunları hakkındaki düşüncelerinizi paylaşın ve bu API'yle ilgilenen spesifikasyon yazarları ve diğer kişilerle konuşun.
Chrome'un uygulamasında hatalar veya beklenmedik davranışlar bulursanız bunu bildirmek için hata bildiriminde bulunun. Blink>SecurityFeature>SanitizerAPI
bileşenlerini seçin ve uygulayıcıların sorunu izlemesine yardımcı olmak için ayrıntıları paylaşın.
Demo
Sanitizer API'nin nasıl çalıştığını görmek için Mike West'in Sanitizer API Playground'a göz atın:
Referanslar
- HTML Sanitizer API spesifikasyonu
- WICG/sanitizer-api deposu
- Sanitizer API ile ilgili SSS
- MDN ile ilgili HTML Sanitizer API referans dokümanları
Fotoğraf: Towfiqu barbhuiya tarafından Unsplash'te yapıldı.