実現したいこと
JavascriptでDom XSSの脆弱性につながるコードを修正したい
前提
還移式の掲示板を作成していたのですが、非同期通信で1画面で完結する掲示板に変更中です
該当のソースコード
<div id="input_area"> <form name="input_form"> <div>名前<input type="text" name="namae" id="namae"></div> <div>コメント<textarea name="message" id="message"></textarea></div> <div> <input type="radio" name="stamp" value="1" id="stamp_1"><label for="stamp_1"></label> <input type="radio" name="stamp" value="2" id="stamp_2"><label for="stamp_2"></label> <input type="radio" name="stamp" value="3" id="stamp_3"><label for="stamp_3"></label> <input type="radio" name="stamp" value="4" id="stamp_4"><label for="stamp_4"></label> <input type="radio" name="stamp" value="5" id="stamp_5"><label for="stamp_5"></label> <input type="radio" name="stamp" value="6" id="stamp_6"><label for="stamp_6"></label> <input type="radio" name="stamp" value="7" id="stamp_7"><label for="stamp_7"></label> <input type="radio" name="stamp" value="8" id="stamp_8"><label for="stamp_8"></label> </div> <div> <input type="file" class="attach" name="attach[]"> <div class="viewer"></div> </div> <div> <input type="file" class="attach" name="attach[]"> <div class="viewer"></div> </div> <div> <input type="file" class="attach" name="attach[]"> <div class="viewer"></div> </div> <div> <button type="button" id="input_button">確認画面へ進む</button> </div> </form> </div> <div id="confirm_area"></div> <div id="result_area"></div> <script> const input_area = document.getElementById("input_area"); const confirm_area = document.getElementById("confirm_area"); const result_area = document.getElementById("result_area"); var namae_value = ""; var message_value = ""; var stamp_value = ""; function input_button_click() { namae_value = ""; message_value = ""; stamp_value = ""; //サーバーにデータを送信する際に使用するオブジェクトを生成 const formData = new FormData(input_form); //オブジェクト内の既存のキーに新しい値を追加 formData.append("action", "bbs_quest_input"); const opt = { method: "post", body: formData } //非同期通信 fetch("<?php echo home_url('wp-admin/admin-ajax.php'); ?>", opt) .then(response => { return response.json(); }) .then(json => { if (json.error != "") { alert(json.error); return; } namae_value = document.getElementById("namae").value; message_value = document.getElementById("message").value; const stamps = document.getElementsByName('stamp'); for (var stamp of stamps) { //checkedプロパティは、対象の要素がcheckedを持っていればtrueを、持っていなければfalseを返す if (stamp.checked) { stamp_value = stamp.value; break; } } confirm_area.innerHTML = ""; var div; var child; child = document.createElement("h2"); child.innerHTML = "確認画面"; confirm_area.appendChild(child); div = document.createElement("div"); child = document.createElement("p"); child.innerHTML = "名前:" + namae_value; div.appendChild(child); confirm_area.appendChild(div); div = document.createElement("div"); child = document.createElement("p"); child.innerHTML = "コメント:" + message_value; div.appendChild(child); confirm_area.appendChild(div); div = document.createElement("div"); child = document.createElement("input"); child.type = "radio"; child.name = "stamp"; child.id = "confirm_stamp"; child.value = stamp_value; child.checked = true; div.appendChild(child); child = document.createElement("label"); child.htmlFor = "confirm_stamp"; div.appendChild(child); confirm_area.appendChild(div); div = document.createElement("div"); for (const i in blobType) { if (blobType[i] != "") { child = null; if (blobType[i] == "img") { child = document.createElement("img"); } else if (blobType[i] == "video") { child = document.createElement("video"); child.setAttribute("controls", null); } else if (blobType[i] == "iframe") { child = document.createElement("iframe"); } if (child !== null) { child.style.maxHeight = "200px"; child.style.maxWidth = "200px"; child.src = blobUrl[i]; div.appendChild(child); } } } confirm_area.appendChild(div); div = document.createElement("div"); child = document.createElement("button"); child.type = "button"; child.innerText = "入力画面へ戻る"; child.addEventListener("click", () => { input_area.style.display = "block"; confirm_area.innerHTML = ""; confirm_area.style.display = "none"; }); div.appendChild(child); child = document.createElement("button"); child.type = "button"; child.innerText = "結果画面へ進む"; child.addEventListener("click", confirm_button_click); div.appendChild(child); confirm_area.appendChild(div); input_area.style.display = "none"; confirm_area.style.display = "block"; }) .catch(error => {}); } function confirm_button_click() { const formData = new FormData(); formData.append("action", "bbs_quest_confirm"); const opt = { method: "post", body: formData } fetch("<?php echo home_url('wp-admin/admin-ajax.php'); ?>", opt) .then(response => { return response.json(); }) .then(json => { if (json.error != "") { alert(json.error); return; } result_area.innerHTML = ""; var div; var child; child = document.createElement("h2"); child.innerHTML = "結果画面"; result_area.appendChild(child); div = document.createElement("div"); child = document.createElement("p"); child.innerHTML = "名前:" + namae_value; div.appendChild(child); result_area.appendChild(div); div = document.createElement("div"); child = document.createElement("p"); child.innerHTML = "コメント:" + message_value; div.appendChild(child); result_area.appendChild(div); div = document.createElement("div"); child = document.createElement("input"); child.type = "radio"; child.name = "stamp"; child.id = "result_stamp"; child.value = stamp_value; child.checked = true; div.appendChild(child); child = document.createElement("label"); child.htmlFor = "result_stamp"; div.appendChild(child); result_area.appendChild(div); div = document.createElement("div"); for (const i in blobType) { if (blobType[i] != "") { child = null; if (blobType[i] == "img") { child = document.createElement("img"); } else if (blobType[i] == "video") { child = document.createElement("video"); child.setAttribute("controls", null); } else if (blobType[i] == "iframe") { child = document.createElement("iframe"); } if (child !== null) { child.style.maxHeight = "200px"; child.style.maxWidth = "200px"; child.src = blobUrl[i]; div.appendChild(child); } } } result_area.appendChild(div); confirm_area.style.display = "none"; }) .catch(error => {}); } </script>
アドバイス頂きたいこと
セキュリティについて前回もアドバイス頂いたのですが、HTMLElement.innerHTML は変数が保証されていない場合 DOM-based XSS の原因につながるようで、どう修正していくべきか悩んでおります。
Document.createTextNode を代用する場合どのように修正すれば良いのでしょうか?
参考サイト
※DOM-based XSSについて
https://gihyo.jp/dev/serial/01/javascript-security/0006
※クロスサイトスクリプティング対策
https://atmarkit.itmedia.co.jp/ait/articles/1312/17/news010_2.html
https://atmarkit.itmedia.co.jp/ait/articles/1312/17/news010_2.html
回答2件
あなたの回答
tips
プレビュー