Yeni Sanitizer API'nin amacı, rastgele dizelerin sayfaya güvenli bir şekilde eklenmesi için sağlam bir işlemci geliştirmektir.
Uygulamalar her zaman güvenilmeyen dizelerle ilgilenir, ancak bu içeriği bir HTML dokümanının parçası olarak güvenli bir şekilde oluşturmak zor olabilir. Yeterli dikkat olmazsa yanlışlıkla siteler arası komut dosyası (XSS) için kötü amaçlı saldırganların yararlanabileceği fırsatlar oluşturabilirsiniz.
Yeni Sanitizer API teklifi, bu riski azaltmak amacıyla rastgele dizelerin sayfaya güvenli bir şekilde eklenmesi 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())
Çıkış karakterli kullanıcı girişi
Kullanıcı girişi, sorgu dizeleri, çerez içerikleri ve benzeri öğeler DOM'ye eklenirken dizelerden doğru şekilde çıkış yapılmalıdır. .innerHTML
aracılığıyla DOM işlemeye özellikle dikkat edilmelidir. Çıkış karaktersiz 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ış yapar veya dizeyi .textContent
kullanarak genişletirseniz alert(0)
yürütülmez. Ancak, kullanıcı tarafından eklenen <em>
aynı zamanda dize olarak da genişletildiğinden, bu yöntem HTML'deki metin süslemesini tutmak için kullanılamaz.
Burada yapılacak en iyi şey çıkış yapmak değil, tizasyon işlemini yapmaktır.
Kullanıcı girişini temizleme
Çıkış yapma ile temizleme arasındaki fark
Çıkış karakteri ekleme, özel HTML karakterlerinin HTML Varlıkları ile değiştirilmesini ifade eder.
Temizleme işlemi, HTML dizelerinden anlamsal olarak zararlı parçaların (komut dosyası yürütme gibi) kaldırılmasını ifade eder.
Örnek
Önceki örnekte <img onerror>
, hata işleyicinin yürütülmesine neden olur, ancak onerror
işleyicisi kaldırıldıysa, <em>
değişmeden bırakılarak 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="">`
Temizleme işlemini doğru bir şekilde yapmak için giriş dizesini HTML olarak ayrıştırmak, zararlı olarak kabul edilen etiket ve özellikleri atlamak ve zararsız olanları tutmak gerekir.
Önerilen Sanitizer API spesifikasyonu, bu tür işlemleri tarayıcılara standart bir API olarak sunmayı amaçlamaktadır.
Dezenfektan API'sı
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. Aşağıdaki gibi olabilir.
$div.setHTML(user_input) // <div><em>hello world</em><img src=""></div>
setHTML()
ifadesinin Element
üzerinde tanımlandığını unutmayın. Bir Element
yöntemi olduğundan, ayrıştırma işlemi kendinden açıklamalı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.
Temizleme işleminin sonucunu dize olarak almak için setHTML()
sonuçlarından .innerHTML
öğesini 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ştirin
Sanitizer API, varsayılan olarak komut dosyasının 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 alması gerektiğini belirtir.
allowElements
: Dezenfektanın saklaması gereken öğelerin adları.
blockElements
: Temizleyicinin, alt öğelerini tutarken kaldırması gereken öğelerin adları.
dropElements
: Dezenfektanın kaldırması gereken öğelerin, alt öğeleriyle birlikte adları.
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>
Ayrıca aşağıdaki seçenekleri kullanarak temizleyicinin belirtilen özelliklere izin verip vermeyeceğini de belirleyebilirsiniz:
allowAttributes
dropAttributes
allowAttributes
ve dropAttributes
özellikleri için özellik eşleşme listeleri beklenir. Bu listelerde anahtarları özellik adı, değerleri, hedef öğe listeleri veya *
joker karakteri olan nesneler bulunur.
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 veriliyorsa öğe ve özellikler için 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 bilinen bir kitaplıktır. Sanitizer API ile DOMPurify arasındaki temel fark, DOMPurify'ın temizleme işleminin sonucunu bir dize olarak döndürmesidir. Bunu .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, Sanitizer API'nin tarayıcıda uygulanmadığı durumlarda 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 çift ayrıştırma işlemi, işleme süresini boşa harcar, ancak ikinci ayrıştırma işleminin sonucunun ilkinden farklı olduğu durumlardan kaynaklanan ilginç güvenlik açıklarına da yol açabilir.
HTML'nin ayrıştırılması bağlam da gerektirir. Örneğin <td>
, <table>
dilinde anlamlı olurken <div>
için geçerli değildir. DOMPurify.sanitize()
, dizeleri bağımsız değişken olarak kabul ettiğinden, ayrıştırma bağlamının tahmin edilmesi gerekiyordu.
DOMPurify yaklaşımını geliştiren Sanitizer API, ç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
Standartlaştırma sürecindeki Sanitizer API ile ilgili tartışmalar devam ederken Chrome da bu API'yi uygulama sürecindedir.
Adım | Durum |
---|---|
1. Açıklayıcı oluşturun | Tamamlandı |
2. Spesifikasyon taslağı oluştur | Tamamlandı |
3. Geri bildirim alma ve tasarımı yineleme | Tamamlandı |
4. Chrome kaynak denemesi | Tamamlandı |
5. Lansman | M105'te Gönderme Hazırlığı |
Mozilla: Bu teklifi prototip oluşturmaya değer ve aktif bir şekilde uyguluyor.
WebKit: Yanıtı WebKit posta listesinde görebilirsiniz.
Sanitizer API'yi etkinleştirme
about://flags
veya KSA seçeneği ile etkinleştirme
Chrome
Chrome, Sanitizer API'yi uygulama sürecindedir. 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 kanallarının önceki sürümlerinde --enable-blink-features=SanitizerAPI
adresinden bu özelliği etkinleştirip hemen deneyebilirsiniz. Chrome'un flag'lerle çalıştırılmasıyla ilgili talimatlara göz atın.
Firefox
Firefox, Sanitizer API'sını deneysel bir özellik olarak da uygulamaktadır. Bu özelliği etkinleştirmek için about:config
alanında 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 bildiriminiz varsa öğrenmeyi çok isteriz. Sanitizer API GitHub sorunları hakkındaki düşüncelerinizi paylaşıp bu API ile ilgilenen spesifikasyon yazarları ve kullanıcılarla tartışın.
Chrome uygulamasında hata veya beklenmeyen bir davranış bulursanız 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.
Demografi
Sanitizer API'yi çalışırken 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 Hakkında SSS
- MDN hakkında HTML Sanitizer API referans belgeleri
Fotoğraf, Towfiqu barbhuiya tarafından Unsplash'ta yayınlandı.