はじめに
前回の記事の続きです。
前回の記事はこちら
sessionとデータの保存
今回はsession
について理解していきます。
今週はサボりすぎたので、この章はまとめていく予定です。
sessionとcookie
session
と cookie
についての説明です。
簡単に言うと、クライアント側でのデータ保存か(cookie
)、サーバ側でのデータ保存(session
)です。
cookie
と session
の説明があるので、引用すると下記のようなものです。
cookieとは、簡単に言えばローカルマシンに保存されたユーザの操作の履歴情報です(当然ログイン情報を含みます)。
またユーザが再度このページにアクセスした際ブラウザはHTTPプロトコルを通してローカルのcookieの内容をサーバに送信し、検証を行います。
または継続して前の操作を行います。
session
sessionとは、簡単に言えばサーバ上に保存されたユーザの操作の履歴情報です。
サーバはsession idを使用してsessionを識別します。
session idはサーバが生成します。ランダム性とユニーク性を保証し、ランダムな秘密鍵に相当します。
ハンドシェイクやデータ通信中にユーザの本当のパスワードが暴露されるのを防ぎます。
しかしこの方法では、依然としてリクエストを送信したクライアントとsessionを対応させる必要があります。そのためcookieメカニズムによってクライアントのID(session id)を取得することで、GETメソッドでidをサーバに送信することができます。
cookie
早速 cookie
について見ていきましょう。
お使いのブラウザで、 F12
を押して開発者用のコンソールが出せます。Chrome
ですと、Application
タブに、Cookies
というものがあるのが見えますので、それが保存されているCookie
になります。
記載の通り、クライアント(ブラウザ)に保存されています。
GoでCookieを設定する
これは実は少し前のトークンの部分で使っていますね。
ですので、今回はコード例だけ見てみましょう。net/http
パッケージにある SetCookie
でセットすることが可能です。
http.SetCookie(w ResponseWriter, cookie *Cookie)
Goでcookieを読む
今度は読む方法です。
cookie, _ := r.Cookie("username")
fmt.Fprint(w, cookie)
rはrequest
ですね。
session
sessionはWeb開発環境ではまた新しい意味が含まれます。
クライアントサイドとサーバサイドの間でステートを保持するためのソリューションです。
しばしばSessionはこのようなソリューションの保存構造も指します。
sessionメカニズムはサーバサイドのメカニズムです。サーバでハッシュテーブルの構造に似たもの(ハッシュテーブルを使う場合もあります)を使用することで情報を保存します。
ということです。Session
は若干難しい話になっておりますが、簡単に言うとクライアント-サーバ間でデータを保持するためのものと捉えて良いと思います。
まとめの部分がかなりわかりやすいので、読んでみます。
上述の通り、sessionとcookieの目的は同じです。
どちらもhttpプロトコルのステートレスであるという欠点を克服するためにあります。
しかしその方法は異なります。sessionはcookieを通じてクライアントにsession idを保存します。
またユーザの他のセッション情報はサーバのsessionオブジェクトに保存されます。
これとは対照的に、cookieはすべての情報をクライアントに持たせる必要があります。
ということです。データの保存する部分がクライアントかサーバかということですね。
Webセキュリティにも関わってくる話ですが、クライアント側で保持ししているデータに関しては、そのまま使用するべきではなくサーバ側では検証をする必要があったりします。
なので、このあたりは気をつけたほうが良いと思います。
では次章 Goはどのようにしてsessionを使用するか
をやります。
Goはどのようにしてsessionを使用するか
現在Goの標準パッケージにはsessionのサポートがありません。
この節では実際に手を動かしてgoバージョンのsession管理と作成を実現してみます。session
のサポートがないらしいので、ここで自作をするらしいです。
sessionの作成過程
サーバサイドで保持する必要があるので、作成部分をいくつかのステップに分けるようです。
- グローバルでユニークなIDの生成(sessionid)
- データの保存スペースを作成。普通はメモリの中に対応するデータ構造を作成します。しかしこのような状況では、システムは一旦電源が切れると、すべてのセッションデータが消失します。もしeコマースのようなホームページであった場合、これは重大な結果をもたらします。そのため、このような問題を解決するためにセッションデータをファイルの中やデータベースの中に書き込むことができます。当然この場合I/Oオーバーヘッドが増加しますが、ある程度のsessionの永続化は実現できますし、sessionの共有にも有利です。
- sessionのグローバルでユニークなIDをクライアントサイドに送信します。
なるほど、当然どれも必要ですね。
session
のユニークIDの伝え方を今度は考えると、下記の2つになるようです。
- Cookie サーバサイドはSet-cookieヘッダーを設定することでsessionのIDをクライアントサイドに送信することができます。クライアントサイドは以降の各リクエストすべてにこのIDを含めます。またsession情報を含んだcookieの有効期限を0(セッションcookie)、つまりブラウザプロセスの有効期限に設定することもよく行われます。各ブラウザはそれぞれ異なる実装がされていますが、差はそれほど大きくはありません(一般的にはブラウザウィンドウを新規に作成した際に反映されます)。
- URLの書き直し いわゆるURLの書き直しとは、ユーザに返されるページの中のすべてのURLの後ろにsessionIDを追加することです。このようにユーザがレスポンスを受け取った後、レスポンスのページの中のどのリンクをクリックしたりフォームを送信しても、すべて自動的にsessionIDが付与されます。これによりセッションの保持を実現します。このような方法はすこし面倒ではありますが、もしクライアントサイドがcookieを禁止している場合、このようなソリューションがまず選ばれます。
1番目は、cookie
にsession_id
を含めて送信してもらうパターンです。
2番目は、URLについている、jsessionId=XXXXX
とか見たことがあると思います。アレデスね。
Goでsession管理を実現する
session管理設計
session
管理には下記が必要です。
- グローバルなsessionマネージャ
- sessionidがグローバルにユニークであることの保証
- 各ユーザをひとつのsessionに関連付ける
- sessionの保存(メモリ、ファイル、データベース等に保存できます)
- sessionの期限切れ処理
これらを実装するようですね。
さてここからはコード例になるので、自分の環境で試してみます。
(前回と同様の環境を使います。)
https://gist.github.com/katsuobushiFPGA/c086076486a223cafd112bd3abf29779
Session
のサンプルプログラムを書きました。Session
に関しては、筆者の方のライブラリを使用させていただいております。
(この章のsesson
の制作をライブラリ化したもの)
2つほど動作を確認できるものがあります。
/count
にアクセスした際にアクセス数がカウントアップされることのテスト/login
で 入力したユーザ名を リダイレクトした後もサーバ側で保持できているかのテスト
上記が確認できますので試してみてもらえればと思います。
Session
のサンプルが試せたと思います。
sessionストレージ
こちらはコード例のみで、上記と同じような内容になりますので、じっくり読むことで理解します。
この章はこれで終わりにします。
sessionハイジャックの予防
この章は、sessionハイジャック
と呼ばれる 中間者攻撃についての理解を深めるパートです。
sessionハイジャックは広範囲に存在する比較的重大な脆弱性です。
session技術において、クライアントサイドとサーバサイドはsessionのIDによってセッションを維持します。
しかしこのIDは簡単にスニッフィングされ、第三者に利用されてしまいます。これは中間者攻撃の一種です。
先程、コンソールに出力していたsessionID
ですね。
これが盗まれて、第三者に利用されたときにどうするべきかというものです。
sessionハイジャックの過程
先程、session
のサンプルを書いたものと同じものを使用していきます。count.gtpl
も同じでいいですね。
cookie
の情報を確認してみます。gosessionid
という 今回管理している cookie
の value
を確認してみます。
次のステップが重要です:別のブラウザ(ここではfirefoxブラウザを開きました)を開き、
chromeのアドレスバーのアドレスを新たに開いたブラウザのアドレスバーにコピーします。
その後firefoxのcookieエミュレートプラグインを開き、新規にcookieを作成します。
上の図のcookieの内容をそのままfirefoxの中に再設定します
やってみましょう。
Firefoxで localhost:9090/count
にアクセスしてみると、最初は Count:1
が表示されています。
同じものにしてみました、保存してみましょう。
F5で更新してみると、 Firefoxの方は count:10となりました!
Firefox と Chromeのキャプチャです。
ブラウザを変えても、sessionIDを取得することができました。
この後cookieの保存過程をエミュレートします。
この例は一台のコンピュータの上で行ったものです。
たとえ二台によって行ったとしても結果は同じです。この時もし交代で2つのブラウザのリンクをクリックした場合、操作しているカウンターが実は同じものであるということに気づくでしょう。
おぉ~。sessionID
を盗んで同じセッションになりましたね。
これがログイン後の画面のセッションとかですと、他の人のログインセッションを乗っ取ることができるわけですね。
ここではfirefoxがchromeとgoserver間のセッション維持の鍵を盗みました。
すなわち、gosessionidです。これは"セッションハイジャック"の一種です。
goserverからすると、httpリクエストからgosessionidを得ました。
HTTPプロトコルのステートレスによってgosessionidがchromeから"ハイジャック"されたものなんか知る方法はありません。
依然として対応するsessionを探し、関連する計算を実行します。
同時にchromeも自分が保持しているセッションがすでに"ハイジャック"されたことを知る方法もありません。
なるほど、セッションIDは鍵なので鍵が盗まれたという感じですね。
sessionハイジャックの予防措置
cookieonlyとtoken
この節は予防方法についてのようです。
- ひとつの方法はsessionIDの値にcookieによってのみ設定されるようにすることです。URLの書き直し方法は許さないようにし、同時にcookieのhttponlyをtrueに設定します。
なるほど?
このプロパティはクライアントサイドのスクリプトが設定されたcookieにアクセスできるか否かを設定します。まず、これによってこのcookieがXSSによって読み取られ、sessionハイジャックを引き起こすことを防止できます。
HttpOnly
は Javascript
からの読み込みができなくなるので、上記のようなケースを防げるわけですね。
つぎにcookieの設定がURLの書き直し方法によって容易にsessionIDを取得することができなくなります。
なるほど
- ステップ2は各リクエストの中にtokenを追加することです。前の章で述べたformの重複送信を防止するのに似た機能を実装します。各リクエストの中で隠されたtokenを追加し、毎回このtokenを検証することでユーザのリクエストがユニークであることを保証します。
これはフォームの章でもやりましたね。token
を用意しておくことで個々のクライアントを識別することができます。
間隔をおいて新しいSIDを生成する
もう一つの方法は、sessionの他に作成時間を設けることです。
一定の時間が過ぎると、このsessionIDは破棄され、再度新しいsessionが生成されます。
このようにすることで、ある程度sessionハイジャックの問題を防ぐことができます。
なるほどね。
sessionが始まると、生成されたsessionIDの時間を記録する一つの値が設定されます。
毎回のリクエストが有効期限(ここでは60秒と設定しています)を超えていないか判断し、定期的に新しいIDを生成します。これにより攻撃者は有効なsessionIDを取得する機会を大きく失います。
上の2つの手段を組み合わせると実践においてsessionハイジャックのリスクを取り除くことができます。
sessionIDを頻繁に変えると攻撃者に有効なsessionIDを取得する機会を失わせます。
sessionIDはcookieの中でやりとりされ、httponlyを設定されるため、URLに基づいた攻撃の可能性はゼロです。同時にXSSによるsessionIDの取得も不可能です。
最後にMaxAge=0を設定します。これによりsession cookieがブラウザのログの中に記録されなくなります。
結構大変そうですが、セキュリティ対策と考えれば必須の処理ですね。
おわりに
今回はsession
について勉強しました。go
での session
管理は結構大変そうです。
セキュリティ対策については、すべての言語で共通ですので、知っておかないといけないことですね。
次からは、テキスト処理
についてです。