質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
WYSIWYG

WYSIWYGとは、ディスプレイに表示されたものが見た通りの状態でプリンターなどに出力できる技術、およびその概念です。HTMLがなくても容易にWebページのレイアウトなどができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Q&A

解決済

1回答

2339閲覧

WYSIWYGエディタで受け付けたユーザーからの値変換につきまして[XSS対策]

pegy

総合スコア245

WYSIWYG

WYSIWYGとは、ディスプレイに表示されたものが見た通りの状態でプリンターなどに出力できる技術、およびその概念です。HTMLがなくても容易にWebページのレイアウトなどができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

1グッド

0クリップ

投稿2021/04/16 14:53

編集2021/04/16 14:56

前提・実現したいこと

私はquill.jsというWYSIWYGエディタのライブラリを利用して、ユーザーからリッチテキストエディタから入力された値を取得して出力するアプリケーションを検討しております。以下のコードが当該ライブラリに基づき作成されたコードです。

html

1<!--sample.html--> 2<!DOCTYPE html> 3<html lang="ja"> 4<head> 5 <meta charset="utf-8"> 6 <title>TexTile</title> 7 8 <script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script> 9 <!--quill.js--> 10 <link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet"> 11 <script src="https://cdn.quilljs.com/1.3.6/quill.js"></script> 12</head> 13 14<body> 15 <div id="textile"></div> 16 <form class="" action="#" method="post"> 17 <input type="hidden" id="textile_hidden" name="editor-input"> 18 <input type="submit" name="" value="投稿する"> 19 </form> 20 21 <script> 22 var toolbarOptions_textile = [ 23 ['bold', 'italic', 'underline', 'strike'], // toggled buttons 24 ['blockquote',], // custom button values 25 [{ 'list': 'ordered'}, { 'list': 'bullet' },{ 'align': [] }], //{ 'direction': 'rtl' } 26 [{ 'indent': '-1'}, { 'indent': '+1' }], // outdent/indent 27 [{ 'script': 'sub'}, { 'script': 'super' }], // superscript/subscript 28 [{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown 29 [{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme 30 [{ 'font': [] }], 31 ['link', 'image','video'], 32 // ['formula'], 33 ['clean'], // remove formatting button 34 ]; 35 36 var quill_textile = new Quill('#textile', { 37 theme: 'snow', 38 placeholder: 'テキストを作成する', 39 modules: { 40 toolbar: toolbarOptions_textile 41 }, 42 }); 43 44 var editor = document.getElementById('textile'); 45 var editorInput = document.getElementById('textile_hidden'); 46 47 quill_textile.on('text-change', function(delta, oldDelta, source) { 48 var editorHtml = editor.querySelector('.ql-editor').innerHTML; 49 editorInput.value = editorHtml; //html形式で取得する 50 /*editorInput.value = JSON.stringify(quill_textile.getContents()) //JSON形式で取得 */ 51 }); 52 </script> 53 </body> 54</html>

ここで仮に以下のような値をユーザーから受け付けたとします。
画像

ここで上記のsample.htmlにおいて、2通りのエディタの入力値の取得方法があります。
0. htmlでそのまま出力して値を取得する editorInput.value = editorHtml; //html形式で取得する

  1. JSON形式で取得する(ライブラリが提供) editorInput.value = JSON.stringify(quill_textile.getContents()) //JSON形式で取得

上記の1.html形式でそのまま取得した場合には以下のように取得されます。

html

1<p>今日の日記は<strong><u>以下の通り</u></strong>です。</p> 2<ol> 3 <li><a href="https://finance.yahoo.co.jp/" target="_blank">Yahoo!ファイナンス</a>をチェックした。</li> 4 <li>友達とご飯を食べに行った。</li> 5</ol> 6<p>(メモ)こんなコードを思いついた。<script>alert("danger")</script></p></p> 7<p><span class="ql-size-large" style="background-color: rgb(255, 255, 0);">良い一日</span>でした!</p>

他方、2.JSON形式で出力した場合、以下のように出力されます。

JSON

1{"ops":[ 2 {"insert":"今日の日記は"}, 3 {"attributes":{"underline":true,"bold":true},"insert":"以下の通り"}, 4 {"insert":"です。\n"}, 5 {"attributes":{"link":"https://finance.yahoo.co.jp/"},"insert":"Yahoo!ファイナンス"}, 6 {"insert":"をチェックした。"}, 7 {"attributes":{"list":"ordered"},"insert":"\n"}, 8 {"insert":"友達とご飯を食べに行った。"}, 9 {"attributes":{"list":"ordered"},"insert":"\n"}, 10 {"insert":"(メモ)こんなコードを思いついた。\n"}, 11 {"attributes":{"background":"#ffff00","size":"large"},"insert":"良い一日"}, 12 {"insert":"でした!\n"} 13]}

実現したいこととしてこれらの値をRDBに格納し、安全に出力することとなります。

検討1

そこで、まずライブラリが提供する、上記のJSON形式のデータをパースしてHTMLを再構築した上で、"insert"の値をhtmlspecialchars()で出力することを検討しました。hrefやsrc属性の中身もリスクがある認識ですが、そのチェックは別途するものとしてここでは割愛させていただきます。
ただ、実際の構造を見ていただくと分かる通り、<ol><li>のネスト構造を識別し難いことや、attributesが属性だけを属性だけを指すのではなく例えばsizeの指定があれば、<span>を作った上で、特定のclass属性を指定するなど、実に使いづらい印象で、直接HTMLを取得して、自ら許可するタグを置換してホワイトリスト方式のアプローチで値を格納して出力する方がより効率的で合理的であると考えました。

検討2

上記検討1を踏まえて例えば、以下のように、ホワイトリストタグをHTMLを変換すれば、あとで出力する際にもある程度安全にできるのではと考えております。

HTML

1[*p*]今日の日記は[*strong*][*u*]以下の通り[*/u*][*/strong*]です。[*/p*] 2[*ol*] 3 [*li*][*a href(https://finance.yahoo.co.jp/),target(_blank)*]Yahoo!ファイナンス[*/a*]をチェックした。[*/li*] 4 [*li*]友達とご飯を食べに行った。[*/li*] 5[*/ol*] 6[*p*](メモ)こんなコードを思いついた。<script>alert("danger")</script></p>[*/p>*] 7[*p*][*span class(ql-size-large) style(background-color: rgb(255, 255, 0))*]良い一日[*/span*]でした![*/p>*] 8 9<script> 10 /* 11whiteListTag = ["<p>","<b>","<u>","<em>","<strike>","<strong>","<ol>","<ul>","<li>","<span>"] 12whiteListPop = ["class","style","href","target"] 13*/ 14</script>

質問

  1. ここで、例えば正規表現を用いて検討2に対応するばあ、閉じタグや属性がない開始タグであればpreg_replaceでそれぞれホワイトリストにあるものを置換していけば良いので分かるのですが、属性がある場合にはどのように考えれば良いのでしょうか?

例えば正規表現で/<span.*(class).*>/といった表現で該当するタグを見つけることなどが思いつくのですが、属性の記載順や属性の組み合わせなどはかなりの量に及ぶため、preg_match_allpreg_replaceを利用しても、分解して上記のような安全なコードに変換する術が思いつかないため、アドバイスを頂きたいです。

  1. また、そもそも置換の考え方に重大な問題を想定しており、上記のように<p>(メモ)こんなコードを思いついた。<script>alert("danger")</script></p></p>意図的に</p>の閉じタグを送られてきた場合、これまでも[*/p*]に変換されてしまい、ホワイトリストが悪用され、意図しない値を得ることになってしまうことが想定されます。

このようなケースの一般的な対応方法や、そもそも異なるアプローチをとるべきというアドバイスがあれば合わせてお願い申し上げます。

fugusuki2202👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

解決はしませんでしたが、JSONデータを利用して調整してみようと思います。

投稿2021/04/19 01:58

pegy

総合スコア245

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問