2018/08/13)Railsアプリケーションを作る機会も多くなったと思います。今までPHPのみを使ってきた方の為に、開発者がよく落ちてしまうRails/Rubyの落とし穴を少しだけ紹介します。RailsからWebアプリをはじめる方にも役立つと思います。関連to_iやto_fは型変換ではない私のブログ読者はPHP開発者の方が多いと思うので基本的なところから紹介します。RubyはPHPよりデータ型に厳格です。””(空文字列)や0は偽(false)と評価されません。false基本的にデータ型が一致しなければ、真とは評価されません。数値も同じで数値文字列と数値型(整数と浮動小数点)もデータ型が一致しなければ真と評価されません。そこで型を”一時的”に変えるto_i(整数型)、to_f(浮動小数点型)があります。false文字列の”123′′と整数の123は比較するとfalseと評価されます。Webアプリケーションは基本的に全てテキストなのでこれでは困ります。to_iを使えば、文字列を整数として評価できます。trueこれで目出度し、目出度し、となれば良いのですがto_iは型変換ではありません。一時的に型を変えているダケです。元のデータはそのまま残っています。"123"“123”と表示され文字列となっている事が分かります。<script>alert(1)</script>"この様に元のデータはそのまま残っているので型変換と勘違いして、そのまま出力してしまうと脆弱性の原因になってしまいます。123違う変数に保存すればデータ型も当然変わります。to_iやto_fをしたから数値型のデータのハズ!と思っていたら間違えてしまうので危険です。to_iやto_sは型変換ではない、と理解しておきましょう。無駄なto_sは要注意です。abc"元々文字列型のデータにto_sしても、当然と言えば当然ですが、問題なく実行されます。ライブラリの中などでこれがあると、数値型を文字列型に変換した上で連結ように見えます。見ての通り文字列部分は、当然ですが、残ったままです。文字列連結した上でhtml_safeを設定してしまうと脆弱性の発見が困難になります。"123"としてしまう事も可能です。nilのようなコードで確認できます。abc”は不正なので両方共nilになっていることが上記の例から分かります。数値として正しい値の場合、もちろん数値になります。nil後、知っておくべきはRubyでは0、””などfalseやnilで無いものは常に真である事ですが、これはプログラムを作れば嫌でも分かるので間違える事はほぼ無いと思います。文字列データの開始と終端の正規表現は^,$ではないPHPのpreg_*()もmb_regex_*()も文字列データの開始と終端はそれぞれ^と$です。同じような動作をする正規表現をRubyで書く場合、開始と終端はそれぞれ\Aと\zになります。正規表現を使ったバリデーションをする場合、^と$を使ってしまうとPHPのバイナリセーフでないPOSIX互換正規表現(ereg関数)を使った時と同じ脆弱性を作ってしまいます。詳しくはマニュアルを参照)Rubyの正規表現では\Aと\zが文字列型に保存された先端と終端であることを解説していないページも多いようです。(将来の為の備考:少なくともこのエントリを執筆した時点で解説がないです)なかなか分かりづらい落とし穴かも知れません。Railsセキュリティガイドには明確にセキュリティリスクであると解説してあります。プロトコルであることをチェックするバリデーションコードは無力です。@user.homepageのようなコードでJavaScriptを実行されてしまいます。バリデーションするにはRuby/\Ahttps?:\/\/[^\n]+\z/i012/\Ahttps?:\/\/[^\n]+\z/iのように書きます。設定で緩和することも可能ですが、Rubyのコードを書く場合には文字列型データの先端と終端にマッチさせる場合は常に\Aと\zを利用すると良いでしょう。PHPのmb_regex()はRubyと同じOnigurumaライブラリを使っている、と知っている方も多いと思います。同じOniguramaライブラリなのでPHPと同じ、と思って勘違いしているケースが多くあります。\Z(\zでなく\Z)に置き換えられています。(”\Zもあります”は伏線だったのですが追記で書いてしまいます)pcreの方も同様になっています、$では最後の改行にはマッチします。検索対象の終わりあるいは終端の改行文字の前http://jp1.php.net/manual/ja/regexp.reference.meta.phpこれが困る場合もあります。改行インジェクションはHTTPヘッダーインジェクションやメールヘッダーインジェクション脆弱性の原因になります。\A検索対象文字列の始端(複数行モードとは独立)\Z検索対象文字列の終端、または終端の改行(複数行モードとは独立)\z検索対象文字列の終端(複数行モードとは独立)http://jp1.php.net/manual/ja/regexp.reference.escape.phpということで、入力バリデーションに正規表現を正しく使わないと意図しない問題の原因になります。基本的に正規表現の必要性がないものは文字列関数でバリデーションすると良いです。\zを使っていれば問題ありません。こんな仕様なのか書こうかと思いましたが、なんだがRuby/Rail用のエントリの追記でPHPの事を書きすぎたので、バリデーションのところまで来たらまた詳しく紹介します。erbは万能ではないPHPのテンプレートでもデフォルトでHTMLエスケープしてくれる物も多いので、これはRailsだけでなく全てのWebアプリケーションに共通と言えます。デフォルトでエスケープしてくれることは安全性向上に非常に役に立ちます。デフォルトでHTMLエスケープする仕様は万能ではありません。>というコードはHTML5などでは有効なHTMLを生成します。(HTML5ではクオート無し属性値もOK)これは非常に危険です。onMouseOver=alert(1)ならHTMLエスケープは無力です。こんなミスはほとんどしない、と思うかも知れません。JavaScriptコンテクストの中では似たようなミス、数値だという前提でクオートなし、は結構見かけます。当然ですがJavaScript文字列を出力する場合、JavaScript文字列用のエスケープが必要です。(参考:JavaScript文字列のエスケープ)タグ属性値を直接書く場合、必ずクオートで囲んで書くようにすると良いです。%>で出力しても何の効果もありません。この場合、確実にバリデーションするしかありません。プリペアドステートメントを利用している場合で、識別子エスケープが利用できない場合のSQLインジェクション対策に似ていますね。(参考:SQL識別子のエスケープ)標準のHTMLヘルパーの属性名にも同様な注意が必要です。タグの属性名には普通シンボル(文字列定数のような物をイメージしてください)を使います。シンボルを使っていればJavaScriptインジェクションに注意する必要がありません。属性名は”文字列”でも構いません。(最新のコードは確認していませんが、少なくとも許可されている)属性名がユーザー由来の”文字列”である場合、JavaScriptインジェクションに脆弱になります。私の知る限りではマニュアルには記載がないようです。マニュアルに記載がないからといって安全とは限りません。セキュリティ的に重要な部分の仕様はコードで確認すると良いでしょう。HTMLコンテクストからJavaScriptコンテクストの文字列(<script>タグの中、onclick属性値など)へのエスケープはHTMLエスケープでも役立つ事もありますが、HTMLパーサーからJavaScriptパーサーにデータが渡された時点でも安全であることを保証しなければなりません。JavaScriptコンテクストへの出力などはJavaScript文字列のエスケープで紹介しているように、HTMLでもJavaScriptでも安全に処理されるエスケープ方法でエスケープした文字列を使うと良いです。ヘルパーを使っていれば適切に処理してくれる物もありますが、URLエンコードが必要な場所にはURLエンコードが必要です。erbはコンテクストを認識した自動エスケープをサポートしているテンプレートシステムではありません。ほとんどのPHPのテンプレートシステムと同様にコンテクストに応じたエスケープが必要です。ヘルパーAPIの実装・仕様にも注意してください。register_globals=OnのようなモデルRailsのモデルはPHPerお馴染み(?)のregister_globals=Onのような脆弱性があります。Assignemnt)という名前の脆弱性として知られています。GitHubもマスアサイメントに脆弱だったのでご存知の方も多いと思います。Parametersによるホワイトリスティングが可能なので利用しましょう。モデルでのバリデーションPHPフレームワークの場合、入力バリデーションをコントローラーで行えるような仕様・仕組みになっている物も多いです。Railsはモデルでバリデーションするようになっています。分からないけどRailsが何となく上手く処理してくれているのだろう、と思っていると全くバリデーションや必要なエスケープ処理がないコードを書いてしまうケースがあります。何となく上手く処理してくれるような事はない、と考えましょう。インターネットに載っている例も疑うべきです。StackOverflowのLDAPの例にはFilterのエスケープ、DNのエスケープが全くない例が載っていたりします。APIを使っていれば大丈夫、と勘違いしているのかも知れません(?)紹介した例を安易にモデルに書いたりすると問題になります。RubyプログラマはPHPプログラマよりスキルが高い人が多いから、スニペットやライブラリも安全!と思っていると落とし穴に落ちてしまいます。本格的にプログラムを作っていないWebデザイナ系の方も多いPHPとは多少状況が異なりますが、幅広い開発者によって支えられている現在のRuby/Railsはそう変わらない、と思って使っていた方が安全です。入力バリデーションは境界防御となるコントローラーでのバリデーションをお奨めしますが、モデルでのバリデーションでも十分という場合にはRails風にモデルでバリデーションでも構いません。1対1でコントローラーとモデルがマッピングされている、シンプルなアプリケーションなら大きな違いはありません。バリデーションを自動で行なってくれる物はあまりありません。基本的に全て自分がバリデーションしなければならない、と考えてください。APIやライブラリを使う場合にも、エスケープが必要なのか?バリデーションが必要なのか?と確認してから使うと良いでしょう。(参考:エスケープファースト、この順序は変えられない)まとめまだまだ色々あるのですが、Ruby/Railsの事を書くより、PHPの事を書かなければならないので今日の所はこの辺りで終わりにしておきます。Ruby/Railsの事も書いて欲しい!という要望が多いようであれば、PHP本が一段落したら書きたいと思います。Ruby/Railsで開発している方はこのブログを見ていないと思うので、ニーズは多くないかな?と思っています。Guideを理解してから開発しましょう。Railsは自動的に安全なアプリケーションを作ってくれるプラットフォームではありません。Railsアプリの書き方は異なりますが、PHPフレームワークを使ったアプリとあまり変わらない、と思って使った方が良いです。2.1のscrub良いですね。基本的には壊れた文字エンコーディングのデータを処理するべきではありません(バリデーションするか例外で処理中止すべきです)が、どうしても処理する必要がある場合もあります。エンコーディング変換で文字列として不正なバイト列を除去する方法でも構わないのですが、APIがある事は重要だと思います。紹介エレクトロニック・サービス・イニシアチブのWebシステムのコンサルタントのブログです。PHPの開発、PHP4.x/5.xレガシーPHPセキュリティパッチサービスなどを行っています。ブログのサブタイトルを「書かない日記」としているのは、ブログを始めた時にあまり書かないつもりで始めたのでこのサブタイトルになっています。yohgaki@ohgaki.net私が記述したブログ中のコードは記載が無い場合はMITライセンスです。その他のコードは参考リンクなどのライセンス情報を参照ください。