はじめに
自分で一からWebアプリケーションを作成してみたくてRailsチュートリアルを初めてみました。Railsチュートリアルを進めていく過程で色々な知識を得ることができたので自分の知識の整理も兼ねて今回はセッション
やクッキー
について纏めてみました。以下、Railsチュートリアルを元にしてセッションとかクッキーとかの説明です。都度更新予定。
Railsチュートリアル
対象:Rails 5.1 (第4版) 8章~9章
セッションとクッキー
セッションとは
ステートレス(状態を維持できない)なhttpにおいて、ユーザの状態(ログイン済みとか)をアプリ側で管理できるようにするための仕組み
セッションIDとは
- ユーザの状態(ログイン状態など)を識別するために、WebサーバやWebアプリケーションによって付与されるユーザ識別情報。Railsチュートリアルではログイン状態を管理するために暗号化されたユーザIDがセッションIDとして使われている
クッキーとは
セッションの種類と詳細
セッションには一時セッション
と永続セッション
があります
一時セッションとは(8章)
- ブラウザを閉じると消去されるデータ領域(一時クッキーともいうべきもの)にセッションID(暗号化されたユーザID)をセットするRailsのセッションの仕組み
- よって、ブラウザを閉じると対象サイトのクッキーは削除されます
- Railsチュートリアルでは暗号化されたユーザーIDをセッションIDとして一時セッションの仕組みを使い、ブラウザの一時クッキーに保存します
一時セッションの取り出し
- クライアントからアクセスが来た時は
session[パラメータ名]
を使ってユーザーIDを元通りに取り出すことができる。内部で暗号化に使用した秘密鍵みたいなものを使用して復号化と暗号化をしているみたい。 - 一時セッションの場合だと単純に一時クッキーから暗号化されたユーザIDを復号して取り出すのみ。それをDBにユーザ検索をかけてログイン状態を判断している。
8章の一時セッションの説明で気になった箇所
以下の文章が、むむ?となりました。
そしてここが重要なのですが、攻撃者がたとえこの情報を
cookiesから盗み出すことができたとしても、
それを使って本物のユーザーとしてログインすることはできないのです。
自分なりの解釈を示すと
一時セッションでもクッキーの値が盗まれたら本物のユーザとしてログインできます(なりますまし)。恐らく永続セッションと比べてクッキーの有効期限が短いので安全だという趣旨だと思います。
永続セッションとは(9章)
- ブラウザを閉じても消去されないデータ領域(永続クッキーともいうべきもの)にセッションIDをセットするRailsのセッションの仕組み
- 一時セッションよりも長い期間ブラウザに保存される。有効期限はデフォルトで20年。引数で指定できる。
- 一時セッションよりも長く存在するので
セキュリティを強固
にしないといけないというRailsチュートリアルの流れ
永続セッションのセキュリティ
- 一時セッションではクッキーに暗号化されたユーザIDのみ保存。サーバ側ではその他の処理なしなのでセッションによるログイン状態管理は暗号化されたユーザIDをクッキーから取り出すのみ。
- 永続セッションではサーバ側でトークンのハッシュをクッキーに保存している。DBにもそのトークンのハッシュも保存。このハッシュがセキュリティ的に意味がある。
ハッシュをどう使うのか
セッションID(暗号化されたユーザID)を復号してDB検索までは一時セッションと同じで、その後に、永続クッキーに保存されたトークンのハッシュとDB内のハッシュ値を比較して一致することを確認する。トークンは永続セッションを使う度に異なる値が生成されます。
どういうこと?
つまり、一時セッションにはなかった、ログイン状態の判断をサーバ側にもたせるということなんだと思います。いつでもDBのハッシュを消すことができる(攻撃者のログインをできなくする) ことになるんだと思います。
railsチュートリアル9章より
署名されたユーザーIDがあれば記憶トークンは不要なのではないかと
疑問に思う方もいるかもしれません。
しかし記憶トークンがなければ、暗号化されたIDを奪った攻撃者は、
暗号化IDをそのまま使ってお構いなしにログインしてしまうでしょう。
現在の設計では、攻撃者が仮に両方のcookiesを奪い取ることに成功
したとしても、本物のユーザーがログアウトするとログインできない
ようになっています。
これは永続セッションの場合は本物のユーザがログアウトするとDBのハッシュが削除されるので攻撃者が仮にクッキーを奪っても攻撃者はログインできなくなるということです。これが目的で記憶トークンを追加しているんだと思います。
これについてはなるほどなぁ、と思いました。Webアプリケーション側でのセキュリティ対策(XSSやCSRFへの対応やセッションIDに重要情報を保持しない)(後述)+サーバ側でもログイン状態の判定を持つ
ことでよりセキュリティ的に強固になるのでないでしょうか。
但し一時セッションはブラウザを閉じるとセッションが消滅するということでこの仕組は使われてないですが、一時セッションでもこの仕組は使ったほうがよいのではないかと思いました。
セッションのセキュリティについて(Railsチュートリアル)
セッションのセッションで気をつけなきゃいけないこと
- セッションIDの推測
- セッションIDの盗み出し
- セッションIDの強制
Webアプリケーションでセッションを使用する場合、上記3つについて考慮しなくてはいけません。詳細な攻撃方法は別途参照して下さい。
Railsチュートリアルはどうなのか
- セッションIDの推測
- Railsチュートリアルでは暗号化されたユーザIDをセッションIDとして使用しています。が、暗号化されているので推測不能という認識でいます
- セッションIDの盗み出し
- Railsチュートリアルで作成するWebアプリケーションの機能に対してのXSSやCSRFへの対応はRailsではデフォルトでやってくれます。
- セッションIDをURLに埋め込んでいません
- webアプリケーションの対策とは外れますが、サーバではSSL化してネットワークでの盗聴を防ぎましょう
- セッションIDの強制
- いわゆるセッション固定攻撃に対しては、ログインする度にセッションIDを変更しています
- セッションIDに重要情報を保持しないことでセッション再生攻撃を防ぎます
セッションを保存する場所はどうするか
デフォルト
Railsではデフォルトでクッキーに保存します。Railsチュートリアルでもクッキーに保存しています
どこがよいのだろう
ネットで色々調べるとDBやKVSに保存するのがいいのではないかーという趣旨の記事がありました。が、DBやKVSに保存してもクッキーに整理番号のようなものはクッキーに保存するみたいなので(使ったことはない)、結局クッキーが漏洩するとどこに保存しようがどうしようもないのかなと想像しました。よってクッキーに情報を保存しても問題ないのかなぁという考えです。
しかし、永続セッションであった様にハッシュをDBに保存する仕組みは必要かと思いました。というのは、DBでハッシュを削除すればログイン状態にはなりません。要するにクライアントのクッキーはWebアプリケーション側でいつでも削除するわけにはいきませんが、この方法だとハッシュを削除すればログイン入力を強制&不当に取得したクッキーから攻撃者のログインを防げるからです。
ログインについて
上記より、ログインはセッションの仕組みを使って実現しています。ソースコードレベルではどうなっているかRialsチュートリアルを元に示します。
ログインするとは?
Railsのセッションの仕組みを使ってクライアントのブラウザにあるクッキーにセッションID(ユーザ識別情報、暗号化されたユーザID)を挿入すること
実装方法
Railsチュートリアルでのログイン実装。セッションの仕組みを使ってクッキーに保存している。デフォルトでは保存先はクッキーだがDBやKVSなどにも変更できる。
# ログイン(一時セッション)
def log_in(user)
session[:user_id] = user.id
end
# ログイン(永続セッション)
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
ログイン済みとは
Railsチュートリアルではクライアントからアクセス→view表示するときに、クライアントの情報(クッキー)に自サイトのセッションID(ユーザ識別情報)があり、システムにそのユーザが存在すればログイン済みと判断している
ログアウトについて
ログイアウトするとは?
ログインするときの逆で、セッションの仕組みを使ってクライアントのクッキーを削除&永続セッションの場合、ハッシュ値を削除する
実装方法
# ログアウト #####################################
# 永続的セッションを破棄する
def forget(user)
user.forget #ハッシュ値を削除
cookies.delete(:user_id) #クッキーを削除
cookies.delete(:remember_token) #クッキーを削除
end
def log_out
# 永続セッションの削除
forget(current_user)
# 一時セッションの削除
session.delete(:user_id)
@current_user = nil
end
##################################################
補足
クッキー
- set-cookieヘッダフィールド
- サーバからブラウザのクッキーに保存してほしいデータ
(属性名=値;)
が入ったフィールド
- サーバからブラウザのクッキーに保存してほしいデータ
- Domain属性
- Cookieの共有範囲。ドメインを指定する。
- Secure属性
- ブラウザはHTTPS通信でのみサーバーにCookieを送信する
セッションIDの漏洩
セッションIDが暗号化されていても、セッションIDが第三者に知られれば、セッション乗っ取りが生じるおそれがあります。
対策
- SSLを使う
- SSLはセッション層のプロトコルなのでその上位のアプリケーション層のhttpはヘッダもボディも暗号化されるので盗聴はできない
- Cookieにsecure属性をつけてhttps時のみクッキーを使用する
まとめ
- SSL通信とCookieにsecure属性をつけていれば盗聴の危険はない
- ただし、SSLとsecure属性をつけてもやり方によってはセッションIDの改変はできる。そこでCookieを攻撃経路とする脆弱性(固定攻撃、XSSなど)に対応したすれば、盗聴できないし、強制、改変されてもセキュリティ的に問題ない
要するにセッションに関する脆弱性にはすべて対応しましょう
参考
まとめ
セッションやクッキーはWebアプリケーションに状態を生み出すWebの根幹の仕組みだと思います。Railsチュートリアルを通してこの大事な技術要素を自分なりに理解することができました。Railsチュートリアルをまだやってみたことがない人は一度やってみると面白い発見がたくさんあるではないでしょうか。今度は他の章についても纏めてみようと思います。