セッション

session は、ユーザとウェブアプリケーションとの相互作用を表現 するために使用できる、連続的な活動の特定の期間で有効な名前空間です。

この章では、セッションをどのように設定するか、 Pyramid が 最初から提供しているセッション実装にはどんなものがあるか、セッションに データを格納したり検索したりする方法、そしてセッションに特有の2つの機能、 フラッシュメッセージとクロスサイトリクエストフォージェリ攻撃防御について 記述します。

デフォルトセッションファクトリを使う

セッションを使用するためには、 Pyramid 設定で session factory をセットアップする必要があります。

非常に基本的な、セキュアでないセッションファクトリの実装のサンプルは Pyramid コアの中で提供されます。この実装は、セッション情報を 格納するために cookie を使用します。この実装には次の制限があります:

  • この実装によって使用される cookie 中のセッション情報は暗号化 されません 。したがって、ユーザのブラウザの cookie ストレージに アクセスできる人なら誰でも、あるいは cookie が流れるネットワークに アクセスできる人なら誰でも、その情報を見ることができます。
  • 保存可能な最大バイト数は、セッションのシリアライズ表現で 4000 未満です。 これは、非常に小さなデータセットにのみ適しています。

しかし、それはデジタル署名されており、したがってそのデータを簡単に 改竄することはできません。

Configurator クラスに session_factory 引数 を渡すことで、 Pyramid アプリケーション内でこのセッションファクトリ を設定することができます:

1
2
3
4
5
from pyramid.session import UnencryptedCookieSessionFactoryConfig
my_session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet')

from pyramid.config import Configurator
config = Configurator(session_factory = my_session_factory)

Warning

UnencryptedCookieSessionFactoryConfig という、非常に長くて明示的 な名前に注意してください。これは、この実装がデフォルトでは 暗号化されない ということを伝えようとしています。セッションオブジェクト に機密情報を保存する場合、この実装を使用するべきではありません。 なぜなら、アプリケーションのユーザとユーザのネットワークトラフィック にアクセスできる第三者の両方が、情報を容易に読み出すことができるからです。 また、このセッション実装を使用していて、不注意にもアプリケーションで クロスサイトスクリプティング脆弱性を生み出せば、セッションデータは cookie の中に暗号化されずに保存されているので、悪意を持った攻撃者が現在 のユーザーのクロスサイトスクリプティングトークンを得ることも簡単でしょう。 端的に言えば、アプリケーションの中で「セッションセキュリティは気にしない」 最も基本的な部分以外では、異なるセッションファクトリ実装 (できれば サーバー上にセッションデータを保存するもの) を使用してください。 そうすれば、アプリケーションがクロスサイトスクリプティング脆弱性を 持っていないと確信できます。

セッションオブジェクトを使う

アプリケーションのために一旦セッションファクトリが設定されたら、 セッションファクトリによって提供されるセッションオブジェクトに 任意の request オブジェクトの session 属性によって アクセスすることができます。例えば:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from pyramid.response import Response

def myview(request):
    session = request.session
    if 'abc' in session:
        session['fred'] = 'yes'
    session['abc'] = '123'
    if 'fred' in session:
        return Response('Fred was in the session')
    else:
        return Response('Fred was not in the session')

セッションは Python 辞書とほとんど同じように使うことができます。 それはすべての辞書メソッドに加えて、いくつかの追加の属性とメソッドを サポートします。

追加の属性:

created
このセッションが作られた時刻を示す整数タイムスタンプ。
new
真偽値。 new が True の場合、このセッションは新規作成されました。 そうでなければ、シリアライズ済みのデータから復元されました。

追加のメソッド:

changed()
セッション名前空間の中の mutable な値を変更する場合、このメソッド を呼んでください。このメソッドをいつ、そしてなぜ呼ばなければならないか についての詳細は、下記の gotchas を参照してください。
invalidate()
セッションを無効にしたい場合 (すべてのデータをダンプして、 – 恐らく – cleaning cookie をセットする)、 このメソッドを呼んでください。

セッションオブジェクトがサポートするメソッドと属性の正式な定義は、 pyramid.interfaces.ISession ドキュメンテーションにあります。

Some gotchas:

  • セッションデータのキーおよび値は pickle 可能でなければなりません。 これは、典型的には、文字列、リスト、辞書、タプル、整数などのような 基本型のオブジェクトのインスタンスを意味します。pickle 可能でない オブジェクトをセッションデータのキーまたは値に入れると、セッションが シリアライズされる時にエラーが上げられるでしょう。
  • セッションオブジェクトに mutable な値 (例えばリストまたは辞書) を入れて、 続いてその値を変更した場合、セッションオブジェクトの changed() メソッドを呼ばなければなりません。この場合、セッションにはそれが変更 されたことを知る方法がありません。しかし、セッションオブジェクトを 直接修正した場合、例えば値をセットしたり (つまり __setitem__) キー を削除したり (例えば delpop) した場合は、セッションがその データを再度シリアライズする必要があることを自動的に知るので、 changed() を呼ぶ必要はありません。どちらの場合でも changed() を呼ぶことに害はないので、不確かなときはセッションデータを変更した後で そのメソッドを呼んでください。

代替セッションファクトリを使う

これを書いている時点では、 pyramid_beaker と呼ばれる唯一の代替 セッションファクトリ実装が存在します。これは、バックエンドとして Beaker ライブラリを使用するセッションファクトリ です。 Beaker は、ファイルに基づいたセッション、データベースに基づいた セッション、暗号化された cookie に基づいたセッションをサポートしています。 pyramid_beaker についての詳細は、 pyramid_beaker ドキュメンテーション を 参照してください。

独自のセッションファクトリを作る

Pyramid のためのデフォルトの、あるいは他の利用可能なセッション 実装のどれも適合しない場合、 session factory を実装することで自分 のセッションオブジェクトを作成することができます。セッションファクトリは session を返す必要があります。それぞれの型に対するインタフェースは pyramid.interfaces.ISessionFactorypyramid.interfaces.ISession で利用可能です。 pyramid.session モジュールの cookie 実装をインスピレーションとして 使用すると良いかもしれません。

フラッシュメッセージ

「フラッシュメッセージ」は session に格納されるメッセージ文字列 の単なるキューです。フラッシュメッセージを使用するために、 デフォルトセッションファクトリを使う または 代替セッションファクトリを使う で述べられているように session factory を有効にしなければなりません。

フラッシュメッセージには2つの主な用途があります: 内部リダイレクトを行なった 後でユーザにステータスメッセージを一度だけ表示することと、単発の ログメッセージを表示する汎用的なコードを HTML テンプレートに直接 アクセスすることなく可能にすることです。そのユーザインタフェースは session オブジェクトの多くのメソッドから構成されます。

session.flash メソッドを使う

フラッシュメッセージ・キューにメッセージを追加するためには、 セッションオブジェクトの flash() メソッドを使用してください:

request.session.flash('mymessage')

flash() メソッドは、必要ならキューを作成して、フラッシュキューに メッセージを追加します。

flash() は3つの引数を受け取ります:

flash(message, queue='', allow_duplicate=True)

message 引数は必須です。後でユーザに表示してほしいメッセージを表わします。 通常これは文字列ですが、提供された message は決して変更されません。

queue 引数は、提供されたメッセージをどのキューに追加するか選択 できるようにします。これは後でページ上の異なる場所に表示するため異なる 種類のメッセージをフラッシュストレージに push するのに使えます。 キューにはどんな名前も渡せますが、文字列でなければなりません。 キューはそれぞれ独立していて、 pop_flash() によってポップしたり、 peek_flash() によって別々に検査したりすることができます。 queue のデフォルトは空の文字列です。空の文字列はデフォルトの フラッシュメッセージ・キューを表わします。

request.session.flash(msg, 'myappsqueue')

allow_duplicate 引数のデフォルトは True です。これが False で、 既にキューの中にあるメッセージ値を追加しようとした場合、そのメッセージは 追加されません。

session.pop_flash メソッドを使う

session.flash() API によって1つ以上のメッセージが一旦フラッシュキューに 追加されたら、それを使うために全キューをポップして返すのに session.pop_flash() API を使うことができます。

フラッシュオブジェクトから特定のメッセージキューをポップするには、 セッションオブジェクトの pop_flash メソッドを使用してください。 これは、フラッシュキューに追加されたメッセージのリストを返し、 キューを空にします。

pop_flash(queue='')
1
2
3
>>> request.session.flash('info message')
>>> request.session.pop_flash()
['info message']

上記のように session.pop_flash をそれに対応する session.flash への呼び出しなしで再び呼び出すと、空のリストが返ります。 これはキューが既にポップされているからです。

1
2
3
4
5
>>> request.session.flash('info message')
>>> request.session.pop_flash()
['info message']
>>> request.session.pop_flash()
[]

session.peek_flash メソッドを使う

session.flash APIによって1つ以上のメッセージが一度フラッシュキューに 追加されたら、そのキューを「盗み見る」ために session.peek_flash API を使うことができます。 session.pop_flash と異なり、キューは フラッシュストレージからポップされません。

peek_flash(queue='')
1
2
3
4
5
6
7
8
9
>>> request.session.flash('info message')
>>> request.session.peek_flash()
['info message']
>>> request.session.peek_flash()
['info message']
>>> request.session.pop_flash()
['info message']
>>> request.session.peek_flash()
[]

クロスサイトリクエストフォージェリ攻撃防御

クロスサイトリクエストフォージェリ 攻撃は、あるウェブサイトの id を持つユーザが別のウェブサイト上の URL またはボタンをクリックするときに起きる現象です。この攻撃は、高い特権が 必要な何らかのコマンドを実行するために、元のアプリケーションに対して ユーザを秘密裡にリダイレクトさせます。

フォーム送信によって起動される高い特権を要求するコード中のあらゆる アクションを行なう前に、正しい CSRF トークンPyramid セッションオブジェクトの中にセットされていることを確かめることで、 これらの攻撃のほとんどを回避することができます。 CSRF トークンサポートを使用するために、 デフォルトセッションファクトリを使う または 代替セッションファクトリを使う で述べられているように session factory を有効にしなければなりません。

session.get_csrf_token メソッドを使う

セッションから現在の CSRF トークンを得るには、 session.get_csrf_token メソッドを使用してください。

token = request.session.get_csrf_token()

session.get_csrf_token メソッドは引数を取りません。このメソッドは CSRF トークン 文字列を返します。もしこのセッション内でそれ以前に session.get_csrf_token または session.new_csrf_token が起動 されていれば、既存のトークンが返されます。もしこのセッション内でそれ以前に CSRF トークンが存在しなければ、新しいトークンがセッションにセットされ 返されます。新しく作成されたトークンは不透明 (opaque) でランダム化 (randomized) されています。

返されたトークンは、高い特権を必要とするメソッドに POST するフォームの 中で hidden フィールドの値として使用することができます。フォームの post ハンドラは、セッションからユーザに関連付けられた現在の CSRF トーク ンを得るために 再度 session.get_csrf_token を使用して、それを hidden フォームフィールドの値と比較するべきです。例えば、レンダリングされた フォームが csrf_token という名前の hidden 入力フィールドとして session.get_csrf_token によって得られた CSRF トークンを含んでいた場合:

1
2
3
token = request.session.get_csrf_token()
if token != request.POST['csrf_token']:
    raise ValueError('CSRF token did not match')

session.new_csrf_token メソッドを使う

新しい CSRF トークンを明示的にセッションに追加するには session.new_csrf_token メソッドを使用してください。これは、既存の CSRF トークンを消去して、新しい CSRF トークンを作成して、トークンを セッションにセットして、トークンを返す点のみが session.get_csrf_token と異なります。

token = request.session.new_csrf_token()