DOMに任意のJSONを埋め込むためのベストプラクティス?


110

次のように、DOMに任意のJSONを埋め込むことを考えています。

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

これは、JavaScriptテンプレートエンジンで後で使用するために、任意のHTMLテンプレートをDOMに格納する方法に似ています。この場合、後でJSONを取得して次のように解析できます。

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

これは機能しますが、最善の方法ですか?これはベストプラクティスや標準に違反していますか?

注:私は、JSONをDOMに保存する代わりの方法を探していません。特定の問題に対する最善の解決策であるとすでに判断しています。私はそれを行うための最良の方法を探しています。


1
なぜあなたはそれをvarjavascript として持っていないのですか?
Krizz

@Krizz、それは後でカプセル化されたJavaScriptの複雑なチェーンによって処理される静的ドキュメントの一部である必要があります。それをDOMに保存するのが私がやりたいことです。
ベン・リー

@Krizz私は同様の問題で提起されました。AJAXリクエストを行わずに、ユーザーごとに異なるサイトにデータを配置したかったのです。だから私はいくつかのPHPをコンテナーに埋め込んで、JavaScriptでデータを取得するために上記と同様のことを行いました。
Patrick Lorio

2
あなたのオリジナルの方法が実際には最高だと思います。HTML5では100%有効です。表現力豊かで、CSSで削除または非表示にする「偽の」要素を作成しません。そして、文字エンコーディングを必要としません。欠点は何ですか?
Jamie Treworgy

22
</script><script>alert()</script><script>JSONオブジェクト内に値を含む文字列がある場合は、驚くでしょう。最初にデータをサニタイズしない限り、これは安全ではありません。
silviot 2014

回答:


77

あなたのオリジナルの方法が最高だと思います。HTML5仕様はこの用途にも対応しています。

「(スクリプトではなく)データブロックを含めるために使用する場合、データはインラインで埋め込む必要があり、データの形式はtype属性を使用して指定する必要があり、src属性を指定してはならず、script要素の内容を指定する必要があります。使用するフォーマットに定義された要件に準拠する。」

こちらをお読みください:http : //dev.w3.org/html5/spec/Overview.html#the-script-element

あなたはまさにそれをしました。愛してはいけないことは何ですか?属性データで必要な文字エンコードはありません。必要に応じてフォーマットできます。それは表現力豊かであり、使用目的は明確です。それはハックのようには感じられません(たとえば、CSSを使用して "carrier"要素を非表示にするように)。それは完全に有効です。


3
ありがとうございました。スペックからの引用は私を納得させました。
ベン・リー

17
最初にJSONオブジェクトをチェックしてサニタイズする場合にのみ完全に有効です。ユーザーが作成したデータを埋め込むことはできません。質問に対する私のコメントを参照してください。
silviot 2014

1
余分な疑問:それを置くのに良い場所はどこですか?頭または体、上または下?
シャレット2017年

1
残念ながら、表示されます CSPポリシーが/すべて停止しますことをscript、タグを。
ラリーK

2
</ script>を含み、したがってHTMLインジェクションを許可するJSONを埋め込むことからどのように効果的に保護しますか?しっかりした/簡単なものはありますか、それともデータ属性を使用する方が良いですか
jonasfj

23

一般的な方向性として、代わりにHTML5データ属性を使用します。有効なJSONを入力するのを止めるものは何もありません。例えば:

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

jQueryを使用している場合は、次のように簡単に取得できます。

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

1
理にかなっている。ただし、キー名に単一引用符を使用するJSON.parseと機能しません(少なくともネイティブのGoogle Chrome JSON.parseは機能しません)。JSON仕様では二重引用符が必要です。しかし、のようなエンティティを使用して修正するのは簡単...&lt;unicorns&gt;:...です。
ベン・リー

4
ただし、1つの質問:HTML 5の属性の長さに制限はありますか?
Ben Lee

はい、それでうまくいきます。HTMLを一重引用符で使用し、JSONデータで二重引用符を使用するように切り替えることもできます。
Horatio Alderaan

1
わかりました、私の質問への答えを見つけました:stackoverflow.com/questions/1496096/…-これは私の目的には十分です。
ベン・リー

2
これは単一の文字列では機能しません。たとえば"I am valid JSON"、タグに二重引用符を使用したり、文字列に単一引用符を含む単一引用符を使用したりします。たとえばdata-unicorns='"My JSON's string"'、単一引用符はJSONとしてエンコードするとエスケープされません。
ロビーアベリル

13

jsonをスクリプトタグに埋め込むこの方法には、潜在的なセキュリティ上の問題があります。jsonデータがユーザー入力から発信されたと仮定すると、事実上スクリプトタグから抜け出し、domへの直接注入を可能にするデータメンバーを作成することが可能です。こちらをご覧ください:

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

これが注射です

<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>

エスケープ/エンコードを回避する方法はありません。


7
これは本当ですが、メソッドのセキュリティ上の欠陥ではありません。ユーザー入力に由来する何かをページに入れる場合は、それをエスケープすることに注意を払う必要があります。この方法は、ユーザー入力に関する通常の予防策を講じている限り、引き続き有効です。
ベン・リー

JSONはHTMLの一部ではありません。HTMLパーサーはそのまま続行します。JSONがテキスト段落またはdiv要素の一部である場合と同じです。プログラムのコンテンツをHTMLエスケープします。さらに、スラッシュをエスケープすることもできます。JSONはこれを必要としませんが、不要なスラッシュを許容します。安全に埋め込むために彼女を使用することができます。PHPのjson_encodeは、デフォルトでこれを行います。
Timo Tijhof、2015

7

OWASPのXSS防止チートシートのルール#3.1を参照してください。

このJSONをHTMLに含めたいとします。

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

<div>HTMLで非表示を作成します。次に、安全でないエンティティ(&、<、>、 "、 '、および/など)をエンコードしてJSONをエスケープし、要素内に配置します。

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

これtextContentで、JavaScriptを使用して要素のを読み取り、解析することで、要素にアクセスできます。

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

私はこれが最善かつ最も安全な答えだと思います。オブジェクト内の引用符など、多くの一般的なJSON文字がエスケープされ、特定の文字が二重にエスケープされることに注意してください{name: 'Dwayne "The Rock" Johnson'}。ただし、フレームワーク/テンプレートライブラリには、HTMLエンコーディングを行う安全な方法がすでに含まれている可能性が高いため、おそらくこのアプローチを使用するのがおそらく最善です。別の方法としては、HTMLで安全であり、JS文字列内に安全に配置できるbase64を使用します。JSでbtoa()/ atob()を使用してエンコード/デコードするのは簡単で、サーバー側で実行するのはおそらく簡単です。
sstur

さらに安全な方法は、意味的に正しい<data>要素を使用し、value属性にJSONデータを含めることです。次に&quot、データを囲むために二重引用符を使用する場合、または&#39;単一引用符を使用する場合(おそらくより良い)、引用符をエスケープする必要があるだけです。
ルナーベルク

5

関数コールバック(JSONPの一種)を使用して、JSONをインラインスクリプトに挿入することをお勧めします。

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

実行中のスクリプトがドキュメントの後にロードされている場合、おそらく追加の識別子引数を使用して、これをどこかに保存できます。 someCallback("stuff", { ... });


@BenLeeは非常にうまく機能するはずですが、コールバック関数を定義する必要があるという唯一の欠点があります。JSONに特殊なHTML文字(&など)と引用符がある場合、他の推奨される解決策は無効になります。
コピー

データを見つけるためにdomクエリを必要としないため、これは気分が良くなります
Jaseem

@copyこの​​ソリューションでは、エスケープが必要です(種類が異なります)。MadCoderの回答を参照してください。完全にするために、ここにそのまま残します。
pvgoran 2017

2

私の推奨は、JSONデータを外部.jsonファイルに保持し、それらのファイルをAjax経由で取得することです。CSSおよびJavaScriptコードをWebページ(インライン)に配置しないので、なぜJSONを使用するのですか?


12
CSSとJavascriptは通常、他のページ間で共有されるため、Webページにインラインで配置しないでください。問題のデータがこのコンテキストに対してサーバーによって明示的に生成された場合、そのデータを埋め込む方が、キャッシュできない何かに対する別の要求を開始するよりもはるかに効率的です。
Jamie Treworgy

これは、設計が不十分なレガシーシステムを更新しているためです。システム全体を再設計するのではなく、一部を修正するだけで済みます。JSONをDOMに格納することが、この部分を修正するための最良の方法です。また、@ jamietreの発言にも同意します。
ベン・リー

@jamietre OPは、このJSON文字列はでのみ必要になると述べていることに注意してください。問題は、それが常に必要か、または一部の場合にのみ必要かです。一部の場合にのみ必要な場合は、外部ファイルに入れて、条件付きでのみロードすることは理にかなっています。
サイムVIDAS

2
スケールを何らかの方法で転倒させる可能性のある「what ifs」がたくさんあることに同意します。しかし、一般的に言えば、ページがいつレンダリングされる必要があるかがわかっている場合は、たとえそれが可能であっても、すぐに送信する方が良いことがよくあります。たとえば、折りたたみを開始する情報ボックスがいくつかある場合、通常はそれらのコンテンツをインラインで含めて、即座に展開するようにします。新しいリクエストのオーバーヘッドは、既存のリクエストの余分なデータのオーバーヘッドに比べて大きく、応答性の高いユーザーエクスペリエンスを実現します。ブレークポイントがあると確信しています。
Jamie Treworgy

2

HTML5には、機械で読み取り可能なデータを保持するための<data>要素が含まれています。より安全な方法<script type="application/json">として、JSONデータvalueをその要素の属性内に含めることができます。

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>

この場合、値を二重引用符で囲むことを選択&#39;した&quot;場合は、すべての単一引用符をまたはで置き換える必要があります。それ以外の場合、他の回答のようなリスクXSS攻撃が示唆しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.