Yeni Sanitizer API, rastgele dizelerin bir sayfaya güvenli bir şekilde eklenmesi için sağlam 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 gösterilmezse kötü amaçlı saldırganların istismar edebileceği siteler arası komut dosyası çalıştırma (XSS) fırsatları kolayca oluşturulabilir.
Bu riski azaltmak için yeni Sanitizer API önerisi, rastgele dizelerin bir sayfaya güvenli bir şekilde eklenmesi için sağlam bir işlemci oluşturmayı amaçlamaktadır. 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
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. 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.
Sanitasyonun sonucunu dize olarak almak için setHTML()
sonuçlarından .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ütmeyi 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
: Sanitasyon aracının, alt öğelerini koruyarak kaldırması gereken öğelerin adları.
dropElements
: Temizleyicinin alt öğeleriyle birlikte kaldırması gereken öğelerin 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çeneklerle temizleyicinin belirtilen özelliklere izin verip vermeyeceğini de kontrol edebilirsiniz:
allowAttributes
dropAttributes
allowAttributes
ve dropAttributes
mülkleri, özellik eşleme listeleri (anahtarları özellik adları, değerleri ise hedef öğelerin listeleri veya *
joker karakteri olan nesneler) bekler.
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, temizleme işlevi sunan iyi bilinen 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="">`
DOMPurify, Sanitizer API tarayıcıda uygulanmadığında 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şlem süresini boşa harcamakla birlikte, ikinci ayrıştırmanın sonucunun ilkinden farklı olduğu durumlarda ilginç güvenlik açıklarına da neden olabilir.
HTML'nin ayrıştırılması için bağlam da gerekir. Örneğin, <td>
<table>
için geçerlidir ancak <div>
için geçerli 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ını iyileştirir ve ç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, 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şturma | 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 öneriyi prototiplemeye değer buluyor ve aktif olarak uyguluyor.
WebKit: Yanıtı WebKit posta listesinde bulabilirsiniz.
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
bölümündeki dom.security.sanitizer.enabled
bayrağını 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 hata veya beklenmedik bir davranışla karşılaşırsanız hatayı bildirmek için hata kaydı oluşturun. 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