Hatena::ブログ(Diary)

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

2016-02-02

[] Launch Burp from inside Fiddler  Launch Burp from inside Fiddlerを含むブックマーク

I've written the article about FiddlerScript last week and I've gotten reply like this:

Yes, I know Burp rulez but I love Fiddler without reason so match so that I've just written Fiddler Script for launching Burp. Add these code snippet to your CustomRules.js of FiddlerScript, you can see 2 menu items, "Launch burp" in Tools menu, "Use Upstream Burp" in "Rules" menu. Click "Launch burp" first and turn-on "Use Upstream Burp" to start inspecting http traffic with Burp.

Enjoy!

class Handlers
{
    public static const m_burpPort : String = "8080";
    public static RulesOption( "Use Upstream &Burp" )
    var m_UseBurp: boolean = false;

    public static ToolsAction( "Launch &burp" )
    function launchBurp(){
        const cmd = "cmd";
        const cmdArgs = "/c java -jar -Xmx1024m";
        const burpFolder = "C:\\Program Files (x86)\\burp";

        var req : System.Net.HttpWebRequest;
        var res : System.Net.HttpWebResponse;
        var running : Boolean = false;
        var files : String[];
        var i : Number;
        var burpJar : String = "";
        
        try{
            FiddlerObject.StatusText = "Checking burp already running";
            var req = System.Net.HttpWebRequest( System.Net.WebRequest.Create( "http://localhost:" + m_burpPort ) );
            req.Headers.Add( "X-check-burp", "1" );
            res = System.Net.HttpWebResponse( req.GetResponse() );
            if(  res.StatusDescription === "OK" ){
                running = true;
            }
        }catch( e ){
            running = false;
        }
        if( !running ){
            // search latest burp jar
            files = System.IO.Directory.GetFiles( burpFolder, "burpsuite*.jar" );
            for( i = 0; i < files.length; i++ ){
                if( files[ i ] > burpJar ){
                    burpJar = files[ i ];
                }
            }
            if( burpJar === "" ){
                FiddlerObject.alert( "nou found burpsuite*.jar in "  + burpFolder );
                return;
            }
            FiddlerObject.StatusText = "Launching burp...";
            System.Diagnostics.Process.Start( cmd, cmdArgs + " \"" + burpJar + "\"" );
            FiddlerObject.StatusText = "";
        }else{
            FiddlerObject.StatusText = "burp is already running";
        }
    }
    static function OnBeforeRequest(oSession: Session) {
        if( oSession.oRequest.headers.Exists( "X-check-burp" ) ){
            oSession["ui-hide"] = "burp";
        }
        if( m_UseBurp ){
            oSession.bypassGateway = false;
            oSession[ "x-overrideGateway" ] = "localhost:" + m_burpPort;
        }
    }
}

2016-01-24

[] Electronのwebview要素ではallowpopups属性をつけてはいけない  Electronのwebview要素ではallowpopups属性をつけてはいけないを含むブックマーク

Electronを使ってブラウザのようなアプリケーションを作る場合には webviewタグが使用される。例えば、アプリケーション内にexample.jpのサイトを表示するには以下のようにHTMLに記述する。

<webview src="http://example.jp/"></webview>

ここで、webviewタグにallowpopups属性を付与すると、example.jpサイト内のコードからwindow.open等を使って新たにウィンドウを開くことができるようになる。このとき、example.jpに悪意があり以下のようなコードが含まれているとする。

if( typeof require === "undefined" ) window.open( 'http://example.jp/', '', 'nodeIntegration=1');
else require( "child_process").exec("calc.exe",function(){});

requireが未定義の場合には自身のサイトをwindow.openを使って開きなおしているが、このときに'nodeIntegration=1'を与えている。これにより、新たに生成されたwindowではnodeの機能すなわちrequireやprocessなどが利用可能になる。コードのelse以降は開きなおされたwindow内で動作し、requireが利用可能になっているので、ここでは電卓を起動している。

このように、webviewタグにallowpopups属性が付与されている場合、webview内に表示されているサイトからクライアントPC上で任意コードの実行が可能になる可能性がある。

ここで注意すべきは、allowpopups属性はあくまでも新たにポップアップウィンドウを開くことを許可しているだけであり、開かれたウィンドウがnode機能を利用可能かどうかを制御しているのではないということである。現状のnodeでは、新たにウィンドウを開く際にnode機能をオンにして開くことができるが、allowpopupsを付与しないことによって新たにウィンドウの生成を禁止していることがたまたまnode機能付きでコード実行されるのを禁止しているのに過ぎないという点に留意しておく必要がある。

まとめ。webviewタグにallowpopups属性をつけてリモートサイトを表示している場合、リモートサイトから任意コードの実行が可能になる。

2016-01-09

[] iframe sandbox は万能ではない  iframe sandbox は万能ではないを含むブックマーク

HTML5で導入されたiframe要素のsandbox属性は、そのiframe内のコンテンツに対しJavaScriptの実行を始め様々な制約を課すことでセキュリティの向上に役立つ機能である。例えば、以下のように指定されたiframeでは、iframe内からformのsubmitなどはできるが、iframe内でのJavaScriptの実行やtarget=_blankなどによってウィンドウを開くことなどは禁止される。

<iframe sandbox="allow-forms" src="..."></iframe>

sandbox属性に明示的に allow-scripts という値を指定しない限りはiframe内では直接的にはJavaScriptは実行できないが、かといってiframe内から間接的にJavaScriptを必ずしも実行させることが不可能かというとそうでもない。

sandbox属性にallow-top-navigationがついている場合を考える。

<iframe sandbox="allow-top-navigation" src="sandboxed.html"></iframe>

sandboxed.html内に記述したJavaScriptはiframe内での実行は阻止されるが、トップレベルのwindowへの干渉は可能である。そのため、sandboxed.htmlに以下のような記述を行いユーザーにクリックを促す。

<a target="_top" href="data:text/html,<script>alert(1);</script>">click me</a>

iframe内には "click me" の文字列が表示され、クリックすることでトップレベルのコンテンツがdata:スキームで指定されたHTMLに置き換わる。このdata:スキームはiframe内ではなくトップレベルウィンドウ内で表示されるので、そこに含まれるJavaScriptは当然ながらsandbox属性の影響を受けずに実行される。

もっとも、一般的なWebブラウザであればdata:スキームはもとのWebサイトのオリジン(iframeの親やiframe内のオリジン)ではなくnullオリジンで実行されるために、直接的にWebサイトへ影響を与えることは通常はない。

問題はElectronでこのような記述をしている場合である。

このような方法でElectronアプリによるXSSの被害を低減させていたとしても、sandbox属性にallow-top-navigationが指定されている場合には攻撃者によって作成されたJavaScriptの実行へとつながる可能性があり、その場合にはオリジンが異なっていたとしてもnode.jsと同等のローカルホスト上での任意のコード実行になり得る。実際に、攻撃者がiframe内のsandboxed.htmlとして以下のようなHTMLを生成できた場合には攻撃者は任意コードの実行ができる。

<a target="_blank" href="data:text/html,&lt;script&gt;1&lt;/script&gt;&lt;webview nodeintegration src=&quot;data:text/html,&lt;b&gt;webview&lt;/b&gt;
&lt;script&gt;require('child_process').exec('calc.exe',function(){})&lt;/script&gt;&quot;&gt;&lt;/webview&gt;">click me</a>

なお、sandbox属性の値としてallow-top-navigationだけでなく、allow-poopupsも同様である。

まとめ:ElectronアプリでXSSの脅威を低減するためにiframe sandboxを用いる場合は、allow-top-navigationおよびallow-popupsは避ける。

2016-01-06

クロスサイトスクリプティング対策 ホンキのキホン  クロスサイトスクリプティング対策 ホンキのキホンを含むブックマーク

本稿はCodeZineに2015年12月28日に掲載された記事の再掲となります。

クロスサイトスクリプティング(XSS)は、古くから存在し開発者にもっともよく知られたセキュリティ上の問題のひとつでありながら、OWASP Top 10でも2010年に引き続き2013年でも3位と、未だに根絶できていない脆弱性です。

本記事では、Webアプリケーションの開発においてXSSを根絶するために必要な対策の基本を本気でお伝えします。

はじめに

OWASPでは開発者に向けたセキュリティ対策のためのドキュメントやチートシートを多数用意しており、XSSへの対策としても「XSS (Cross Site Scripting) Prevention Cheat Sheet」というドキュメントが用意されています。

ただし、このXSS Prevention Cheat Sheetはシンプルなルールを定めたチートシートであると謳いつつも、開発者が直面するあらゆる場面で包括的に適用できる抜けのないものを目指しているために、多岐に渡る条件のもとでの詳細なルールが定められており、残念ながら決して誰しもが簡単にすぐ適用できるものではないというのが現状です。

そこで、本記事ではより一般的で頻繁に遭遇する共通的な状況下に限定することで、XSSへの対策方法を簡潔に説明したいと思います。

本記事では物足りない方、本記事だけでは自身の開発しているWebアプリケーションへの対策として不十分だと感じた方はぜひXSS Prevention Cheat Sheetのほうにも目を通してみてください。

復習: そもそもXSSとは

XSSへの対策方法を解説する前に、改めてXSSとはどのような脆弱性であるのかを振り返っておきましょう。

例えば、http://shop.example.jp/search?item=OWASP というURLでアクセスすると、商品名に「OWASP」を含む商品の一覧を表示するショッピングサイトがあったとします。

f:id:hasegawayosuke:20160106222032p:image:w400

レスポンスとして返されるHTMLの一部には、URLのitemパラメータで指定された文字列「OWASP」が含まれています。

<div>
    <span>OWASP</span>に関する検索結果:20件
</div>

ここで、http://shop.example.jp/search?item=<s>OWASP</s>のように「OWASP」という文字列のかわりに「<s>OWASP</s>」という文字列を与えてアクセスした場合に、サーバ側がそのまま「<s>OWASP</s>」という文字列をHTML内に含めて返したとします。

<div>
    <span><s>OWASP</s></span>に関する検索結果:0件
</div>

f:id:hasegawayosuke:20160106222030p:image:w400

ブラウザ上に文字列として「<s>OWASP</s>」と表示させるには、HTML内には「&lt;s&gt;OWASP&lt;/s&gt;」と生成されなければいけないのですが、サーバ上でHTMLを生成する際に「<」や「>」をエスケープせずにそのまま出力しているために、ブラウザ内ではそのままHTMLタグとして取り扱われてしまいます。

このように、HTMLを生成する段階でのエスケープに漏れのある状態で、攻撃者が「<script>alert("hacked!")</script>」のような文字列を与え、そのときのURL http://shop.example.jp/search?item=<script>alert("hacked!")</script> *1をSNSなどを通じて多数の人に拡散したとします。

このURLにアクセスした一般の利用者は、ブラウザ上でいきなり表示される「hacked!」のメッセージに、このショッピングサイトが攻撃者に改ざんされてしまったのではないかと強い不安を感じてしまうでしょう。

f:id:hasegawayosuke:20160106222025p:image:w400

この例では攻撃者はalert関数によってメッセージを表示させるという単純で実質的には無害なことしか行っていませんが、攻撃者の作成したJavaScriptが利用者のブラウザ上で動くということは、より悪質な攻撃が行えるということを意味しています。

例えば、攻撃者はJavaScriptによってHTMLに自由にメッセージを書き込む見かけ上の改ざんを行うこともできますし、このショッピングサイトに利用者自身が登録している氏名や住所、メールアドレスなどを盗み見ることもできます。また、偽のクレジットカード番号入力画面を作成し、そこに入力されたカード番号を搾取することや、セッションCookieにhttponly属性が付与されていない場合にはdocument.cookieを通じて利用者のセッションを攻撃者がまるまる乗っ取ってしまうこともできます。

このように、HTMLを生成する際に文字列がエスケープされておらず、攻撃者によって作成されたHTMLタグやJavaScriptが利用者のブラウザ上で表示、実行される脆弱性をXSSと呼びます。

XSSの発生する原因

先ほどの例では、テキストとして表示されるべき部分(テキストノード)に攻撃者によって与えられた「<」や「>」がエスケープされずそのまま埋め込まれたためにHTMLのタグとして扱われることでXSSが発生しましたが、それ以外にもXSSが発生する原因としてよくあるのは、属性値へ出力する際に「"」や「'」がエスケープされていないというものです。

前述と同じく http://shop.example.jp/search?item=OWASP というURLにアクセスすることで「OWASP」を含む商品一覧を表示するショッピングサイトがあり、そこではレスポンスとして返されるHTMLの一部にinput要素のvalue属性の値としてURLのitemパラメータで指定された「OWASP」という文字列が含まれていたとします。

<form method="GET" action="/search">
    検索文字列: <input type="text" name="item" value="OWASP">
    <input type="submit" vaue="検索">
</form>

このときに、攻撃者が検索文字列として「" onmouseover="alert('hacked!')」という文字列を与えた場合に、サーバ側が以下のようなHTMLを返したとします。

<form method="GET" action="/search">
    検索文字列: <input type="text" name="item" value="" onmouseover="alert('hacked!')">
    <input type="submit" vaue="検索">
</form>

そうすると、input要素中ではvalue属性は空になり、onmouseover属性によってマウス移動時のイベントハンドラが設定されたHTMLとしてブラウザには解釈されます。

攻撃者によって誘導された利用者がブラウザでこのURLを開くと、onmouseoverイベントハンドラが有効であるためにinput要素の上でマウスを動かした瞬間にブラウザ内で攻撃者の作成したJavaScriptが動作してしまいます。

このように、テキストノードでは「<」「>」がエスケープされていないためにXSSが発生しましたが、属性値への出力では「"」(HTMLの書き方によっては「'」の場合もあり得ます)のエスケープがなされていないためにXSSが発生しました。

また、テキストノードと属性値以外にも、リンク先として設定されるURLにjavascript:スキームなどが埋め込まれた場合にもXSSが発生します。

例えば、自身のプロフィールの設定として任意のURLを登録できるSNSがあったとします。他の利用者がその人物のプロフィールページを表示させたときには、URLは自動的にリンクとしてa要素となるといったものです。

<div>
    <a href="https://www.owasp.org/index.php/Kansai">https://www.owasp.org/index.php/Kansai</a>
</div>

このような場合に、攻撃者が自身のプロフィールにURLとして「javascript:alert('hacked!')」といった文字列を設定した場合に、以下のようにそのままa要素のhref属性にこの文字列が設定されるとどうなるでしょうか。

<a href="javascript:alert('hacked!')">javascript:alert('hacked!')</a>

この攻撃者のプロフィールページを一般の利用者が閲覧し、そのときにリンク先のURLをクリックしてしまうと、攻撃者の作成したJavaScriptが利用者のブラウザ上で動作してしまいます。

このように、攻撃者が与えたURLによってリンクを生成する場合にjavascript:スキームなどを許容していると、エスケープ漏れの場合と同様にXSSが発生することになってしまいます。

XSSの対策

ここまで説明してきたとおり、XSSが発生する原因は以下の3種類が大部分を占めています。

  1. テキストノードに出力する際の「<」「>」のエスケープ漏れ
  2. 属性値に出力する際の「"」「'」のエスケープ漏れ
  3. URLをリンクとして取り扱う際のプロトコルスキームの確認漏れ

1.および2.はエスケープ漏れがXSSの原因であることは自明ですので、HTMLとして出力する際にこれらをエスケープします。エスケープすべき対象は以下の5文字です。

「&」は直接XSSの原因になるわけではありませんが、この文字をエスケープしなければユーザーから入力された「&」を正しく表示できませんので、エスケープが必要です。

< > " ' &

また、2.についてはエスケープに加えて、属性値を必ず「"」または「'」で囲むようにします。そうでなければ、"や'をエスケープしていたとしてもXSSの発生につながることがあるからです。多くのプログラミング言語やWebアプリケーション作成用のフレームワークでは、HTMLに出力するためにこれらの文字を適切にエスケープする関数があらかじめ用意されていることが多いでしょう。

PHPであれば htmlspecialchars 関数がそれにあたります。

<?php
    // GETで与えられたitemパラメータを取得
    $item = $_GET[ 'item' ];

    // 様々な処理
    ...
?>
<body>
    <!-- itemをエスケープしてHTML内のテキストノードに出力 -->
    <span><?php echo htmlspecialchars( $item, ENT_QUOTES, 'UTF-8' ); ?></span>
    ...

    <!-- itemをエスケープしてHTML内の属性値に出力 -->
    検索文字列: <input type="text" name="item" value="<?php echo htmlspecialchars( $item, ENT_QUOTES, 'UTF-8' ); ?>">
</body>

エスケープするのは、HTMLとして出力する直前です。外部から入力された文字列を受け取った直後にエスケープしてしまうと、その後その文字列を用いて様々な処理、例えば文字列の文字数に依存しているなどの処理を行う際に問題が発生しやすくなるからです。必ず、「HTMLとして出力する直前にエスケープ」を原則としましょう。

3.については、URLをリンクとして出力する際にはスキームがhttpあるいはhttpsであること、すなわちURLが「http://」または「https://」で始まることを確認するようにします。場合によっては「/foo」のように「/」で始まるURLを許容してもよいでしょう。*2

決して、javascript:スキームであればNG、のような確認を行わないようにしてください。XSSが発生するのはjavascript:スキームだけでなくvbscript:スキームを始め複数の種類があるため、「httpやhttpsのように安全なものだけを許可する」という方針のほうが堅実だからです。

例えば、PHPであれば以下のように正規表現を用いて文字列の先頭が「http://」「https://」あるいは「/」であることを確認し、そうである場合にのみa要素のhref属性にURLを設定することで、リンクによるXSSを防ぐことができます。

<?php
    function checkUrl( $url ){
        if( preg_match( "/\Ahttps?:\/\//", $url ) || preg_match( "/\A\//", $url ) ){
            return $url;
        }else{
            return "";
        }
    }
?>
...
<div>
    <a href="<?php echo htmlspecialchars( checkUrl( $url ), ENT_QUOTES, 'UTF-8' ); ?>">
        <?php echo htmlspecialchars( $url, ENT_QUOTES, 'UTF-8' ); ?></a>
</div>

ここでもa要素のhref属性の属性値として出力していますので、先ほど同様にhtmlspecialcharsによるエスケープを忘れてはいけません。

まとめ

ここまで解説したとおり、XSSの対策として有効な基本的なルールは以下のようなものになります。

  • テキストノードや属性値としてHTMLを出力する時点で「<」「>」「"」「'」「&」の5文字をエスケープする
  • 属性値は引用符で囲む
  • URLをリンクとして取り扱う場合にはhttpおよびhttpsスキームに限定する
これですべてのXSSが防げるわけではありませんが、現実に発生しているXSSの多くはこのルールを徹底することで防げるものが大半です。 本稿では、XSSの原理と対策の基本について解説を行いました。 一方、XSSは攻撃の種類や発生個所が多岐に渡るため、本稿だけではカバーできていない内容も多数あります。それらについては先に紹介したXSS (Cross Site Scripting) Prevention Cheat Sheetなどを活用してみてください。また、近年はJavaScriptによるクライアント側でのコード量が増えたことによって、JavaScript上で発生するDOM Based XSSと呼ばれる種類のXSSも増加しています。これについてはDOM based XSS Prevention Cheat Sheetなどを参考にしてみてください。 本稿がセキュアなWebアプリケーション構築の一助になれば幸いです。

*1:実際にはURLのitemパラメータはitem=%3Cscript%3E...のようになりますが、ここでは可読性を優先して便宜上URLエンコーディングを解いた状態で表記します。以降のURLに対する例も同様です。

*2:注2. 攻撃者の指定したURLをリダイレクト先として用いる場合には、リダイレクト先が許可されたドメインであるかを確認しオープンリダイレクタと呼ばれる脆弱性を防ぐ必要があります。オープンリダイレクタについての説明は本稿の範囲を超えますので今回は割愛します。

2015-12-25

[] Electronでアプリを書く場合は、気合いと根性でXSSを発生させないようにしなければならない。  Electronでアプリを書く場合は、気合いと根性でXSSを発生させないようにしなければならない。を含むブックマーク

そのうちもう少しきちんと書きますが、とりあえず時間がないので結論だけ書くと、タイトルが全てでElectronでアプリを書く場合は気合いと根性でXSSを発生させないようにしなければならない。

これまでWebアプリケーション上でXSSが存在したとしても、影響範囲はそのWebアプリケーションの中に留まるので、Webアプリケーションの提供側がそれを許容するのであればXSSの存在に目をつむることもできた。しかし、ElectronアプリでDOM-based XSSが一か所でも発生すると、(おそらく)確実に任意コード実行へとつながり、利用者のPCの(そのユーザー権限での)全機能が攻撃者によって利用できる。

そのため、Electronでアプリケーションを作成する開発者は気合いと根性でXSSを完全につぶさなければならない。

nodeIntegration:falseやContent-Security-Policyといった保護機構は残念ながら役に立たない。

  mainWindow = new BrowserWindow({width: 600, height: 400, webPreferences:{nodeIntegration:false}});

みたいにレンダラプロセスでnode機能を切り離し、さらにレンダラのHTMLで

<meta http-equiv="Content-Security-Policy" content="default-src 'self';">

ていたとしても、レンダラのHTML上に

<webview nodeintegration src="data:text/html,<script>require('child_process').exec('calc.exe',function(){})</script>"></webview>

のようなタグを注入されると、nodeの機能をフルに活用して任意のコードが実行可能となる。

現在、多くのElectron製アプリケーションで任意コード実行可能な脆弱性が存在していることを確認しています。