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


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

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

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

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

```ruby:
# {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`すると任意のコードを実行される可能性がある。

```ruby:
# {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攻撃の対象になる可能性がある。

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

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

```

### 対策
* rubyのバージョンを2.2以上に上げる
    * 2.2以上であればシンボルもGCの対象になったため
    * see => [(翻訳) Ruby 2.2 のシンボル GC - FIVETEESIXONE](https://fiveteesixone.lackland.io/2015/01/21/symbol-gc-ruby-2-2/)
* White List通すなどして気をつける。
* Gemなどでの to_sym をしていないかチェックする
* **よく**気をつける
* 祈る(-人-)

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

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

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

```erb:
<!-- rawメソッド -->
<%= raw "<b>hanako</b>" %>
<!-- '=='ヘルパー -->
<%== "<b>hanako</b>" %>
<!-- html_safe 下はalertが表示される -->
<p><%= "<script>alert('a');</script>".html_safe %></p>
```
html_safeは **安全な文字列なのでescapeしないでねというマーク**

### 対策
* 安全でない文字列をエスケープ無しで出力しない
* ヘルパーでタグを出力する

```ruby:
"<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` と条件式が入る

```ruby:
# {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])`のみで取得してしまうと、権限を超えてアクセスされる可能性がある。

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

### 対策
* ログインされたユーザーでスコープをしぼる

```ruby:
@project = current_user.projects.find(params[:id])
```

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

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

## 対策
* systemコマンドに引数を分けて渡す

```ruby:
system("/bin/echo", params[:path])
```

* エスケープする

```ruby:
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

```

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

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

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

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

### 対策
* ファイルパスをチェックする

```ruby:
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 = true` を `environtments/*.rb` で指定し　httpsを強制する。


## セッション固定化攻撃 (Session Fixatin)
以下の様な手順でsession_idを固定し、なりますましを行う

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


### 参考

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

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

```ruby:
# メジャーな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がデフォルトで埋め込んでいる)