unshiuならびにrailsでコーディングする際にセキュリティ上気をつけることのまとめです。
セッション
原則としてセッションにのせる情報はユーザIDのみとします。またセッション情報はbase64でハッシュ化されているにすぎないため、クレジットカード情報、パスワード、その他機密情報に関してセッションに格納することは禁止とします。
保持期間について
クリティカルな個人情報(カード情報、銀行口座情報、職歴etc)を扱う場合基本は30分とします。それ以外でユーザIDのみしかセッションに保持しない場合は利便性を考慮し、最終ログインから2週間保持とします。ただし案件により要望がある場合は必要に応じて変更してください。
CSRF対策
デフォルトで scaffold した時点では対策されていないので注意してください。以下の点が設定されていることを確認してください。
config/environment.rb
config.action_controller.session_store = :active_record_store
config.action_controller.session = {
:session_key => '_mobilesns_session_id',
:secret => 'ランダムな30文字以上の英数字'
}app/controllers/application.rb
protect_from_forgery :secret => 'ランダムな30文字以上の英数字'
config.action_controller.session :secret の値と protect_from_forgery :secret の値は違うものを指定します。またこれらの値はアプリケーションごとに必ず違う値でかつ推測されない値を利用し、データベースのパスワードと同等のレベルの機密情報として扱います。そのため開発環境、テストサーバと本番サーバでも必ず値をかえることとします。
unshiu においての対策
これらの機密鍵は起動時に、なければランダムな英数字で作成され、以下のディレクトリに保存され再利用されます。
#{RAILS_ROOT}/public/system/fils/secret_key/XSS対策
ユーザが入力をした内容は原則として、サニタイジングをします。 validateで入力をはじいていたとしても、ソースコードの統一のため必ず表示の際にサニタイジングしてください。お知らせなど、運営者などが運営者の責任で管理画面から入力し、ユーザの画面に表示する内容に関しては、必要に応じて、サニタイジングを外すことを例外的に認めます。
実装方法
<%=h @user_name %>ファイル
アップロードしたファイル
ファイル名に ../ という文字列が含まれていた場合は必ず削除します。
filename.strip do |name|
name.gsub! /^.*(\\|\/)/, ''
name.gsub! /[^\w\.\-]/, '_'
endなお pluignなどを利用してる場合、必ずそのpluignが対応しているか確認して下さい。
ユーザへ閲覧させてはならないファイルについて
以下のようにサーバ上に存在はしているが、ユーザに見せてはならないファイルがあります。
- .svnファイル
- system以下のフォルダ
- log, アップロードされたファイルが置かれている
- images以下はアップロードされた画像があるため閲覧可能にしておかなければならない
これらはApacheの機能を使いブロックします。
<DirectoryMatch "^/.*/(\.svn|csv|system/files)/.*">
ErrorDocument 403 /404.html
Order allow,deny
Deny from all
Satisfy All
</DirectoryMatch>パスワード
ユーザの入力情報問題追跡の必要性もあるためは production 環境であってもログに出力されます。ただし、パスワードに関しては生の情報が記録されてはDB内で暗号化している意味がありません。
以下の設定は本番環境において必須です。
app/controllors/application.rbfilter_parameter_logging :password
またパスワード以外も機密情報に関しては同様の設定をします。
SQLインジェクション
以下のように変数をconditionsの中で展開するようなコードの書き方はSQLインジェクションの温床になるため禁止です。
find(:all, :conditions => ["name = #{params[name]}"])必ず、'?'パラメータ記法や、名前付きパラメータ記法をつかってください。
find(:all, :conditions => ["name = ?", params[name]])
find(:all, :conditions => ["name = :name", :name => params[name]])
なお例外的に速度的な問題や仕様的にどうしてもSQL文を自分で組み立てなければならない場合は以下のようにsanitize_sqlでサニタイズを必ず行います。
sql = sanitize_sql("select * blog where name = #{name}")ただし前提としてActiveRecordは優れたO/Rマッパーであるため、自前でSQLを組み立てなければならない事態は極わずかです。
アクセス権限処理フィルター
ブラックリスト方式でフィルターをかけると、メソッドを追加した際に権限チェックがもれてしまう場合があるため、ホワイトリスト方式でかけてください。この場合、もし万が一納品された場合「閲覧できない」というエラーになりますが、閲覧してはならないデータが閲覧できるよりは被害が少なく、ユーザの心理的にも安全性に不安を抱く結果にはなりにくくなります。
携帯識別IDによるログイン
利便性のために携帯識別IDのみを利用してログインすることがあるかと思いますが、リクエストヘッダーは簡単に書き換えられるためPCからのアクセスを許可していると、簡単になりすましログインが可能です。これを防ぐにはIPアドレスで携帯からのアクセスかどうかを判別するのが一番効果的ですが、キャリアのIPアドレスは定期的に変更されるため、更新を定期的にする必要があったり、 PCなどの確認することに問題が生じます。
クライアントがユーザの利便性と運用の手間を考慮してその制限をしたくないということは十分ありえますが、必ず携帯識別IDのみによるログインを許した場合の危険性を説明してください。説明した上でそれでもかまわないという場合のみ携帯識別IDでのログインをする仕様にしてください。
なお、IPアドレスの自動更新や特定のIPのみ(自社内やクライアント社内のみ)携帯閲覧を許すことも仕組み的には可能なので運用負荷だけが問題ならそれを提案することも可能です。