水無月ばけらのえび日記

bakera.jp > 水無月ばけらのえび日記 > 安全なWebアプリを作りたければ新しいフレームワークがオススメ

安全なWebアプリを作りたければ新しいフレームワークがオススメ

2011年11月30日(水曜日)

安全なWebアプリを作りたければ新しいフレームワークがオススメ

公開: 2011年12月10日21時20分頃

こんな記事が……なぜPHPアプリにセキュリティホールが多いのか? 第44回 セキュリティ対策が確実に実施されない2つの理由 (gihyo.jp)

例えば,Railsの入力のセキュリティ対策はセキュアであるとは言えません。Railsのバリデーションは「データベースにデータが保存される前」に行われます。データベースにデータを保存する必要がないようなアプリケーションの場合,入力のバリデーションをフレームワークとして行う仕組みになっていません。本来入力はデータベース利用の有無に関わらず入力を受け入れた直後に行うべきです。多くのフレームワークがRailsの影響を受け同様の仕様となっています。Railsが脆弱な仕様を採用したことは不幸なことだったと思います。

……。

まず、バリデーションはセキュリティのためにする処理ではありません。たまたまセキュリティの役に立つこともありますが、役に立たないこともあります。たとえば、問い合わせフォームに本文の入力欄があり、任意のテキストが入力できて、DBにはText型 (任意の長さの任意のテキスト) として保存するとましょう。この場合、空欄だとエラー、という程度のバリデーションしか行われないはずです。長すぎるデータを蹴る場合もありえるでしょうが、いずれにしてもセキュリティの役に立つ処理ではありません。

どんなバリデーションが行われるのかは、単に仕様によります。任意のテキストを許す必要があるなら、' (単引用符) だろうとでも < (小なり) だろうと入力させなければなりませんし、エラーにしてはいけません。セキュリティの観点から勝手なバリデーションをすると、そういった文字が入力できない残念なシステムになってしまいます。

逆の言い方をすると、バリデーションを行っても、それはインジェクション系の脆弱性への対応とはならないということです。数値のみを入力させる場合などは対策にもなり得るのですが、それはたまたまですので、バリデーションとは別途に考えなくてはなりません。題名の「セキュリティ対策が確実に実施されない理由」という観点で言うなら、バリデーションをセキュリティ施策の一環だと誤解している人がいる、ということが理由のひとつではあるでしょう。

それはさておき、なぜかRailsが叩かれているので、Railsの話を少し。

RailsにはActiveRecordというものがあります。これはDBのカラム名や型を見て、自動的にモデルクラスを作ってくれるというものです。逆に言うと、DBに保存しないデータはActiveRecordでは扱えません。

ActiveRecordのバリデーションの仕組みは優れているので、DBに保存しないデータも同じようにバリデーションしたい、というニーズはあります。ただし、これはセキュリティとはあまり関係なく、単に同じように扱いたいという動機であることがほとんどでしょう。古いRailsではこれが大変で、いろいろ頑張ってActiveRecord::Baseを拡張したクラスを独自に作ったり、かなり面倒なことをしていました。

そして、おそらくそう思った人が多かったからでしょう、Rails3ではActiveModelというクラスが追加されて、お望みのことが簡単にできるようになりました。古いRailsもべつに脆弱というわけではないと思いますが、DB由来でないデータもバリデーションしたい場合はRails3をオススメします。

※もし「バリデーションのタイミングが気に入らない、入力直後に常にやるべき」という話であれば、before_filterでvalid? メソッドを呼ぶようにでもすれば良いのではないでしょうか。特に意味はないと思いますが、やりたければ。

Viewヘルパーを利用すれば安全,と考えるのは危険です。Railsアプリケーションのソースコード検査も仕事でしていますが,Railsで開発している方でもViewヘルパーがすべてのパラメータをエスケープ処理してくれないことを知りません。これはRailsだけの問題でなくすべてのWebアプリケーションフレームワーク共通の問題だと思ってください。

「Viewヘルパーがすべてのパラメータをエスケープ処理してくれない」というのは、何を言われているのか良く分かりませんでした。Viewの中で変数を出力する際にHTMLタグなどがエスケープされない、という話をされているのでしょうか?

昔のRailsでは、Viewに <%= var %> のように書くと、値はそのまま出力されていました。エスケープしたい場合は <%=h var %> のように書く必要があります。つまり、ヘルパーメソッド h() を呼ぶ必要があります。

逆に言うと、h() さえ呼べば、ほとんどの場合は問題ありません。問題が起きるのは、属性値に引用符を付けていないとか、単引用符で括ろうとしているとか、JavaScriptのコードを出力しているとか、javascript:で始まるURLが入力できるとか、そういった限定的な状況だけです。通常は単に h() を呼べば良いだけです。ただ、h() を書き忘れることはあるので、そういう意味では注意が必要ではあるでしょう。

……というのは古いRailsの話で、Rails3では h() を呼ばなくてもデフォルトでエスケープされるようになりました。エスケープせずに出力したい場合は、明示的に <%=raw var %> と書く必要があります。というわけで、Rails3の方がXSSを避けやすくなっています。

Rails以外で言うと、ASP.NET4では <%: var %> のように書くとデフォルトでエスケープされます。以前、安全なテンプレートシステムはあるのかという話で「2種類のデータがどちらも同じ string 型なのがややこしさの元」と書きましたが、.NET4にはHtmlStringという型があって区別できるようになっています。エスケープなしで出力する場合は、HtmlString型の値を渡すとそのまま出力されます。

ASP.NET MVC3で使われるRazorも同様で、デフォルトではエスケープされ、HtmlString型を渡すとそのまま出力されます (HtmlStringのコンストラクタを呼ぶかわりに、「@Html.Raw(foo)」と書くこともできます)。

そんなわけで、新しめのフレームワークでは、Viewでの変数出力はエスケープされるのがデフォルトになってきています。結論としては、安全なWebアプリケーションを作りたければ、新しめのフレームワークを使うのがオススメ、ということで。

※ただ、PHPのフレームワークがどうなっているのかは良く知らないのですが。PHPに詳しい大垣さんが「すべてのWebアプリケーションフレームワーク共通の問題」と言われているということは、PHPのフレームワークはまだ駄目なのでしょうか……?

関連する話題: Web / セキュリティ / クロスサイトスクリプティング脆弱性

最近の日記

関わった本など