Railsのセキュリティ対策で調べた事

  • 217
    いいね
  • 1
    コメント
この記事は最終更新日から1年以上が経過しています。

SQLインジェクション

DB操作の際にSQLを自前で組み立てたりするとSQLインジェクションの可能性がある

# { name: "' or 1=1 --'" } 等が指定され場合にダメ
User.where("name='#{params[:name]}'")

# => placeholder使うようにする
User.where(name: params[:name])
User.where("name = ?", params[:name])

orderなどのメソッドにparamsそのまま渡すとだめ

# {order: ",8"}  Orderメソッドは列番号指定が可能
User.order("name #{params[:order]}")

# {id: 532, lock: "or 1=1"} lockは任意のSQLを指定できる
User.where(id: params[:id]).lock(parmas[:lock])

参考

http://rails-sqli.org/

対策

  • SQLに自分でパラメーターを埋め込まなず placeholder を使う
  • 入力された値をそのまま update_all や order や lock などのメソッドに渡さない

Unsafe Refrection

ユーザー入力の文字列を使ってObject.const_getすると任意のコードを実行される可能性がある。

# {class: "File", arg: "/etc/hosts"}
clazz = params[:class].constantize
obj = clazz.new(params[:arg])

参考

http://blog.conviso.com.br/2013/02/exploiting-unsafe-reflection-in.html

対策

  • White Listでconst_getする文字列をしぼる

シンボルがGCされない問題(DoS)

RubyではSymbolがGC対象にならないので、ユーザー入力をそのままSymbolにするのはDoS攻撃の対象になる可能性がある。

# DoSの可能性がある
params[:key].to_sym

# Railsでは以下の "hanako" はSymbolに変換されるので
User.where(name: { "hanako" => "tarou" }
# 以下はDoS攻撃の可能性がある(3.2.12以前)
User.where(name: params[:query])

対策

  • White List通すなどして気をつける。
  • Gemなどでの to_sym をしていないかチェックする
  • よく気をつける
  • 祈る(-人-)

クロスサイトスクリプティング(XSS)

デフォルトではHTMLエスケープされる。

エスケープ無しで出力するには以下のような方法がある。

<!-- rawメソッド -->
<%= raw "<b>hanako</b>" %>
<!-- '=='ヘルパー -->
<%== "<b>hanako</b>" %>
<!-- html_safe 下はalertが表示される -->
<p><%= "<script>alert('a');</script>".html_safe %></p>

html_safeは 安全な文字列なのでescapeしないでねというマーク

対策

  • 安全でない文字列をエスケープ無しで出力しない
  • ヘルパーでタグを出力する
"<em>#{text}</em>".html_safe
#=> ヘルパーを使う
content_tag :em, text

リダイレクション

redirect_to メソッドに意図しないパラメーターが渡される事によって、意図しないサイトに遷移してしまい、refelerなどが情報が漏洩してしまう。

# paramsの内容そのままに main アクションにとばす
redirect_to(params.update(action: 'main'))

対策

  • White Listなどで渡すパラメーターをしぼる

User Management

アクティベーション等のときにparamsの値でユーザーを特定しているとnilが渡された場合 IS NULL と条件式が入る

# {token: nil}
User.find_by_activation_code(params[:token])
# => SQL: SELECT * FROM users WHERE (users.activation_code IS NULL) LIMIT 1

Regular Expression

^$ は Ruby では行頭、行末という意味で ^[a-z]+$ とかは改行がRequestに含まれた場合に不十分になる

# {username: "hanako\nたろう"}
/^[a-z]+$/ =~ params[:username]

参考

http://guides.rubyonrails.org/security.html#regular-expressions

対策

  • ^$ を \A\z に変える
# {username: "hanako\nたろう"}
/^[a-z]+$/ =~ params[:username]
#=> ^$を\A\zにかえる
# {username: "hanako\nたろう"}
/\A[a-z]+\z/ =~ params[:username]

Privilege Escalation

ログインユーザーによりアクセスできる権限が設定されているリソースで、Model.find(params[:id])のみで取得してしまうと、権限を超えてアクセスされる可能性がある。

# 本来ログインされたユーザーにアクセス件がない Project にアクセス出来てしまう
@project = Project.find(params[:id])

対策

  • ログインされたユーザーでスコープをしぼる
@project = current_user.projects.find(params[:id])

Command Line Injection

ユーザー入力されたパラメーターをそのままsystemメソッド等にわたすと、任意のコマンドが実行される恐れがある

# {path: "&& rm *"}
system("/bin/echo #{params[:path]}")

対策

  • systemコマンドに引数を分けて渡す
system("/bin/echo", params[:path])
  • エスケープする
path = Shellwords.escape(params[:path])
system("/bin/echo #{path}")

アップロードファイル

アップロードファイルのファイル名を指定する時にサニタイズしないと重要なファイルを上書きされる恐れがある。
また同時にたくさんのユーザーからファイルアップロードされるとDoS攻撃の対象になる。

参考

http://guides.rubyonrails.org/security.html#file-uploads

対策

  • ファイル名だけ抜き出す(railsのセキュリティガイドより抜粋)
def sanitize_filename(filename)
  filename.strip.tap do |name|
    # NOTE: Unix上ではFile.basename で windowsのpathが渡された場合正常に動かない
    # ファイル名だけ抜き出す
    name.sub! /\A.*(\\|\/)/, ''
    # 英数字+'.'(ピリオド)+'-'(ハイフン)意外を '_'(アンダースコア)に置き換える
    name.gsub! /[^\w\.\-]/, '_'
  end
end

  • ファイルのアップロード処理は非同期でする

ダウンロードファイル

ファイル名をユーザー入力から受け取ると機密ファイルをダウンロードされる危険性がある

# {filename: "../../../etc/passwd"}
send_file('/var/www/uploads/' + params[:filename])

参考

http://guides.rubyonrails.org/security.html#file-downloads

対策

  • ファイルパスをチェックする
basename = File.expand_path(File.join(File.dirname(__FILE__), '../../files'))
filename = File.expand_path(File.join(basename, @file.public_filename))
raise if basename != File.expand_path(File.join(File.dirname(filename), '../../../'))
send_file filename, disposition: 'inline'

Loging Parameter

RailsログにRequestパラメーターが残るがセンシティブな情報はフィルターする必要がある

config/application.rb
config.filter_parameters += [:password]

セッションハイジャッキング

盗聴などの手段によってCookieに保存されているsession_idを盗まれた場合に成り済まされる可能性がある。

対策

  • config.force_ssl = trueenvirontments/*.rb で指定し httpsを強制する。

セッション固定化攻撃 (Session Fixatin)

以下の様な手順でsession_idを固定し、なりますましを行う

  1. 攻撃者がサイトにログインし有効なsession_idを取得する
  2. XSSなどの手段で一般ユーザーのsession_idクッキーを固定しログイン画面を表示する
  3. 一般ユーザーがログインする 
  4. 攻撃者は固定化されたsession_idでサイトにアクセスする

参考

http://guides.rubyonrails.org/security.html#session-fixation

対策

ログイン後にsession_idをクリアする。

# メジャーなGemは対応ずみらしい。
# データも全部消えるので、引き継ぐ必要がある場合は自分でやる必要がある。
reset_session

クロスサイトリクエストフォージェリ(CSRF)

以下のような手順で、利用者の意図しないリクエストを送る事ができる

  1. 利用者が対象サイトでログイン状態ある
  2. 他サイトで画像 <img src="http://対象サイト/project/1/destroy"> などに埋め込まれた罠をふんでしまう
  3. 利用者が対象サイトの有効なsession_idを伴って対象サイトにリクエストしてしまう

参考

http://guides.rubyonrails.org/security.html#session-fixation

対策

  • GETで安全でない(変更が伴う)リクエストを実装しない。
  • POSTリクエストにTokenを埋め込む(Railsがデフォルトで埋め込んでいる)