Hatena::ブログ(Diary)

葉っぱ日記 このページをアンテナに追加

2015-01-30

[][] ブラウザ上でMarkdownを安全に展開する  ブラウザ上でMarkdownを安全に展開するを含むブックマーク

不特定のユーザーが入力したMarkdownをブラウザ上でJavaScriptを使ってHTMLに変換するという場面においては、JavaScriptで変換してHTMLを生成するという処理の都合上どうしてもDOM-based XSSの発生を考えないわけにはいかない。かといって、MarkdownをパースしHTMLを生成するという処理すべてをXSSが存在しないように注意しながら自分で書くのも大変だし、markedmarkdown-jsなどの既存の変換用のJSを持ってきてもそれらがXSSしないかを確認するのは結構大変だったりする。

そういった場合には、Markdownから生成されたHTMLをRickDOMを通すことで、万が一HTML内にJavaScriptが含まれていたとしてもそれらを除外し、許可された要素、許可された属性だけで構築された安全なHTMLに再構築することができる。さらに、そうやって生成された安全なHTMLを sandbox属性付きのiframeの中に書き込むことで万が一RickDOMライブラリに脆弱性があったとしてもJavaScriptの実行を抑止することができる。

<iframe id="frame-main" sandbox="allow-same-origin allow-popups" ></iframe>
...
<script>
    var rickdom = new RickDOM();
    var unsafeHtml = markdown.toHTML( markdownText );                // 任意のライブラリを利用してmdをHTMLに変換する
    var elm = rickdom.build( "<div>" + unsafeHtml + "</div>" )[ 0 ]; // RickDOMにより安全なHTMLを再生成
    var html =  
        "<!doctype html><html><head>" +
        "<style>.....</style>" +
        "<body>" + elm.outerHTML + "</body></html>";
    document.getElementById( "frame-main" ).srcdoc = html;           // sandboxedなiframe内のコンテンツとして設定
</script>

sandbox属性にはallow-same-originフラグとallow-popupsフラグを指定している。allow-same-originフラグを指定することにより、iframeの外側からはiframe内のコンテンツを自由に操作することができる。これを指定しない場合は、iframe内外は強制的に異なるオリジン扱いとなってしまい、iframeの外側からiframe内のコンテンツを操作することができなくなってしまう。

<iframe id="frame-main" sandbox="allow-same-origin allow-popups" ></iframe>
...
<script>
    var elm = document.getElementById( "frame-main" ).contentDocument.querySelector( "h1" ); 
    if( elm ) elm.scrollIntoView( true );
</script>

また、allow-popupsフラグをつけていることにより、iframe内のリンク文字列に target="_blank" などを指定することでリンクをクリック時には新しいタブを開いてその内容を表示させることができる。

<iframe id="frame-main" sandbox="allow-same-origin allow-popups" ></iframe>
...
<script>
    // iframe内のhref属性を持つ<a>要素すべてにtarget=_blankを設定する
    Array.prototype.forEach.call(
        document.getElementById( "frame-main" ).contentDocument.querySelectorAll( "a[href]" ), 
        function( node ){
            node.setAttribute( "target", "_blank" );
        }
    );
</script>

このように、(1)RickDOMのような「一部のHTMLを許容しつつ安全にHTMLを組み立てる」ライブラリを使用する、(2)iframe sandboxを利用する、という2段階の保護を設けることにより、第三者の作成したMarkdownであっても安心して自身のサイト内にHTMLとして埋め込むことが可能になる。

実際に上記方法により任意のMarkdownを表示可能なサンプルを http://utf-8.jp/tool/md/?http%3A%2F%2Futf-8.jp%2Ftool%2Fmd%2Ftest.md に作成した。

参考 :

2015-01-03

[] ECMAScript テンプレートリテラル  ECMAScript テンプレートリテラルを含むブックマーク

(自分用メモ) ES6のテンプレートリテラルを使うと、HTML生成におけるテンプレート的なものがJavaScriptでも書きやすくなる。

function safeHtml(){
    var t = arguments[ 0 ].raw;
    var result = t[ 0 ];
    for( var i = 1; i < arguments.length; i++ ){
        result += arguments[i]
            .replace( /&/g, "&amp;")
            .replace( /</g, "&lt;" )
            .replace( />/g, "&gt;" )
            .replace( /"/g, "&quot;" )
            .replace( /'/g, "&#x27;" )
            + t[i];
    }
    return result;
}

var userinput = "<img src=# onerror=alert(1)>";
var html=safeHtml`<div>\r\n←これはそのまま円記号+英数字で表示される<div>${userinput}</div></div>`;
var elm.innerHTML = html;

2014-12-17

[] 脆弱性"&'<<>\ Advent Calendar 2014 (17日目)  脆弱性"&'<<>\ Advent Calendar 2014 (17日目)を含むブックマーク

この記事は脆弱性"&'<<>\ Advent Calendar 2014の17日目の記事です。今日は少し昔話をしようと思います。がはは。

かつて、日本製TwitterのようなWassrというサービスがありました。当時、Twitterは数日に一度くらいはサービスが落ちていて、Twitterユーザーも「またか」と思いながら我慢して使うようなサービスであり、Twitterが落ちるたびにWassrはユーザーを増やすとともに、画像の添付のように当時Twitterにはまだなかった機能をどんどんアグレッシブに取り入れていく、使っていて楽しいサービスでした。

さて、そんなWassrがある日絵文字機能を導入しました。当時はUnicode絵文字もなくスマートフォンも普及しておらず、主にレガシーな携帯電話で使える絵文字をなんとかWeb上でも使えるようにしたという感じのものでした。

絵文字をパレットから選択するとヒトコト(TwitterでいうTweet)を入力するテキストボックス内には {emoji:XXXX} のような形式の文字列が挿入され、それを投稿するとXXXXに該当する絵文字が表示されるという仕組みでした。

その機能が実装されてしばらくして、僕はどんな絵文字が使えるのか、すなわちパレット内に表示されているものが使える絵文字全てなのかどうなのかというのが気になり、とりあえず {emoji:0000}{emoji:0001}…というように片っ端から絵文字のコードを挿入して投稿してみたのでした。すると、応答されたHTMLの投稿したヒトコトが表示される部分以降が言葉どおり真っ白になっていて、</body>の閉じタグさえないという状態になってしまいました。ああ、なんだかよくわからないけど意図しない結果が返ってきた、これは面白い!そう思って僕は自分のユーザ名にも{emoji:xxxx}のような白くなる絵文字コードを設定し、ページをリロードしてみると、今度は自分のユーザー名が表示されるより以降のHTMLが途切れてページが真っ白になって返ってきました。

よしもう少し調べようとHTMLソースを見ながらページをリロードすると…今度はなんとログインページが表示されてしまったのです。あれ?ログインしているはずなのに、なにか操作ミスをしたかなと思いながら自分のIDとパスワードを入力しログインを試みると、なんと「そのユーザーは存在しません」というエラーメッセージが。

ここで僕は、もしかして自分のしたことが原因でアカウントが消えるような惨事を引き起こしたのか、あるいは運営からトラブルを作ったということでアカウント消されたのだろうかと思い至ったわけです。

Wassrのアカウントを消されたので焦ってTwitterに書き込んだ当時のTweetがこれ。

そして、それをみたkanさんのTweetがこれ。

その時点では詳しくは知らなかったのですが、後日聞いたところによると

  • 存在しない絵文字のコードが指定された場合、例外をトラップしていなかったために絵文字を展開するサービスが落ちてしまいその絵文字指定より後ろのHTMLが生成されなくなる
  • 私のユーザ名部分にそういった絵文字コードが含まれていたため私をフォローしている人全員にそういう現象が発生した
  • とにかく復旧のために焦って私の投稿したヒトコトを消したもののまだ現象がなおらない(ユーザ名にも含まれていたので)
  • そんなわけでとりあえず私のアカウントを消した

という状況だったようです。そして落ち着いたところでWassrアカウントは復旧されたのですが、復旧された時点ではよほど焦ったのか、ユーザ名が "hsegawa" になっていました。それはそれで気に入ったというのもあり、以降当分の間僕は反省の意味も込めてユーザー名に hsegawa を使ったりもしていたのですけど。

そして、さらにその後、Wassr開発の人と飲みに行くようになり、その席でやはりこの話が出たときには「あれは正直訴えてもいいかと思った。何しろ広告が表示されないという実害が出たので」ということを言われたのでした。

教訓:攻撃するつもりがなくても遊びすぎるとよくない。

プログラマですがプログラマですが 2014/12/17 10:15 バグったソフトをリリースしておいて訴えるとかいう人とは、わたしは付き合いたくないですね。

kiya2015kiya2015 2014/12/17 10:24
カギに欠陥があって泥棒に入られた場合、たしかに欠陥のあるカギを作った製造者にも落ち度があるが泥棒は罪を問われるのが当然。

プログラマですがプログラマですが 2014/12/17 12:15 なにが当然なんだかさっぱりわかりません。誰も泥棒してないし。

バグを見つけてくれたのだから感謝するべきところを、逆恨みしたり訴えたりなどという思考回路はソフト屋としては二流もしくは素人です。

mtodmtod 2014/12/17 23:53 この場合、訴えるってのはどう考えても飲みの席の冗談でしょ

まあねまあね 2014/12/18 11:40 広告非表示で実害って言い方も気になったけど、目くじら立てる話ではないわな

双六双六 2014/12/18 14:32 「ああ、なんだかよくわからないけど意図しない結果が返ってきた、これは面白い!」
この発想好きだなぁ

2014-12-16

[] 脆弱性"&'<<>\ Advent Calendar 2014 (16日目)  脆弱性"&'<<>\ Advent Calendar 2014 (16日目)を含むブックマーク

この記事は脆弱性"&'<<>\ Advent Calendar 2014の16日目の記事です。

f:id:hasegawayosuke:20141216085343p:image

Enjoy!

で終わらせようかと思ったんだけど、毎日Enjoyし過ぎじゃないのみたいに思われそうなのでここ数日のを少し解説。

脆弱性"&'<<>\ Advent Calendar 2014 (12日目)
URLが示すとおりAppleのサイトで任意コンテンツを表示可能な脆弱性。見つけたときにはもともとAppleサイトの問題なのかなと思ったけれど、Oracleのサイトにも同じような問題があって、Oracleへ連絡したらJavadocの脆弱性ということでJavaの脆弱性修正にて対応された。
脆弱性"&'<<>\ Advent Calendar 2014 (13日目)
特許庁。見たまま。フレーム内に任意コンテンツを挿入可能。
脆弱性"&'<<>\ Advent Calendar 2014 (14日目)
オライリーのフィードバックコメントを書くページ。12日目、13日目はiframeやframeとして任意コンテンツが差し込めるだけだったけどオライリーのサイトはURLを見るとわかるように普通にXSS。
脆弱性"&'<<>\ Advent Calendar 2014 (15日目)
マイクロソフト。alert内を見るとわかるようにDOM based XSSなので2013年当時のChromeのXSS Auditorでは止まらない(今だとたぶん止まる)。
脆弱性"&'<<>\ Advent Calendar 2014 (16日目)
今日。マイクロソフト。DOM based XSSでIE10のXSSフィルターを通過。

他にもいろいろあるけど出すと怒られたり金融系のように不安をあおりそうなところも多いのでこれくらいで。明日はまじめなネタを書くかも。

Enjoy!

2014-12-15

[] 脆弱性"&'<<>\ Advent Calendar 2014 (15日目)  脆弱性"&'<<>\ Advent Calendar 2014 (15日目)を含むブックマーク

この記事は脆弱性"&'<<>\ Advent Calendar 2014の15日目の記事です。

f:id:hasegawayosuke:20141215081404p:image

Enjoy!