DOM'a rastgele JSON yerleştirmek için en iyi uygulama?


110

DOM'a rastgele JSON yerleştirmeyi şu şekilde düşünüyorum:

<script type="application/json" id="stuff">
    {
        "unicorns": "awesome",
        "abc": [1, 2, 3]
    }
</script>

Bu, rastgele bir HTML şablonunun daha sonra bir JavaScript şablon motoruyla kullanılmak üzere DOM'da saklanmasına benzer. Bu durumda, daha sonra JSON'yi alıp şu şekilde ayrıştırabiliriz:

var stuff = JSON.parse(document.getElementById('stuff').innerHTML);

Bu işe yarıyor , ama en iyi yol bu mu? Bu, herhangi bir en iyi uygulamayı veya standardı ihlal ediyor mu?

Not: JSON'u DOM'da depolamak için alternatifler aramıyorum, yaşadığım belirli sorun için en iyi çözümün bu olduğuna zaten karar verdim. Ben sadece bunu yapmanın en iyi yolunu arıyorum.


1
neden varjavascript olarak kullanmıyorsunuz?
Krizz

@Krizz, daha sonra karmaşık bir kapsüllenmiş javascript zinciri tarafından işlendiği statik bir belgenin parçası olması gerekir. Bunu DOM'da depolamak, yapmak istediğim şey.
Ben Lee

@Krizz Ben de benzer bir sorunla karşılaştım. AJAX isteği yapmadan her kullanıcı için farklı bir siteye veri koymak istedim. Bu yüzden, bazı PHP'yi bir konteynere gömdüm, javascript'teki verileri almak için yukarıda sahip olduğunuza benzer bir şey yaptım.
Patrick Lorio

2
Aslında orijinal yönteminizin en iyisi olduğunu düşünüyorum. HTML5'te% 100 geçerlidir, ifade edicidir, CSS ile kaldıracağınız veya gizleyeceğiniz "sahte" öğeler oluşturmaz; ve herhangi bir karakter kodlaması gerektirmez. Olumsuz tarafı nedir?
Jamie Treworgy

22
</script><script>alert()</script><script>JSON nesnenizin içindeki değere sahip bir dizeniz varsa sürprizlerle karşılaşırsınız. Önce verileri temizlemediğiniz sürece bu güvenli değildir.
silviot

Yanıtlar:


77

Orijinal yönteminizin en iyisi olduğunu düşünüyorum. HTML5 spesifikasyonu bu kullanımı bile ele alır:

"Veri bloklarını dahil etmek için kullanıldığında (komut dosyalarının aksine), veriler satır içinde gömülmeli, verilerin formatı type özelliği kullanılarak verilmeli, src niteliği belirtilmemeli ve komut dosyası öğesinin içeriği kullanılan format için tanımlanan gereksinimlere uygundur. "

Burada okuyun: http://dev.w3.org/html5/spec/Overview.html#the-script-element

Tam olarak bunu yaptın. Sevmemek nedir? Öznitelik verileriyle gerektiği gibi karakter kodlaması yok. İsterseniz biçimlendirebilirsiniz. Anlamlı ve kullanım amacı açıktır. Bir hack gibi gelmiyor (örneğin "taşıyıcı" öğenizi gizlemek için CSS kullanmak gibi). Tamamen geçerli.


3
Teşekkür ederim. Spesifikasyondan alınan alıntı beni ikna etti.
Ben Lee

17
Yalnızca JSON nesnesini önce kontrol edip temizlerseniz mükemmel bir şekilde geçerlidir: Kullanıcı kaynaklı verileri öylece gömemezsiniz. Soru hakkındaki yorumuma bakın.
silviot

1
ekstra merak: onu koymak için iyi yer neresi? baş mı gövde mi, üst mü yoksa alt mı?
challet

1
Maalesef, CSP politikasının tüm etiketleri durdurabileceği / durduracağı görülüyorscript .
Larry K

2
</script> içeren ve dolayısıyla HTML yerleştirmeye izin veren JSON'un gömülmesine karşı nasıl etkili bir şekilde korunursunuz? Sağlam / kolay bir şey var mı yoksa veri özniteliklerini kullanmak daha mı iyi?
jonasfj

23

Genel bir yön olarak, bunun yerine HTML5 veri özniteliklerini kullanmayı deneyeceğim . Geçerli JSON eklemenizi engelleyecek hiçbir şey yok. Örneğin:

<div id="mydiv" data-unicorns='{"unicorns":"awesome", "abc":[1,2,3]}' class="hidden"></div>

JQuery kullanıyorsanız, geri almak şu kadar kolaydır:

var stuff = JSON.parse($('#mydiv').attr('data-unicorns'));

1
Mantıklı. Anahtar adı için tek tırnak işareti JSON.parsekullanmanın işe yaramayacağını unutmayın (en azından yerel Google Chrome JSON.parse işe yaramaz). JSON belirtimi çift tırnak gerektirir. Ancak, gibi varlıkları kullanarak düzeltmek yeterince kolaydır ...&lt;unicorns&gt;:....
Ben Lee

4
Yine de bir soru: HTML 5'teki özelliklerin uzunluğu için herhangi bir sınır var mı?
Ben Lee

Evet, bu işe yarar. Ayrıca, HTML'nizin tek tırnak kullanması ve JSON verilerinin çift tırnak kullanması için bunu değiştirebilirsiniz.
Horatio Alderaan

1
Tamam, sorumun cevabını buldum: stackoverflow.com/questions/1496096/… - bu benim amaçlarım için yeterli.
Ben Lee

2
Bu, tek bir dize için, örneğin "I am valid JSON"etiket için çift tırnak kullanmak veya dizede data-unicorns='"My JSON's string"'tek tırnaklı tek tırnak kullanmak , örneğin tek tırnaklar JSON olarak kodlamayla kaçmadığından işe yaramaz .
Robbie Averill

13

Bir komut dosyası etiketine json yerleştirme yönteminin olası bir güvenlik sorunu vardır. Json verilerinin kullanıcı girdisinden kaynaklandığını varsayarsak, gerçekte kod etiketinden çıkacak ve doma doğrudan enjeksiyona izin verecek bir veri üyesi oluşturmak mümkündür. Buraya bakın:

http://jsfiddle.net/YmhZv/1/

İşte enjeksiyon

<script type="application/json" id="stuff">
{
    "unicorns": "awesome",
    "abc": [1, 2, 3],
    "badentry": "blah </script><div id='baddiv'>I should not exist.</div><script type="application/json" id='stuff'> ",
}
</script>

Kaçış / kodlama yapmanın hiçbir yolu yoktur.


7
Bu doğru, ancak yöntemin gerçekten bir güvenlik açığı değil. Sayfalarınıza kullanıcı girdisinden kaynaklanan bir şey koyuyorsanız, ondan kaçmak için gayretli olmalısınız. Bu yöntem, kullanıcı girdisi ile ilgili normal ihtiyati tedbirleri aldığınız sürece hala geçerlidir.
Ben Lee

JSON, HTML'nin bir parçası değildir, HTML ayrıştırıcı çalışmaya devam eder. JSON'un bir metin paragrafının veya div öğesinin parçası olacağı durumla aynıdır. Programınızdaki içerikten HTML kaçışı yapın. Ayrıca eğik çizgilerden de kaçabilirsiniz. JSON bunu gerektirmezken, gereksiz eğik çizgileri tolere eder. Onu gömmek için güvenli hale getirmek amacıyla kullanılabilir. PHP'nin json_encode'u bunu varsayılan olarak yapar.
Timo Tijhof

7

OWASP'nin XSS önleme hile sayfasındaki Kural 3.1'e bakın .

Bu JSON'yi HTML'ye dahil etmek istediğinizi varsayalım:

{
    "html": "<script>alert(\"XSS!\");</script>"
}

<div>HTML'de bir gizli oluşturun . Ardından, güvenli olmayan varlıkları (örneğin, &, <,>, ", 've, /) kodlayarak JSON'nuzdan çıkış yapın ve onu öğenin içine yerleştirin.

<div id="init_data" style="display:none">
        {&#34;html&#34;:&#34;&lt;script&gt;alert(\&#34;XSS!\&#34;);&lt;/script&gt;&#34;}
</div>

Artık textContent, JavaScript kullanarak öğenin içeriğini okuyup ayrıştırarak ona erişebilirsiniz :

var text = document.querySelector('#init_data').textContent;
var json = JSON.parse(text);
console.log(json); // {html: "<script>alert("XSS!");</script>"}

Bunun en iyi ve en güvenli cevap olduğuna inanıyorum. Birçok ortak JSON karakterinin kaçtığına ve nesnedeki iç tırnak işaretleri gibi bazı karakterlerin çift çıkış yapıldığına dikkat edin {name: 'Dwayne "The Rock" Johnson'}. Ancak, çerçeve / şablonlama kitaplığınız muhtemelen HTML kodlaması yapmak için güvenli bir yol içerdiğinden, bu yaklaşımı kullanmak muhtemelen en iyisidir. Bir alternatif, hem HTML güvenli hem de JS dizesi içine koymak için güvenli olan base64 kullanmak olabilir. JS'de btoa () / atob () kullanarak kodlamak / kodunu çözmek kolaydır ve muhtemelen sunucu tarafında yapmak sizin için kolaydır.
sstur

Daha da güvenli bir yöntem, anlamsal olarak doğru <data>öğeyi kullanmak ve JSON verilerini valueözniteliğe dahil etmek olacaktır. Daha sonra &quot, veriyi kapatmak için çift tırnak kullanırsanız veya &#39;tek tırnak kullanırsanız (muhtemelen daha iyi olan) tırnak işaretlerinden kaçmanız gerekir .
Rúnar Berg

5

JSON'u bir işlev geri çağırma ( JSONP türü ) ile bir satır içi komut dosyasına koymanızı öneririm :

<script>
someCallback({
    "unicorns": "awesome",
    "abc": [1, 2, 3]
});
</script>

Yürütme betiği belgeden sonra yüklenirse, bunu bir yerde, muhtemelen ek bir tanımlayıcı bağımsız değişkeniyle saklayabilirsiniz: someCallback("stuff", { ... });


@BenLee, geri arama işlevini tanımlamanın tek dezavantajı ile çok iyi çalışmalıdır. Önerilen diğer çözüm, JSON'nizde varsa, özel HTML karakterleri (örneğin &) ve alıntıları bozar.
kopyala

Bu daha iyi hissettiriyor çünkü verileri bulmak için bir dom sorgusuna ihtiyacınız yok
Jaseem

@copy Bu çözümün hala kaçması gerekiyor (sadece farklı bir tür), MadCoder'in cevabına bakın. Tam olması için burada bırakıyorum.
pvgoran

2

Benim tavsiyem, JSON verilerini harici .jsondosyalarda tutmak ve sonra bu dosyaları Ajax aracılığıyla almak olacaktır. CSS ve JavaScript kodunu web sayfasına (satır içi) koymuyorsunuz, öyleyse neden JSON ile yapasınız?


12
Genellikle diğer sayfalar arasında paylaşıldığı için CSS ve Javascript'i bir web sayfasına satır içi koymazsınız. Söz konusu veriler, sunucu tarafından bu bağlam için açıkça oluşturulmuşsa, bunları gömmek, önbelleğe alınamayan bir şey için başka bir istek başlatmaktan çok daha etkilidir.
Jamie Treworgy

Çünkü kötü tasarlanmış eski bir sistemde güncellemeler yapıyorum ve tüm sistemi yeniden tasarlamak yerine sadece bir parçayı tamir etmem gerekiyor. JSON'u DOM'da depolamak, bu parçayı düzeltmenin en iyi yoludur. Ayrıca @jamietre'nin söylediklerine katılıyorum.
Ben Lee

@jamietre OP'nin bu JSON dizesine yalnızca daha sonra ihtiyaç duyulduğunu belirttiğine dikkat edin . Soru, her zaman veya sadece bazı durumlarda gerekli olup olmadığıdır. Yalnızca bazı durumlarda gerekliyse, harici bir dosyada olması ve yalnızca koşullu olarak yüklenmesi mantıklıdır.
Šime Vidas

2
Ölçeği şu ya da bu şekilde değiştirebilecek pek çok "eğer varsa" olduğuna katılıyorum. Ancak genel olarak konuşursak, sayfanın ne zaman oluşturulacağını biliyorsanız - muhtemelen olsa bile - hemen göndermeniz daha iyidir. Örneğin, daraltılmış olarak başlayan bazı bilgi kutularım olsaydı, genellikle içeriklerini satır içine dahil etmek isterdim, böylece anında genişlerler. Yeni bir isteğin ek yükü, mevcut bir istek üzerindeki biraz fazladan verinin ek yüküne kıyasla çok fazladır ve daha duyarlı bir kullanıcı deneyimi yaratır. Eminim bir kırılma noktası vardır.
Jamie Treworgy

2

HTML5, makinede okunabilir verileri tutmak için bir <data>öğe içerir . Belki de daha güvenli bir alternatif olarak <script type="application/json">JSON verilerinizi valueo öğenin özelliğine dahil edebilirsiniz .

const jsonData = document.querySelector('.json-data');
const data = JSON.parse(jsonData.value);

console.log(data)
<data class="json-data" value='
  {
    "unicorns": "awesome",
    "abc": [1, 2, 3],
    "careful": "to escape &#39; quotes"
  }
'></data>

Bu durumda , değeri çift tırnak içine almayı seçerseniz , tüm tek tırnak işaretlerini ile &#39;veya ile değiştirmeniz gerekir &quot;. Aksi takdirde, diğer yanıtların önerdiği gibi riskli XSS saldırılarınız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.