質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.10%

セッションの安全な管理方法について

解決済

回答 2

投稿

  • 評価
  • クリップ 10
  • VIEW 4,425

score 30

ログイン認証無しの一般的なメールフォーム等の入力、確認、完了ページをセッションで管理する場合についてお聞きしたいので教えていただけないでしょうか。

セッションIDの保存にクッキーを使用しているのですが、

1 どのタイミングでセッションを再生成すればよいか。

現在は入力フォームに入った段階で、セッションスタート後すぐにsession_regenerate_idをしています。
その後、リロードした場合や確認ページから入力内容の編集で戻ってきた場合に指定秒経過ごとに再度session_regenerate_idをしています

2 セッションクッキーのタイムアウトについて

入力ページで決めうちでsession_set_cookie_paramsを使用し、expire属性に例えば3600(1時間)と設定するのが良いのか、もしくは、setcookieでページ遍移ごとに有効期限をリセットして延長する方法が良いのか、どちらが良いのか疑問です。

また、セッションクッキーには時間を設定せずに「 0(ゼロ) 」にした方がクッキーをクライアントのメモリのみに保存するので安全にも思います。

どうか、ご教授いただけないでしょうか。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • ikedas

    2017/02/20 16:17

    ソースコードで、ご質問で述べておられる処理をしている箇所を示していただいたほうがいいと思います。

    キャンセル

回答 2

checkベストアンサー

+4

ご質問のテーマについては、以下の二種類の観点を持つことがよいと考えます。

(a)他に脆弱性がなくても単独で情報漏えい等が発生する脆弱性という観点
(b)他に(クロスサイトスクリプティングなどの)脆弱性がある場合に被害を緩和する観点

まず、(a)に関して言えば、ログインせずに、セッション変数で情報を持ち回りする場合、「ログイン前セッションIDの固定化攻撃」が成立する場合があります。
これは、セッションIDのクッキー(デフォルトではPHPSESSID)の値を外部の攻撃者にセットされた場合、そのセッションIDは攻撃者がわかっているので、セッション変数に値が入ったタイミングで攻撃者がサイトにアクセスすると、入力途中の値を攻撃者に閲覧される可能性がある、というものです。

それでは、セッションIDのクッキーを外部からセットする方法ですが、以下のようなものがあります。

  1. クッキーモンスターバグによる方法(地域型JPドメイン名、都道府県型JPドメイン名を使用していて、IE利用の場合)
  2. HTTPSを使用しているサイトで、通信路上に攻撃者がいる場合

クッキーモンスターバグとは、IEを利用していると、Set-Cookie時に domain=tokyo.jp など広範囲に適用されるクッキーが作れてしまう問題のことです。例えば、東京都のドメイン名は metro.tokyo.jp ですが、地方公共団体以外でも xxxx.tokyo.jp のドメイン名を持つサイトは非常に多いです。(Googleで site:tokyo.jp で検索してみてください)これらのサイト全てで、domain=tokyo.jpのクッキーを発行できる(ただしIEユーザのみ)ので、クッキーの汚染は現実的な問題になります。
HTTPSを利用している場合については下記の記事を参照ください。

HTTPSを使ったほうが危険になるのかと驚かれるかもしれませんが、そうではありません。HTTPSを使っておらず通信路上に攻撃者がいる場合は、クッキーの盗聴も改変も可能ですし、そもそも生の個人情報等も盗聴できるので、こちらは問題外ということになります。それを防ぐためのHTTPSであるわけですが、(クッキーの盗聴は防げるが)クッキーの改変は防げない、という問題です。

さて、ログイン前セッションIDの固定化攻撃を防ぐ確実な方法は、適切なタイミングでセッションIDを変更することです。そして、適切なタイミングを下記に示します。

  • 秘密情報をセッション変数に格納する直前

このタイミングでセッションIDを変更すると、「攻撃者が知っているセッションID」には秘密情報は入らないので、確実な対策になります。
ログインを前提とするサイトの場合は、ログイン確認直後、かつセッション変数にログイン情報をセットする前にセッションIDを変更するべきですが、これも同じ理由です。

し・か・し・な・が・ら、ログインをしないケースでは、上記を実施しているウェブサイトはあまり多くないように見受けます。その理由は、セッションIDの固定化攻撃が成立する条件が厳しいわりに、攻撃により得られる「成果」があまり多くない(ログイン状態を乗っ取れるわけではない)ので、脆弱性を許容していると考えられます。(単に知らないだけという可能性ももちろんあります)。

一方、(b)に関しては、セッションタイムアウトの設定が有効です。こちらは、セッションのexpires指定による方法もありますが、より望ましい方法は、セッション変数として「最終クセス日時」を保存して、アクセスの度に確認することです。こうすれば、クッキーにはexpiresを指定しないため、クッキーがパソコンのファイルに保存されることもなくなります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/24 23:34

    徳丸さんですよね。。。?

    徳丸様直々にご教授いただきましてありがとうございます。
    安全なWebアプリケーションの作り方 を拝読させてもらおうと思います、
    お答えいただきありがとうございました。

    キャンセル

  • 2017/02/24 23:39

    はい。徳丸です。
    失礼ながら、ベストアンサーは間違っていますよ。

    キャンセル

  • 2017/02/25 00:38

    徳丸様の最下部のコメント読みました。

    「フォームから入力された値をセッション変数に書き込む直前」ということになります。
    入力フォーム時点で新しいセッションIDを発行しても、それからユーザが操作をしている間に、セッションIDのクッキーが変更されてしまう可能性があります。

    やはりログイン認証仕様を実装しない場合は、確認ページに入った段階でsession_regenerate_idですね。(入力ページで文字入力→確認ページへの遍移ボタンsubmitでセッションファイルに入力データが書き込まれるため)

    キャンセル

+1

(2/24 ご質問の1への回答を若干編集しました)
(2/25 コメントを受けてセッション開始の条件を訂正)

まず、参考資料を示します。

これはひと通り読まれるとよいと思います。

要点はいろいろありますが、セッションIDを守ることに絞ると、次の3つにまとめられるようです。

  1. セッションIDの推測に対する対策
  2. セッションIDの奪取に対する対策
  3. セッションIDのお膳立てに対する対策

このうち1.と3.については、PHPのセッション管理機構を使っていれば通常は問題ないでしょう。

また2. については、cookieの使用は効果的です。ただしcookieを使うだけでは十分でなく、通信路の暗号化を必須とする (HTTPSにした上で、cookieにsecure属性をつける) ことや、スクリプトによるアクセスを防ぐ (httponly属性をつける) ことも検討するべきでしょう。

前置きおわり。


1 どのタイミングでセッションを再生成すればよいか。

session_regenerate_id()は、あまり必要ないと思います。

セッション開始時はsession_start()だけでいいです。実装上は、IDを生成するたびにOSの(疑似)乱数生成器を使っているだけなので、あらためてsession_regenerate_id()しても暗号論的な強度は変わりません。

また、セッションの生成と破棄ですが、次の原則を守るべきです。

  • 守りたい通信が開始するときにセッションを開始する。
  • 守りたい通信が終了したら即座にセッションを破棄する。

今回の場合は、入力フォームで最初に入力があった段階でセッションを開始し、送信完了 (または取り消し) 時にセッションを破棄するようにします。

なお、セッションの破棄のしかたについては末尾[1]に補足します。また、今回はログインはしないとのことですが、する場合について[2]に補足します。

次に、セッション中のセッションID再生成について考えます。

上記1. (セッションIDの推測) については、再生成しても特に乱数の強度が上がるとは思えません。また、2. (セッションIDの奪取) に対して効果はありません。対策は上で述べた通り、通信路を守るなどするしかありません。3. (セッションIDのお膳立て) に対しても特に効果はないです。

一方で、次のような場合は問題が発生します。

  • フォームの送信ボタンの2度クリックをしてしまった場合。
  • リンクの2度クリックをしてしまった場合。
  • ページ中に埋め込まれたコンテンツ (画像など) も、セッション管理の対象である場合。

複数回のリクエストがほぼ同時に発行されるため、その間にセッションIDが再生成されると、クライアントが最新のセッションIDを受け取り損ね、セッションが切れてしまう可能性があります。

ですので、上で述べた対策がとられている限り、ログインセッション中にセッションIDを再生成する必要はないと思います。次で述べるセッションクッキーのタイムアウトが設けられていればいいでしょう。

2 セッションクッキーのタイムアウトについて

cookieのタイムアウトの長さは、作ろうとするシステムの仕様次第だと思います。ただし、絶対的なタイムアウトは設けるべきです。

再度アクセスしたときも継続して使いたい情報があるのなら、cookieにタイムアウトを明示的に設定すればいいと思います。そういうものがなければ、cookieにExpire属性やMax-Age属性を設定せず、ブラウザを閉じた時にcookieが消えるようにすればいいと思います。

ただし、cookieのタイムアウト設定はクライアントが正しく処理してくれることが前提なので、サーバ側では絶対的なタイムアウトを設けて、それを過ぎたセッション情報をサーバから消去すべきです。

絶対的なタイムアウトも、仕様に応じて数時間から数日までいろいろでしょう。


[1] セッション破棄のしかたはPHPのマニュアルにも例がありますが、説明を補足しておきます。

$_SESSION = array();                          # セッションデータを消す。
$params = session_get_cookie_params();        # cookieを期限切れにして消す。
setcookie(session_name(), '', time() - 42000,
    $params["path"], $params["domain"],
    $params["secure"], $params["httponly"]
);
session_destroy();                            # セッションを破棄する。

まずセッションデータを消しておかないと、セッションを破棄するまでの間にcookieを送り込まれる可能性があります。ですので、厳密にやるのなら上のようになります。

[2] ログインする場合について補足します。

上で述べた原則により、基本的にはログイン成功したときにsession_start()し、ログアウトしたらすぐにsession_destroy()するようにします。

なお、ログイン中以外でもセッションを使っているかもしれませんが、ログイン後に同じセッションを使いまわす (session_regenerate_id()でIDだけ変える) ようにしないほうがいいでしょう。ログインしていない間に攻撃者から情報を注入されたときに、それを信頼してログイン後にも使ってしまうようなコードを書けてしまいます。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/24 23:38

    > IPAの資料では「セッションIDの付け替え」を「ユーザがログインに成功した時点でまったく新しい
    > セッションIDを発行し、それまでのセッションIDを無効にするというもの」と説明しています。今回
    > の場合はログインしないということですので、「送信フォームに入った時点」が「セッション付け替え」
    > のタイミングだと思います。

    これは間違いです。「ユーザがログインに> 成功した時点でまったく新しいセッションIDを発行」というのは、そのタイミングがログイン情報をセッション変数に書き込むタイミングだからです。
    ログインがない場合は、入力フォームに入った時点ではなく、「フォームから入力された値をセッション変数に書き込む直前」ということになります。
    入力フォーム時点で新しいセッションIDを発行しても、それからユーザが操作をしている間に、セッションIDのクッキーが変更されてしまう可能性があります。

    キャンセル

  • 2017/02/25 05:42 編集

    > ログインがない場合は、入力フォームに入った時点ではなく、「フォームから入力された値をセッション変数に書き込む直前」ということになります。
    > 入力フォーム時点で新しいセッションIDを発行しても、それからユーザが操作をしている間に、セッションIDのクッキーが変更されてしまう可能性があります。

    そうでしょうか。この場合、セッションが必要なのは、確認画面や取り消し画面、再編集画面への遷移があるので、入力を保持するためでは。「セッション変数に書き込む」タイミングは1回ではないです。

    キャンセル

  • 2017/02/25 14:27 編集

    ああ、そうか。たしかに、新しいセッション (セッションIDではなく) を生成するのは「最初に入力フォームから入力があったとき」ですね。その後、入力画面に再び遷移したときにどうするかは、アプリの仕様次第 (入力ずみとしてもいいでしょうし、セッション破棄してもいい) ということですね。
    ちょっと修正しておきます。

    いずれにせよ、セッションIDだけ変える (サーバのセッションデータストアは変えない) ということは避けたほうがよいと思うので、session_regenerate_id()の出る幕はないですね。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る