Yeni Sanitizer API, rastgele dizelerin bir sayfaya güvenli bir şekilde eklenmesi için güçlü bir işlemci oluşturmayı amaçlamaktadır.
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 kötü amaçla kullanabileceğ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şinden kaçınma
Kullanıcı girişi, sorgu dizeleri, çerez içerikleri vb. DOM'a eklenirken dizeler düzgün bir şekilde kod dışına alınmalıdır. Kaçış karakteri eklenmemiş dizelerin tipik bir XSS kaynağı olduğu .innerHTML
aracılığıyla DOM'de değişiklik yapılmasına özellikle dikkat edilmelidir.
const user_input = `<em>hello world</em><img src="" onerror=alert(0)>`
$div.innerHTML = user_input
Yukarıdaki giriş dizesindeki HTML özel karakterlerini kod dışında bırakırsanız veya .textContent
kullanarak genişletirseniz alert(0)
yürütülmez. Ancak kullanıcı tarafından eklenen <em>
de olduğu gibi bir dize olarak genişletildiğinden, metin süslemesini HTML'de tutmak için bu yöntem kullanılamaz.
Burada yapılacak en iyi şey kaçış karakteri eklemek değil, temizlik yapmaktır.
Kullanıcı girişini temizleme
Kaçış ve temizleme arasındaki fark
Koddan kaçınma, özel HTML karakterlerinin HTML varlıkları ile değiştirilmesini ifade eder.
Sanitasyon, semantik olarak zararlı bölümlerin (ör. komut dosyası yürütme) HTML dizelerinden kaldırılmasını ifade eder.
Örnek
Önceki örnekte <img onerror>
, hata işleyicinin yürütülmesine neden olur ancak onerror
işleyici kaldırılırsa <em>
'yi olduğu gibi bırakarak 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 temizlemek için giriş dizesini HTML olarak ayrıştırmak, zararlı olarak kabul edilen etiketleri ve özellikleri çıkarmak ve zararsız olanları korumak gerekir.
Önerilen Sanitizer API spesifikasyonu, bu tür işlemleri tarayıcılar için standart bir API olarak sağlamayı amaçlamaktadır.
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 varsayılan bağımsız değişken { sanitizer: new Sanitizer() }
'tür. Bu, aşağıdaki gibi olabilir.
$div.setHTML(user_input) // <div><em>hello world</em><img src=""></div>
setHTML()
'nin Element
üzerinde tanımlandığını belirtmek isteriz. Element
yöntemi olduğundan, ayrıştırılacak bağlam açıktır (bu durumda <div>
). Ayrıştırma işlemi dahili olarak bir kez yapılır ve sonuç doğrudan DOM'da 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ırmayı kullanarak özelleştirme
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 işlemesi gerektiğini belirtir.
allowElements
: Sanitatör'ün koruması gereken öğe 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 öğeleri reddetme seçeneğidir. İzin verilirse öğeler 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 meşhur bir kitaplıktır. Sanitizer API ile DOMPurify arasındaki temel fark, DOMPurify'in temizleme sonucunu bir dize olarak döndürmesidir. Bu dizeyi .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="">`
Sanitizer API tarayıcıda uygulanmadığında DOMPurify yedek olarak kullanılabilir.
DOMPurify uygulamasının birkaç dezavantajı 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şleme süresini boşa harcar, ancak 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 ayrıştırılması için bağlam da gerekir. Örneğin, <td>
, <table>
dilinde anlam ifade ederken <div>
dilinde anlamlı değildir. DOMPurify.sanitize()
bağımsız değişkeni yalnızca bir dize aldığından, ayrıştırma bağlamının tahmin edilmesi gerekiyordu.
Sanitizer API, DOMPurify yaklaşımından daha iyi bir süreçti. Aynı zamanda iki kez 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, standartlaştırma sürecinde tartışılmakta ve Chrome'da kullanıma sunulma sürecindedir.
Step | Durum |
---|---|
1. Açıklayıcı oluşturma | Tamamlandı |
2. Spesifikasyon taslağı oluştur | Tamamlandı |
3. Geri bildirim toplayın ve tasarımda iterasyon yapın | Tamamlandı |
4. Chrome kaynak denemesi | Tamamlandı |
5. Başlat | M105'te Gönderim Niyeti |
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 CLI seçeneğiyle 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 Dev kanalının önceki sürümlerinde --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, Sanitizer API'yi deneysel bir özellik olarak da uygular. 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 denedikten sonra geri bildiriminiz olursa bizimle paylaşın. Sanitizer API GitHub sorunları hakkındaki düşüncelerinizi paylaşın ve spesifikasyon yazarlarıyla ve bu API ile ilgilenen kişilerle görüşün.
Chrome'un uygulamasında hatalar veya beklenmedik davranışlar bulursanız bunu bildirmek için hata bildiriminde bulunun. Uygulamacıların sorunu izlemesine yardımcı olmak için Blink>SecurityFeature>SanitizerAPI
bileşenlerini seçin ve ayrıntıları paylaşın.
Demo
Sanitizer API'nin kullanımını görmek için Mike West tarafından hazırlanan Sanitizer API Playground'a göz atın:
Referanslar
- HTML Sanitizer API spesifikasyonu
- WICG/sanitizer-api repo
- Sanitizer API hakkında SSS
- MDN'deki HTML Sanitizer API referans dokümanları
Fotoğraf: Unsplash'taki Towfiqu barbhuiya