---
title: "セッションの使いかた"
version: 1.10
locale: ja
source: https://docs.djangoproject.com/ja/1.10/topics/http/sessions/
canonical: https://djangodocs.dev/ja/1.10/topics/http/sessions/
---
# セッションの使いかた

Django は、匿名のセッションをフルサポートしています。このセッションフレームワークを使えば、任意のデータを、サイトの訪問者ごとに保存し、取得することができます。データはサーバー側に保存され、クッキーの送受信によって抽象化します。クッキーには、データそのものではなくセッション ID が書かれています ( [クッキーベースのバックエンド](#cookie-session-backend) を使用しない限り)。

## セッションを有効にする

セッションは、 [ミドルウェア](/ja/1.10/ref/middleware/) の1つを使って実装されています。

セッションの機能を有効にするには、次のように設定を行います。

- [`MIDDLEWARE`](/ja/1.10/ref/settings/#std-setting-MIDDLEWARE) 設定を編集し `'django.contrib.sessions.middleware.SessionMiddleware'` を含むようにする。 `django-admin startproject` で作られるデフォルトの `settings.py` は `SessionMiddleware` が有効化されています。

セッションを使いたくない場合は、 [`MIDDLEWARE`](/ja/1.10/ref/settings/#std-setting-MIDDLEWARE) 内の `SessionMiddleware` 行と [`INSTALLED_APPS`](/ja/1.10/ref/settings/#std-setting-INSTALLED_APPS) 内の `'django.contrib.sessions'` を削除しても構いません。これで、オーバーヘッドが少しだけ短縮されます。

## セッションエンジンを設定する

デフォルトでは、Django はセッションを、(`django.contrib.sessions.models.Session` モデルを用いて) データベースに保存します。これは便利ですが、セットアップによってはセッションのデータを他の場所においたほうが高速化できる場合があります。そのため、Django はセッションデータをファイルシステムやキャッシュに保存するように設定することができます。

### データベースを使ったセッション

データベースを使った (database-backed) セッションを使いたい場合には、設定ファイルの [`INSTALLED_APPS`](/ja/1.10/ref/settings/#std-setting-INSTALLED_APPS)  に `'django.contrib.sessions'` を追加する必要があります。

一度インストールの設定をすれば、`manage.py migrate` を実行することで、セッションデータを保存する1つのデータベーステーブルをインストールできます。

### キャッシュを使ったセッション

よいパフォーマンスを発揮するには、キャッシュを使ったセッションバックエンドを利用した方が良いかもしれません。

Django のキャッシュシステムを使ってセッションデータを保存するには、まず初めに、キャッシュの設定を済ませておく必要があります。詳しくは、 [キャッシュのドキュメンテーション](/ja/1.10/topics/cache/) を読んでください。

> **Warning**
>
> キャッシュを使ったセッションを用いるのは、Memcached キャッシュバックエンドを使用している場合だけにするべきです。ローカルなメモリキャッシュバックエンドを使用している場合、長時間データをメモリ上に保持しておくのは良い考えではありませんし、全てのデータをファイルやデータベースキャッシュバックエンドを通して送信するよりも、ファイルやデータベースに保存されたセッションから直接送信した方が高速だからです。加えて、ローカルなメモリキャッシュバックエンドは、マルチプロセスに対して安全「ではありません」。したがって、実環境ではあまり良い選択とは言えないでしょう。

設定ファイルの [`CACHES`](/ja/1.10/ref/settings/#std-setting-CACHES) で複数のキャッシュを定義している場合、Django はデフォルトのキャッシュを使用します。他のキャッシュを使用するには、[`SESSION_CACHE_ALIAS`](/ja/1.10/ref/settings/#std-setting-SESSION_CACHE_ALIAS) を使用したいキャッシュの名前に変更します。

キャッシュの設定が完了したら、キャッシュへのデータの保管方法を、次の2つの選択肢から選ぶことができます。

- [`SESSION_ENGINE`](/ja/1.10/ref/settings/#std-setting-SESSION_ENGINE) を `"django.contrib.sessions.backends.cache"` に設定すると、シンプルなキャッシュセッションを保存します。セッションデータはキャッシュに直接保存されることになります。しかし、この場合、セッションデータが永続化されず、キャッシュがいっぱいになったりキャッシュサーバが再起動した際に、一度キャッシュされていたデータがクリアされてしまうことがあります。
- キャッシュデータを確実に永続化したい場合には、[`SESSION_ENGINE`](/ja/1.10/ref/settings/#std-setting-SESSION_ENGINE) を `"django.contrib.sessions.backends.cached_db"` に設定します。こちらは、書き込み透過な (write-through) キャッシュを使用します。つまり、キャッシュにデータが書き込まれるのと同時に、データベースにも同じデータが書き込まれます。セッションは、データがキャッシュ上から読み込めない時に限り、データベースからデータを読み込みます。

いずれのセッションのデータの保存も非常に高速ですが、シンプルなキャッシュを使用した方が、永続化の処理をしないので、より高速です。ほとんどのケースでは `cached_db` バックエンドでも十分高速ですが、パフォーマンスを最重視していて、セッションデータが時間経過で失われてもかまわない場合には、`cache` バックエンドを使用すると良いでしょう。

`cached_db` セッションバックエンドを使用する場合には、[using database-backed sessions](#using-database-backed-sessions) に書かれている手順にしたがって、設定を行ってください。

### ファイルを使ったセッション

ファイルを使ったセッションを使用するには、[`SESSION_ENGINE`](/ja/1.10/ref/settings/#std-setting-SESSION_ENGINE) を `"django.contrib.sessions.backends.file"` に設定します。

Django がセッションファイルを保存する場所を設定したい場合には、[`SESSION_FILE_PATH`](/ja/1.10/ref/settings/#std-setting-SESSION_FILE_PATH) を設定します (出力先のデフォルト値は、`tempfile.gettempdir()`、普通は `/tmp` になっています。)。Web サーバが設定した場所の読み書きの権限を持っていることを確認しておいてください。

### クッキーを使ったセッション

クッキーを使ったセッションを使用するには、 [`SESSION_ENGINE`](/ja/1.10/ref/settings/#std-setting-SESSION_ENGINE) を `"django.contrib.sessions.backends.signed_cookies"` に設定します。セッションデータは、 [暗号化署名](/ja/1.10/topics/signing/) のための Django のツールと [`SECRET_KEY`](/ja/1.10/ref/settings/#std-setting-SECRET_KEY) を使って保存されます。

> **Note**
>
> 保存されたデータに JavaScript からアクセスできないように、[`SESSION_COOKIE_HTTPONLY`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_HTTPONLY) を `True` に設定しておくことをおすすめします。

> **Warning**
>
> **SECRET\_KEY が知られてしまい、** [`PickleSerializer`](#django.contrib.sessions.serializers.PickleSerializer) を使用していた場合、**任意のリモートコードが実行可能になってしまいます。**
>
> [`SECRET_KEY`](/ja/1.10/ref/settings/#std-setting-SECRET_KEY) を知っている攻撃者は、偽造したセッションデータを生成して Django のサイトを騙すことができるのみならず、pickle を使ってシリアライズしたデータを送ることで、リモートから任意のコードを実行できてしまいます。
>
> クッキーを使ったセッションを使うときには、リモートからアクセスできるいかなるシステムに対しても秘密鍵が絶対に外に漏れないように、細心の注意を払ってください。
>
> **このセッションデータは、署名はされていますが、暗号化はされていません。**
>
> クッキーバックエンドを使う時には、セッションデータはクライアントが自由に読むことができます。
>
> MAC (Message Authentication Code; メッセージ認証コード) を使っているので、たとえクライアントがデータを勝手に書き変えてしまったとしても、書き換えられたセッションデータは無効と判定されます。しかし、たとえばミューザのブラウザが保存しているクッキーを保存できたとしても、セッションクッキーのデータがすべて保存できず、データの一部が失われることがあり、この場合にもセッションデータは無効となってしまいます。Django はデータを圧縮しますが、それでもデータのサイズが1クッキーごとの  [common limit of 4096 bytes](https://tools.ietf.org/html/rfc2965#section-5.3) を超えてしまうことは十分に考えられることです。
>
> **情報が最新であることが保証されません**
>
> MAC のおかげで、データの認証性 (データが他のサイトではなく、確実に自分のサイトで作られたものであること) と、完全性 (データが存在し、データが正しいこと) は保証されますが、そのデータが最新のものであることは保証することができません。つまり、クライアントからデータを受け取ったとしても、そのデータがユーザに最後に送った最新のデータであることは保証できないのです。ということは、セッションデータの使い方によっては、クッキーバックエンドでは  [replay attacks](https://en.wikipedia.org/wiki/Replay_attack) ができてしまうことになります。他のセッションバックエンドでは、サーバー側に各セッションの記録が残るので、ユーザがログアウトした時にそれを無効化できますが、クッキーを使ったセッションの場合には、ユーザがログアウトしてもセッションは無効化されません。したがって、攻撃者がユーザのクッキーを盗めば、たとえユーザがログアウトしていたとしても、そのクッキーを使ってユーザになりすますことができてしまいます。クッキーが「古くなった」と判断できるのは、[`SESSION_COOKIE_AGE`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_AGE) を過ぎた場合だけだからです。
>
> **パフォーマンス**
>
> 最後に、クッキーのサイズが大きくなると、 [speed of your site](http://yuiblog.com/blog/2007/03/01/performance-research-part-3/) にも大きな影響があります。

## ビューでセッションを使う

`SessionMiddleware` を有効にすれば、それぞれの [`HttpRequest`](/ja/1.10/ref/request-response/#django.http.HttpRequest) オブジェクト――Django のビュー関数に渡される最初の引数――は、ディクショナリライクなオブジェクトの `session` 属性を持つようになります。

この `request.session` には、ビューの好きな場所で、何度でも、読み書きを行うことができます。

#### `class backends.base.SessionBase`

このオブジェクトは、すべてのセッションオブジェクトのベースクラスとなっているので、以下に挙げるような基本的なディクショナリのメソッドを持っています。

#### `__getitem__(key)`

例: `fav_color = request.session['fav_color']`

#### `__setitem__(key, value)`

例: `request.session['fav_color'] = 'blue'`

#### `__delitem__(key)`

例: `del request.session['fav_color']` 与えられた `key` がセッション内にない場合には、`KeyError` 例外を起こします。

#### `__contains__(key)`

例: `'fav_color' in request.session`

#### `get(key, default=None)`

例: `fav_color = request.session.get('fav_color', 'red')`

#### `pop(key, default=__not_given)`

例: `fav_color = request.session.pop('fav_color', 'blue')`

#### `keys()`

#### `items()`

#### `setdefault()`

#### `clear()`

また、次のようなメソッドも利用できます。

#### `flush()`

セッションから現在のセッションデータを削除し、セッションクッキーを削除します。このメソッドは、前回のセッションデータがユーザのブラウザから再びアクセスされないようにするためなどに使います (たとえば、[`django.contrib.auth.logout()`](/ja/1.10/topics/auth/default/#django.contrib.auth.logout) がこの関数を呼びます)。

#### `set_test_cookie()`

ユーザのブラウザがクッキーをサポートしているかどうか判定するために、テスト用のクッキーをセットします。クッキーの動作原理により、ユーザが次のページへのリクエストを行わないとこのテストは行えません。詳しい情報については、下の [Setting test cookies](#setting-test-cookies) を読んでください。

#### `test_cookie_worked()`

ユーザのブラウザがテストクッキーを正しく保存したかどうかに応じて、`True` または `False` を返します。クッキーの動作原理により、あらかじめ別のページのリクエストとして `set_test_cookie()` を呼び出しておく必要があります。詳しい情報については、下の [Setting test cookies](#setting-test-cookies) を読んでください。

#### `delete_test_cookie()`

テストクッキーを削除します。クッキーをきれいにしておくために使ってください。

#### `set_expiry(value)`

セッションの有効期限を設定します。以下に挙げるようなさまざまな値を与えることができます。

- もし `value` が整数なら、与えられた秒数だけ活動がなかった時にセッションを破棄します。たとえば、`request.session.set_expiry(300)` と呼び出せば、5分後にセッションを破棄するように設定できます。
- もし `value` が `datetime` または `timedelta` オブジェクトならば、指定された日時に破棄されます。`datetime` と `timedelta` の値がシリアライズできるのは、[`PickleSerializer`](#django.contrib.sessions.serializers.PickleSerializer) を使っている場合のみであることに注意してください。
- もし `value` が `0` ならば、ユーザのセッションクッキーは、ユーザがウェブブラウザを閉じた時に破棄されます。
- もし `value` が `None` ならば、セッションはグローバルなセッション有効期限ポリシーにしたがって扱われます。

セッションの読み込みを行っても、有効期限は延長されません。セッションの有効期限は、セッションが最後に\*修正された\*時点を基に計算されます。

#### `get_expiry_age()`

このセッションの有効期限までの残りの秒数を返します。カスタムの有効期限を持たない (または、ブラウザを閉じた時とセットした) セッションの場合、この値は [`SESSION_COOKIE_AGE`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_AGE) と等しいです。

この関数は 2 つの省略可能なキーワード引数を取ることができます。

- `modification`: セッションを最後に修正した時刻を [`datetime`](https://docs.python.org/3/library/datetime.html#datetime.datetime) オブジェクトとして与える。デフォルトは現在の時刻。
- `expiry`: セッションの有効期限の情報を [`datetime`](https://docs.python.org/3/library/datetime.html#datetime.datetime) オブジェクト、[`int`](https://docs.python.org/3/library/functions.html#int) (秒数で)、または `None` として与えます。デフォルトの値は、もしあれば、セッションに保存されている [`set_expiry()`](#django.contrib.sessions.backends.base.SessionBase.set_expiry) で得られる値、なければ `None` です。

#### `get_expiry_date()`

このセッションが破棄される日付を返します。カスタムの有効期限を持たない (または、ブラウザを閉じた時とセットした) セッションに対しては、現在時刻から [`SESSION_COOKIE_AGE`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_AGE) の秒数までの日付に等しいです。

この関数は、[`get_expiry_age()`](#django.contrib.sessions.backends.base.SessionBase.get_expiry_age) と同じキーワード引数を取ることができます。

#### `get_expire_at_browser_close()`

ユーザのセッションクッキーがユーザのブラウザが閉じた時に破棄されるかどうかに応じて、`True` または `False` を返します。

#### `clear_expired()`

保存されているセッションから、有効期限が切れているものを削除します。このクラスメソッドは、[`clearsessions`](/ja/1.10/ref/django-admin/#django-admin-clearsessions) から呼び出されます。

#### `cycle_key()`

現在のセッションデータを保持したまま、新しいセッションキーを作成します。[`django.contrib.auth.login()`](/ja/1.10/topics/auth/default/#django.contrib.auth.login) は、このメソッドを呼び出すことで、過去のセッションを継続できるようにしています。

### セッションのシリアライズ

デフォルトでは、Django はセッションデータを JSON を用いてシリアライズします。設定ファイルの [`SESSION_SERIALIZER`](/ja/1.10/ref/settings/#std-setting-SESSION_SERIALIZER) に設定すれば、セッションをシリアライズするフォーマットをカスカムできます。[自作のシリアライザを書く](#custom-serializers) に書いた注意書きの通り、JSON によるシリアライズを強く推奨します。クッキーバックエンドを利用している場合は特にです。

たとえば、セッションデータをシリアライズするために [`pickle`](https://docs.python.org/3/library/pickle.html#module-pickle) を使用していた場合の、こんな攻撃のシナリオを考えてみましょう。あなたは [署名したクッキーセッションバックエンド](#cookie-session-backend) を使用していて、[`SECRET_KEY`](/ja/1.10/ref/settings/#std-setting-SECRET_KEY) が攻撃者に知られてしまっていたとします (もちろん、これは Django にキーが流出する脆弱性が内在しているというわけではありません)。この時、攻撃者は、セッションに任意の文字列を挿入することができてしまい、このセッションをアンピックすると、サーバ上で任意のコードが実行されてしまいます。このような攻撃は、インターネット上のどこからでも簡単にできてしまいます。クッキーセッションストレージは、クッキーに保存されているデータが勝手に書き換えられないようにクッキーに署名をしていますが、[`SECRET_KEY`](/ja/1.10/ref/settings/#std-setting-SECRET_KEY) が流出するだけで、リモートからコードを実行されてしまう脆弱性が生まれてしまうのです。

#### バンドルされているシリアライザ

#### `class serializers.JSONSerializer`

[`django.core.signing`](/ja/1.10/topics/signing/#module-django.core.signing) の JSON シリアライザのラッパーです。基本的なデータ型だけがシリアライズ可能です。

また、JSON は文字列のキーしかサポートしていないので、`request.session` に文字列以外のキーを使用してしまうと、次のように期待通りに動いてくれません。

```
>>> # initial assignment
>>> request.session[0] = 'bar'
>>> # subsequent requests following serialization & deserialization
>>> # of session data
>>> request.session[0]  # KeyError
>>> request.session['0']
'bar'
```

Similarly, data that can't be encoded in JSON, such as non-UTF8 bytes like
`'\xd9'` (which raises [`UnicodeDecodeError`](https://docs.python.org/3/library/exceptions.html#UnicodeDecodeError)), can't be stored.

JSON によるシリアライズの制限について詳しくは、[自作のシリアライザを書く](#custom-serializers) セクションを読んでください。

#### `class serializers.PickleSerializer`

任意の Python オブジェクトをサポートしますが、上に書いた理由により、このシリアライザを使用すると、攻撃者に [`SECRET_KEY`](/ja/1.10/ref/settings/#std-setting-SECRET_KEY) が知られた場合に、リモートからコードを実行される脆弱性を生んでしまいます。

#### 自作のシリアライザを書く

Note that unlike [`PickleSerializer`](#django.contrib.sessions.serializers.PickleSerializer),
the [`JSONSerializer`](#django.contrib.sessions.serializers.JSONSerializer) cannot handle
arbitrary Python data types. As is often the case, there is a trade-off between
convenience and security. If you wish to store more advanced data types
including `datetime` and `Decimal` in JSON backed sessions, you will need
to write a custom serializer (or convert such values to a JSON serializable
object before storing them in `request.session`). While serializing these
values is fairly straightforward
([`DjangoJSONEncoder`](/ja/1.10/topics/serialization/#django.core.serializers.json.DjangoJSONEncoder) may be helpful),
writing a decoder that can reliably get back the same thing that you put in is
more fragile. For example, you run the risk of returning a `datetime` that
was actually a string that just happened to be in the same format chosen for
`datetime`s).

シリアライザのクラスは、必ず次の2つのメソッドを実装しなければなりません。セッションデータのディクショナリをシリアライズする `dumps(self, obj)` と、それをデシリアライズする `loads(self, data)` の2つです。

### セッションオブジェクトのガイドライン

- Python の普通の文字列を、`request.session` におけるディクショナリのキーとして使うこと。これは慣習のためというよりも、確実で高速にするためのルールです。
- セッションのディクショナリのキーで、アンダースコアで始まるキーは、Django が内部で使用するために予約されているものと考えること。
- `request.session` を新しいオブジェクトで上書きせず、その属性にアクセスしたり、値をセットしたりしないこと。Python のディクショナリのように扱うこと。

### 例

次の簡単なビューは、ユーザがコメントを投稿した後で、`has_commented` 変数を `True` に設定します。こうすることで、同じユーザが2回以上コメントできないようにすることができます。

```
def post_comment(request, new_comment):
    if request.session.get('has_commented', False):
        return HttpResponse("You've already commented.")
    c = comments.Comment(comment=new_comment)
    c.save()
    request.session['has_commented'] = True
    return HttpResponse('Thanks for your comment!')
```

次の次の簡単なビューでは、サイトの「メンバー (member)」にログインする手続きを行います。

```
def login(request):
    m = Member.objects.get(username=request.POST['username'])
    if m.password == request.POST['password']:
        request.session['member_id'] = m.id
        return HttpResponse("You're logged in.")
    else:
        return HttpResponse("Your username and password didn't match.")
```

そして、次の例では、上の `login()` に対応するメンバーのログアウト処理を行います。

```
def logout(request):
    try:
        del request.session['member_id']
    except KeyError:
        pass
    return HttpResponse("You're logged out.")
```

ふつう実際に使われる [`django.contrib.auth.logout()`](/ja/1.10/topics/auth/default/#django.contrib.auth.logout) 関数では、データの意図しない漏洩を防ぐために、この例より少し複雑な処理、`request.session` の [`flush()`](#django.contrib.sessions.backends.base.SessionBase.flush) メソッドを呼び出すなどの処理を行っています。ここで挙げた例は、単にセッションオブジェクトの振る舞いをデモンストレーションすることが目的なので、完全ではない `logout()` を実装しました。

## テストクッキーを設定する

便利のため、Django はユーザのブラウザがクッキーを受け入れることができるかどうかをテストするための簡単な方法を提供しています。テストをするには、一度ビューの中で `request.session` の [`set_test_cookie()`](#django.contrib.sessions.backends.base.SessionBase.set_test_cookie) メソッドを呼び出してから、同じビューではなく、次に呼び出されるビューの中で、 [`test_cookie_worked()`](#django.contrib.sessions.backends.base.SessionBase.test_cookie_worked) メソッドを呼び出すだけで良いです。

このように `set_test_cookie()` と `test_cookie_worked()` を分離しなければならないのはちょっと気持ち悪いですが、クッキーの動作原理により、こうするより仕方がないのです。一度ブラウザにクッキーを設定しても、そのクッキーがブラウザに保存されたかどうかを確認するには、ブラウザがもう一度リクエストを行わなければならないからです。

テストクッキーがちゃんと機能していることを確認できたら、[`delete_test_cookie()`](#django.contrib.sessions.backends.base.SessionBase.delete_test_cookie) を使ってセッションをきれいにしておくことにしましょう。

以下に、テストクッキーの典型的な使用例を挙げます。

```
from django.http import HttpResponse
from django.shortcuts import render

def login(request):
    if request.method == 'POST':
        if request.session.test_cookie_worked():
            request.session.delete_test_cookie()
            return HttpResponse("You're logged in.")
        else:
            return HttpResponse("Please enable cookies and try again.")
    request.session.set_test_cookie()
    return render(request, 'foo/login_form.html')
```

## ビューの外でセッションを使う

> **Note**
>
> このセクションの例では、`SessionStore` オブジェクトを直接 `django.contrib.sessions.backends.db` バックエンドからインポートしています。しかし、実際に自分でコートを書く場合には、以下のようにして、[`SESSION_ENGINE`](/ja/1.10/ref/settings/#std-setting-SESSION_ENGINE) が指すセッションエンジンから `SessionStore` をインポートすることを考えるべきです。
>
> ```
> >>> from importlib import import_module
> >>> from django.conf import settings
> >>> SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
> ```

次のようにして、ビューの外からセッションデータを操作する API が利用可能です。

```
>>> from django.contrib.sessions.backends.db import SessionStore
>>> s = SessionStore()
>>> # stored as seconds since epoch since datetimes are not serializable in JSON.
>>> s['last_login'] = 1376587691
>>> s.create()
>>> s.session_key
'2b1189a188b44ad18c35e113ac6ceead'
>>> s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead')
>>> s['last_login']
1376587691
```

`SessionStore.create()` is designed to create a new session (i.e. one not
loaded from the session store and with `session_key=None`). `save()` is
designed to save an existing session (i.e. one loaded from the session store).
Calling `save()` on a new session may also work but has a small chance of
generating a `session_key` that collides with an existing one. `create()`
calls `save()` and loops until an unused `session_key` is generated.

`django.contrib.sessions.backends.db` バックエンドを使用している場合、各セッションは単なる Django のモデルになっていて、`Session` モデルは `django/contrib/sessions/models.py` で定義されています。普通のモデルにすぎないので、普通の Django データベース API からセッションにアクセスできます。

```
>>> from django.contrib.sessions.models import Session
>>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead')
>>> s.expire_date
datetime.datetime(2005, 8, 20, 13, 35, 12)
```

セッションのディクショナリを取得するには、[`get_decoded()`](#django.contrib.sessions.base_session.AbstractBaseSession.get_decoded) を呼ばなければならないことに注意してください。これが必要なのは、ディクショナリがエンコードされたフォーマットで保存されているためです。

```
>>> s.session_data
'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
>>> s.get_decoded()
{'user_id': 42}
```

## セッションが保存されるタイミング

デフォルトでは、Django がセッションデータベースへデータを保存するのは、セッションが修正された時だけ、つまり、ディクショナリ直下の値が代入または削除された時だけです。

```
# Session is modified.
request.session['foo'] = 'bar'

# Session is modified.
del request.session['foo']

# Session is modified.
request.session['foo'] = {}

# Gotcha: Session is NOT modified, because this alters
# request.session['foo'] instead of request.session.
request.session['foo']['bar'] = 'baz'
```

上の例の場合、セッションオブジェクトに修正したことを明示的に伝えるためには、`modified` 属性を設定すれば良いです。

```
request.session.modified = True
```

このデフォルトの動作を変更するには、設定ファイルの [`SESSION_SAVE_EVERY_REQUEST`](/ja/1.10/ref/settings/#std-setting-SESSION_SAVE_EVERY_REQUEST) を `True` に設定します。`True` に設定すると、Django は1リクエストごとに、セッションをデータベースに保存してくれるようになります。

セッションクッキーが送信されるのは、セッションの作成及び修正時のみであることに注意してください。[`SESSION_SAVE_EVERY_REQUEST`](/ja/1.10/ref/settings/#std-setting-SESSION_SAVE_EVERY_REQUEST) を `True` に設定すると、各リクエストごとにセッションクッキーが送信されるようになります。

同時に、セッションクッキーの `expires` 部分も、セッションクッキーが送信されるごとに更新されます。

レスポンスステータスコードが 500 の時には、セッションは保存されません。

## ブラウザ起動中のみ有効なセッション vs. 永続的なセッション

[`SESSION_EXPIRE_AT_BROWSER_CLOSE`](/ja/1.10/ref/settings/#std-setting-SESSION_EXPIRE_AT_BROWSER_CLOSE) 設定を使うと、セッションフレームワークが、ブラウザ起動中のみ有効なセッションと永続的なセッションのどちらを使うか設定できます。

デフォルトでは、[`SESSION_EXPIRE_AT_BROWSER_CLOSE`](/ja/1.10/ref/settings/#std-setting-SESSION_EXPIRE_AT_BROWSER_CLOSE) は `False` にセットされていて、セッションクッキーは [`SESSION_COOKIE_AGE`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_AGE) の期間ユーザのブラウザに保持されます。これにより、ユーザはブラウザを開くたびにログインし直さずに済みます。

[`SESSION_EXPIRE_AT_BROWSER_CLOSE`](/ja/1.10/ref/settings/#std-setting-SESSION_EXPIRE_AT_BROWSER_CLOSE) を `True` に設定すると、Django はブラウザ起動中のみ有効なクッキーを使用します。このクッキーは、ブラウザを閉じた瞬間に破棄されます。ユーザがブラウザを開くたびにログインするようにしたい場合には、この設定を利用してください。

この設定はグローバルなデフォルトですが、1セッションごとのレベルで設定を上書きすることができます。その場合には、上の [using sessions in views](#using-sessions-in-views) で書いたようにして、`request.session` の [`set_expiry()`](#django.contrib.sessions.backends.base.SessionBase.set_expiry) メソッドを呼び出してください。

> **Note**
>
> 一部のブラウザ (たとえば Chrome) では、ブラウザを閉じて再度開いてもセッションを継続できるようにする設定が提供されています。場合によっては、この設定が [`SESSION_EXPIRE_AT_BROWSER_CLOSE`](/ja/1.10/ref/settings/#std-setting-SESSION_EXPIRE_AT_BROWSER_CLOSE) 設定を妨げて、ブラウザが閉じてもセションが破棄されないことがあります。[`SESSION_EXPIRE_AT_BROWSER_CLOSE`](/ja/1.10/ref/settings/#std-setting-SESSION_EXPIRE_AT_BROWSER_CLOSE) を有効にした Django アプリケーションのテストをする時には、この点に注意してください。

## セッションストアのクリア

ユーザがウェブサイトで新しいセッションを作成した時、セッションデータはセッションストアに溜め込まれます。データベースのバックエンドを使っている場合には、`django_session` データベーステーブルが増大し、ファイルバックエンドを使っている場合には、一時ディレクトリのファイル数が増えてゆくでしょう。

この問題を理解するには、データベースバックエンドで起きていることを考えてみてください。ユーザがログインすると、Django は `django_session` データベースのテーブルに1行を追加します。Django はセッションのデータが変更されるたびに、この行を更新します。ユーザが手動でログアウトすると、Django はこの行を削除します。しかし、ユーザがログアウト\*しなかった\*場合には、この行は決して削除されません。同様のプロセスが、ファイルバックエンドの場合にも発生します。

Django は、有効期限の切れたセッションを自動的に削除する機能を提供\*しません\*。したがって、定期的に有効期限の切れたセッションを削除する仕事は、開発者の手に委ねられています。Django はこの目的のために、クリーンナップ用の管理コマンド [`clearsessions`](/ja/1.10/ref/django-admin/#django-admin-clearsessions) を用意しています。たとえば、cron の毎日のジョブに追加するなどの方法で、定期的にこのコマンドを実行することが推奨されています。

キャッシュバックエンドの場合はこの問題が発生しないことに注意してください。キャッシュの場合、不要なデータは自動的に削除されるようになっているからです。しかし、クッキーバックエンドの場合はそうではありません。セッションデータはユーザのブラウザに保存されるからです。

## 設定

以下の [Django の設定](/ja/1.10/ref/settings/#settings-sessions) を使うと、セッションの振る舞いをコントロールすることができます。

- [`SESSION_CACHE_ALIAS`](/ja/1.10/ref/settings/#std-setting-SESSION_CACHE_ALIAS)
- [`SESSION_COOKIE_AGE`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_AGE)
- [`SESSION_COOKIE_DOMAIN`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_DOMAIN)
- [`SESSION_COOKIE_HTTPONLY`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_HTTPONLY)
- [`SESSION_COOKIE_NAME`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_NAME)
- [`SESSION_COOKIE_PATH`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_PATH)
- [`SESSION_COOKIE_SECURE`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_SECURE)
- [`SESSION_ENGINE`](/ja/1.10/ref/settings/#std-setting-SESSION_ENGINE)
- [`SESSION_EXPIRE_AT_BROWSER_CLOSE`](/ja/1.10/ref/settings/#std-setting-SESSION_EXPIRE_AT_BROWSER_CLOSE)
- [`SESSION_FILE_PATH`](/ja/1.10/ref/settings/#std-setting-SESSION_FILE_PATH)
- [`SESSION_SAVE_EVERY_REQUEST`](/ja/1.10/ref/settings/#std-setting-SESSION_SAVE_EVERY_REQUEST)
- [`SESSION_SERIALIZER`](/ja/1.10/ref/settings/#std-setting-SESSION_SERIALIZER)

## セッションのセキュリティ

サイトのサブドメインからは、クライアントに対して、ドメイン全体に対するクッキーを設定することができます。信頼できるユーザに管理されていないサブドメインから送られたクッキーを許可している場合、この方法で session fixation 攻撃が可能になってしまいます。

たとえば、攻撃者が `good.example.com` にログインして、攻撃者のアカウントに対する有効なセッションを取得したとしましょう。この攻撃者が `bad.example.com` を自由に使えた場合、サブドメインからは `*.example.com` に対してクッキーを設定できるので、先ほど取得したセッションを利用して、攻撃者のセッションキーをターゲットの利用者に送信できてしまいます。次にこの利用者が `good.example.com` を訪問すると、この利用者は攻撃者のアカウントにログインすることになり、そのことに気付かずに大切な個人のデータ (たとえば、クレジットカード情報など) を攻撃者のアカウントに入力してしまう恐れがあります。

もう一つ考えられる攻撃は、`good.example.com` というサイトが [`SESSION_COOKIE_DOMAIN`](/ja/1.10/ref/settings/#std-setting-SESSION_COOKIE_DOMAIN) を `".example.com"` に設定していた場合に、そのサイトのセッションクッキーが `bad.example.com` に送信されてしまう、というものです。

## 技術的な詳細

- セッションのディクショナリは、[`JSONSerializer`](#django.contrib.sessions.serializers.JSONSerializer) を使用している場合には [`json`](https://docs.python.org/3/library/json.html#module-json) でシリアライズできるどんな値でも、[`PickleSerializer`](#django.contrib.sessions.serializers.PickleSerializer) を使用している場合には pickle でシリアライズできるどんな Python オブジェクトでも、保存することができます。
- セッションデータは、データベースの `django_session` という名前のテーブルに保存されます。
- Django は必要なときにだけクッキーを送信します。新しくセッションデータを設定しなければ、Django はセッションクッキーを送信しません。

### `SessionStore` オブジェクト

Django が内部でセッションを操作する時は、対応するセッションエンジンの session store オブジェクトを使用します。慣習により、session store オブジェクトは `SessionStore` と名付けられていて、[`SESSION_ENGINE`](/ja/1.10/ref/settings/#std-setting-SESSION_ENGINE) で指定したモジュール内に置かれています。

Django で利用可能なすべての `SessionStore` クラスは、[`SessionBase`](#django.contrib.sessions.backends.base.SessionBase) を継承していて、以下のデータを操作するメソッドを実装しています。

- `exists()`
- `create()`
- `save()`
- `delete()`
- `load()`
- [`clear_expired()`](#django.contrib.sessions.backends.base.SessionBase.clear_expired)

独自のセッションエンジンを作ったり、既存のものをカスタマイズするには、[`SessionBase`](#django.contrib.sessions.backends.base.SessionBase) か既存の `SessionStore` クラスを継承した新しいクラスを作る必要があるでしょう。

大部分のセッションエンジンでは、拡張は簡単にできますが、データベースを使ったセッションエンジンの場合は、一般に少し手を加える必要があります (詳しくは次のセクションを読んでください)。

## データベースを使ったセッションを拡張する

> **New in Django 1.9**

Django に含まれるデータベースを使ったセッションエンジン (具体的に言えば、`db` と `cached_db`) をカスタマイズするには、[`AbstractBaseSession`](#django.contrib.sessions.base_session.AbstractBaseSession) とともに `SessionStore` クラスを継承する必要があるかもしれません。

`AbstractBaseSession` と `BaseSessionManager` は、[`INSTALLED_APPS`](/ja/1.10/ref/settings/#std-setting-INSTALLED_APPS) に `django.contrib.sessions` を追加しなくても、`django.contrib.sessions.base_session` からインポートすることができます。

#### `class base_session.AbstractBaseSession`

> **New in Django 1.9**

ベースとなる抽象的なセッションのモデルです。

#### `session_key`

プライマリーキーです。フィールド自体は40文字までの文字列を格納できますが、現在の実装では、32文字の文字列 (数字と小文字のASCII文字のランダムな列) を生成します。

#### `session_data`

エンコード・シリアライズされた、セッションディクショナリーを含む文字列です。

#### `expire_date`

セッションの有効期限を表す datetime オブジェクトです。

有効期限が切れたセッションをユーザが利用することはできませんが、[`clearsessions`](/ja/1.10/ref/django-admin/#django-admin-clearsessions) 管理コマンドを実行するまでは、ふつうはデータベースに保存されています。

#### `classmethod get_session_store_class()`

セッションモデルで使用するセッションストアクラスを返します。

#### `get_decoded()`

デコードしたセッションデータを返します。

デコードは、セッションストアのクラスで実行されます。

[`BaseSessionManager`](#django.contrib.sessions.base_session.BaseSessionManager) のサブクラスを作ることで、モデルマネージャをカスタマイズすることもできます。

#### `class base_session.BaseSessionManager`

> **New in Django 1.9**

#### `encode(session_dict)`

与えられたセッションディクショナリを、シリアライズ・エンコードした文字列として返します。

エンコードは、モデルクラスに関連付けられたセッションストアのクラスで実行されます。

#### `save(session_key, session_dict, expire_date)`

与えられたセッションキーに対するセッションデータを保存します。データが空の場合には、セッションを削除します。

`SessionStore` クラスのカスタマイズは、以下に挙げるメソッドやプロパティをオーバーライドすることで行えます。

#### `class backends.db.SessionStore`

データベースを使ったセッションストアを実装しています。

#### `classmethod get_model_class()`

> **New in Django 1.9**

カスタマイズしたセッションモデルを返す必要がある場合には、このメソッドをオーバーライドします。

#### `create_model_instance(data)`

> **New in Django 1.9**

現在のセッションの状態を表す、セッションモデルオブジェクトの新しいインスタンスを返します。

このメソッドをオーバーライドすると、セッションモデルのデータをデータベースへ保存する前に修正することができます。

#### `class backends.cached_db.SessionStore`

キャッシュデータベースを使ったセッションストアを実装しています。

#### `cache_key_prefix`

> **New in Django 1.9**

キャッシュのキー文字列を作るためにセッションキーに追加するプリフィックスです。

### カスタマイズ例

The example below shows a custom database-backed session engine that includes
an additional database column to store an account ID (thus providing an option
to query the database for all active sessions for an account):

```
from django.contrib.sessions.backends.db import SessionStore as DBStore
from django.contrib.sessions.base_session import AbstractBaseSession
from django.db import models

class CustomSession(AbstractBaseSession):
    account_id = models.IntegerField(null=True, db_index=True)

    @classmethod
    def get_session_store_class(cls):
        return SessionStore

class SessionStore(DBStore):
    @classmethod
    def get_model_class(cls):
        return CustomSession

    def create_model_instance(self, data):
        obj = super(SessionStore, self).create_model_instance(data)
        try:
            account_id = int(data.get('_auth_user_id'))
        except (ValueError, TypeError):
            account_id = None
        obj.account_id = account_id
        return obj
```

If you are migrating from the Django's built-in `cached_db` session store to
a custom one based on `cached_db`, you should override the cache key prefix
in order to prevent a namespace clash:

```
class SessionStore(CachedDBStore):
    cache_key_prefix = 'mysessions.custom_cached_db_backend'

    # ...
```

## Session IDs in URLs

The Django sessions framework is entirely, and solely, cookie-based. It does
not fall back to putting session IDs in URLs as a last resort, as PHP does.
This is an intentional design decision. Not only does that behavior make URLs
ugly, it makes your site vulnerable to session-ID theft via the "Referer"
header.
