QA@IT

Web アプリケーションの認証とセキュリティに関して教えて下さい

9282 PV

こんばんは。Web アプリケーションの認証とセキュリティに関して教えて下さい。

Rails のセッション管理

Rails は RESTful な Web アプリケーションの構築を可能にするとは言っていますが、実態としてはサーバーサイドに保持し、セッション ID は Cookie に保存しているものだと思っています(Rails の経験が殆ど無いので間違っていたらすみません)。

で、最近こちらの記事を読みました。
Rails で (Rack の) セッション情報を Cookie に保存する仕組み

この記事によると、セッション情報は確かに Cookie に保存されています。保存されているのは

  • セッション ID
  • CSRF トークン
  • 秘密鍵を用いて上記を暗号化したハッシュ値

で、最後のハッシュ値によりセッション ID の漏出を防いでいると理解しました。同時に、トークンを保持することで CSRF 対策も行われています。

質問

ここで幾つか教えて頂きたいことがあります。

  1. 確かに上記の対策によりセッション ID が推測されたり乗っ取られたりすることは無さそうですが、そうは言っても Cookie の値は HTTP リクエストに乗って送信されます。仮にこのリクエストを覗き見されてしまうとそのままセッションの乗っ取りが可能ですよね?これは一般的にどのように対策するのでしょうか?例えば「はてな」ではログイン画面は https で通信していますが、それ以外の画面は普通の http です。とはいえ「どのユーザーでログインしているか」という情報はやはり Cookie に保持しているのではないかと思い、そうすると Cookie が覗き見されてしまうのでは?と思っていますのですが・・・。

  2. 上記の「はてな」の例で言うと、ログイン画面が https 通信なのは、パスワードを平文で送信するのを防ぐためという理解で良いでしょうか?

  3. インターネット上に公開している Web アプリケーションの場合、「はてな」のようにログイン画面だけ https にするのが普通だと思うのですが、例えば業務アプリケーションのようにプライベートネットワーク内でしか動かない Web アプリケーションの場合、上記のようなセキュリティに関してはどこまで考えるべきなのでしょうか?そもそもログイン画面を https にしたりする必要は無いのでしょうか?それとも、業務アプリケーションだけに万全を期して全ページ https にするなどが普通なのでしょうか?もちろん、クライアントのセキュリティ要件にもよると思うのですが、一般的にはどのようなケースが多いのでしょうか?

  4. Rails は Cookie およびサーバーサイドにセッション情報を保存していますが、REST 本来の意味ではこれは NG だと思っています。とはいえ実態としてはこれ以外にあまり良い方法も思いつきません。サーバーサイドはセッション情報を DB や KVS に保存するなどの方法があるかと思いますが、少なくとも Cookie は使うことになるのかなと思っています。Cookie を使わない方法もあるのでしょうか?Web アプリで BASIC 認証や Digest 認証は使えませんよね?

何だか長くなってしまい恐縮ですが、分かる範囲で教えて頂けると幸いです。宜しくお願い致します。


moro さんの回答 に対する再質問

丁寧な回答ありがとうございます。
幾つか追加で教えて頂いて宜しいでしょうか。

はじめに

こちらは誤解で、Railsは(デフォルトでは)セッション情報を すべて Cookieに入れます。ですからたとえばRailsのセッション情報はシリアライズした上で64KBまでしか入れられませんが、それはそういうものだから設計を見なおすべき、というのがRails wayであるとされています。

おや、そうなんですね?

良く Rails で session[:user_id] みたいなコードを見るのでサーバーサイドにセッションを保持しているのかと思っていました。このセッションストアが KVS だったり ActiveRecord だったりするのかなと思っていたのですが・・・。DB にセッション情報を永続化して都度取得するようにすると重くなってしまうので、インメモリもしくは KVS あたりにセッション情報を保存するのかなと思っていました。

全て Cookie に保存する場合、ショッピングカートなどを実現しようと思ったらきっとサーバー側の永続化層にセッション情報を持たざるを得ない気がしますが(間違っていたらご指摘ください)、例えば良くあるユーザーのロール/権限などの情報はどうするのでしょうか?これも Cookie に保存するのが一般的なのでしょうか?また、サーバー側で「現在アクセス中のユーザー」を取得せざるを得ないケースが多いかと思うのですが、session id と user の紐づけはどのように行うのでしょうか?user id を Cookie に保存するのはマズいですよね・・・?

1

うーんなるほど。やっぱり Cookie が盗聴されてしまった場合はどうしようもないのですね。http であろうと https であろうと、そのまま Cookie を利用されたらなりすまし可能であると。素の http 通信を覗かれるとどうしようもないですね。公共無線 LAN が危険というのはこういうところなんですねぇ。

Rails Casts のページ、教えて頂いてありがとうございます。ただ、これって https の場合になりすましを防ぐ方法になっていますが、そもそも https でなりすまし(というか Cookie の盗聴)されるケースってあるのでしょうか……?この対応はやや過剰に思えますが、念のためということなのでしょうか。どれほど一般的なのかなと疑問に思いました。

2, 3

納得しました。業務アプリではやはり https が基本ですよね。ありがとうございます。
上の Rails Casts の対応もそうですが、実際プライベートネットワークで動くアプリケーションでどこまでセキュリティ対策を施すべきなのかがいまひとつ分からないのですが、上記のセッションハイジャック等も意識した対策を行うのが普通なのでしょうか?

4

サーバサイドに保存しているリソースであるところのユーザのIDが入っていて、ユーザの実体はサーバサイドにあるじゃないかといわれるとそうなんですが、それがREST的にNGだとは思いませんがいかがでしょうか。

そうですね。私はサーバーのメモリ上にセッション情報を持っているものだと考えてこのような発言をしたのですが、Cookie もしくは永続化層にあるのであれば REST 的なのかなとは思いました。個人的には、KVS や RDB などの永続化層にステートを持つのは良いと思うのですが、アプリサーバのメモリにステートを持ってしまうのは REST 的に NG だと思っています。

BASIC認証やDigest認証も使えます。けっこう簡単です。あのあまり見栄えの良くないブラウザダイアログを許容するのであれば、ごく少数のユーザ向け認証はhttps+BASIC認証でやったりもします。session[:user_id]的な識別子が欲しい場合request.env['Authorization']を適宜パースすればよさそうです。

なるほど。まぁ Web アプリとして提供する場合はやはりあのダイアログは駄目ってことになりそうですが使いどころはあるんですね。

もしも64KB以上のデータをセッションに入れたい場合は、セッション情報をDBに入れてCookieにはセッションIDだけを保存することも可能です。

こちらは上述の通り、権限やロールぐらいの情報だとどうするのかなーという点が気になりました。


moro さんの再回答に関して

再度の丁寧な回答、ありがとうございました。

なるほど、であれば普通に考えると

  • Cookie にはセッション ID を保存
  • サーバではセッションのハッシュを保持し、Cookie のセッション ID から現在のセッションを特定
  • セッションにはユーザー ID を保持しておき、これを利用して DB からユーザー情報を取得

という感じになりますね。

  1. の件は勘違いしていました。http と https が混在する状態で、http 側で Cookie が盗まれた場合の心配をしていたのですね。
  2. に関しても理解しました。

丁寧に教えて頂き大変助かりました。ありがとうございます。

回答


編集 履歴 (1)
  • 追加の質問ぶんへの回答も書きました。ただ、この追加の質問は「質問」のほうに書いてあるほうが、後で同じような疑問を持った皆さんにもわかりやすいかと思います。良かったら「質問」のほうへ移しておいていただけませんか? -
  • 質問に移してみました。
    こちらは分かりづらいので削除したいところですが、削除出来ないようなのでノーコメントに更新しておきました。
    -

こんにちは。Railsのセッション管理についての質問だと想定し、アプリの作りに依存しそうな部分はRails前提で回答します。

まずはじめに

実態としてはサーバーサイドに保持し、セッション ID は Cookie に保存しているものだと思っています(Rails の経験が殆ど無いので間違っていたらすみません)。

こちらは誤解で、Railsは(デフォルトでは)セッション情報を すべて Cookieに入れます。ですからたとえばRailsのセッション情報はシリアライズした上で4KBまでしか入れられませんが、それはそういうものだから設計を見なおすべき、というのがRails wayであるとされています。

1. Cookieが覗き見されたらどうする問題

そうは言っても Cookie の値は HTTP リクエストに乗って送信されます。仮にこのリクエストを覗き見されてしまうとそのままセッションの乗っ取りが可能ですよね?

乗っとり可能です!!

一般論として、Webアプリで悪意ある第三者にCookieが盗まれたらヤバイです。けっこうどうしようもありません。だいたいはブラウザのバグです。Webアプリ側で盗まれないようにするのはほぼ不可能です。素のhttpでパケットを盗聴されるケースも、アプリ側ではほとんどなにもできません。最近大手のサービスが軒並み常時httpsになっているのは、この辺の事情かと思います。

というのが前提だと思ってください。

そのうえで、盗まれにくくしたり、盗まれても被害を減少させる手法はいくつかあります。

そのうちの1つがhttpsとhttpでは認証の仕方を変えるというものがあります。httpsでの通信の場合は別のhttpsでしか流れないCookie(Secure Cookie)の情報も参照して、一致している場合のみ有効なセッション情報とするという対応です。

けっこうややこしいですが、下記のRails Castの説明が詳しくていいと思います。

http://railscasts.com/episodes/356-dangers-of-session-hijacking?language=ja&view=asciicast

この場合は『httpではなりすまされても仕方ないが、httpsでは防ぎたい』という線引で、重要な操作はhttpsで行うことで実質の被害を減少させる手法ですね。

あとはCookieにhttp only属性を付けて悪意あるJSからのアクセスを防いだり、セッション中に短めのexpire時間を設定し、攻撃者に盗まれたCookieをすぐ無効化できるようにする、などがあげられます。

2. ログイン画面がhttpsなのは〜

おっしゃるとおり第三者の覗き見を避けるのと、送信先のサーバが間違いなくはてなであることを保証するためかと思います。

3. 業務アプリケーションのようにプライベートネットワーク内でしか動かない Web アプリケーションの場合

わたしはこのごろはだいたいの案件で、すべてのページをhttpsにしています。httpとhttps混在させるとかえって面倒(JSの生成元同一ポリシーなどとの絡みで)なので、基本はすべてhttpsの中に置き、どうしても必要な場合のみhttpのページを作っています。

サービス系だとSNS系メディアとの関係でhttpのページが必要になることは多いですが、業務だと逆にすべてhttps下で困ることはあまりないのではないでしょうか。

4. について

いくつか論点がありますが、

Rails は Cookie およびサーバーサイドにセッション情報を保存していますが、REST 本来の意味ではこれは NG だと思っています。

上述のように、セッション情報はすべてCookieに入っています。当然ながら、Cookieも実体はHTTPリクエストヘッダであり、リクエストの一部です。

サーバサイドに保存しているリソースであるところのユーザのIDが入っていて、ユーザの実体はサーバサイドにあるじゃないかといわれるとそうなんですが、それがREST的にNGだとは思いませんがいかがでしょうか。

少なくとも Cookie は使うことになるのかなと思っています。Cookie を使わない方法もあるのでしょうか?Web アプリで BASIC 認証や Digest 認証は使えませんよね?

BASIC認証やDigest認証も使えます。けっこう簡単です。あのあまり見栄えの良くないブラウザダイアログを許容するのであれば、ごく少数のユーザ向け認証はhttps+BASIC認証でやったりもします。session[:user_id]的な識別子が欲しい場合request.env['Authorization']を適宜パースすればよさそうです。

また、もしも4KB以上のデータをセッションに入れたい場合は、セッション情報をDBに入れてCookieにはセッションIDだけを保存することも可能です。その場合はRails session ActiveRecordStoreあたりで調べていただければ良いかと思います。


以下、追記への回答です

@ursm さんから指摘があって回答を変更しました。cookieサイズ(=通常のセッションサイズ)の上限は4KBでした。とても小さいですね。とはいえ、実用上は特に困らないというのは相変わらずです。

良く Rails で session[:user_id] みたいなコードを見るのでサーバーサイドにセッションを保持しているのかと思っていました。このセッションストアが KVS だったり ActiveRecord だったりするのかなと思っていたのですが・・・。DB にセッション情報を永続化して都度取得するようにすると重くなってしまうので、インメモリもしくは KVS あたりにセッション情報を保存するのかなと思っていました。

上記の例では、ユーザのID(普通は整数) だけがセッションに入っています。アプリ側ではセッションから読み出した ユーザのIDを使ってユーザをDBから取得 します。多くはいったん読み出したユーザのデータをコントローラのインスタンス変数に保持し、コントローラとビューの各所で使いまわします(慣用的にcurrent_userというアクセサで取るようにつくります)。言い換えますと、「セッション情報に含む」と「セッション情報に含まれるキーから読み出したDB上のデータを、リクエストの間中メモリ上に保持すること」は違うということです。

ちなみに、余程のことがなければリクエストごとにDBから検索してきてもそうそう速度へのインパクトはないかと思いますし、実際にそこで困ったことはありませんね。

全て Cookie に保存する場合、ショッピングカートなどを実現しようと思ったらきっとサーバー側の永続化層にセッション情報を持たざるを得ない気がしますが(間違っていたらご指摘ください)、例えば良くあるユーザーのロール/権限などの情報はどうするのでしょうか?これも Cookie に保存するのが一般的なのでしょうか?

サーバ側で読み出したユーザデータをつかって、ふつうにサーバ側でカートを実装することが多いですね。DBMSにするか揮発するKVSにするかなどの手段はともあれ、おっしゃるとおりサーバ側の何処かに永続化します。権限も、読みだしたユーザデータに紐づくようなものをまた権限テーブル的なところから読み出したりして判定します。

また、サーバー側で「現在アクセス中のユーザー」を取得せざるを得ないケースが多いかと思うのですが、session id と user の紐づけはどのように行うのでしょうか?user id を Cookie に保存するのはマズいですよね・・・?

上記のように、セッション中(つまりcookie中)に保存したユーザIDを使って検索します。セッションcookieは改竄防止の署名がありますので、IDを保存してまずい理由はとくにありません。逆に言うと、見られてマズイ情報はセッションに入れません。ユーザ本人に見られてマズイ情報(改竄はされない)って意外と少ないです。

1

ただ、これって https の場合になりすましを防ぐ方法になっていますが、そもそも https でなりすまし(というか Cookie の盗聴)されるケースってあるのでしょうか……?

言い方がわかりづらかったかもしれません。あのページは、「httpとhttpsのどちらのページもあるサービスを提供している場合に、http側でクッキーが盗まれてもhttps側に被害を出さない」方法を説明しています。ですから、すべてをhttpsでくるんでしまえば盗聴、ひいてはなりすましは考えなくても良いでしょう(証明書の運用などに気をつけ、SSLを正しく使っている限り)。

2, 3

どこまで〜、はなかなか悩ましいですよね。ということで、盗聴やなりすまし対策については「すべてhttpsにしましょう」という構成にすることで話をつけています。すぐ上で書いたように、すべてhttpsであれば元回答にあるようなシナリオでのセッションハイジャックは対策ずみになります。

4

4であげられていた点も回答済みかと思いますが、いかがでしょうか。

繰り返しになりますが、Railsでは

  1. セッションデータ
  2. セッションデータに入っている何らかのキーをもとにDBから引いてきてメモリに保持しアクション内で引き回すデータ

は明確に区別されます。cookieに保存されるのは1.のデータで、これはユーザのIDや最終アクセス時刻など 4KB にすら到底及ばないようなデータです。アプリケーション側では、そのセッションデータを元に2.のデータをロードし、実際の処理で使うのでした。権限やロールもユーザデータに含まれるキーなどを元に都度DBから読みだして行き、サーバ側での処理に用います。

編集 履歴 (4)
  • 質問欄にも追記しましたが理解出来ました。
    何度も丁寧な回答ありがとうございました!
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。