# Django 5.2.9 ドキュメント

# 暗号署名

ウェブアプリケーションのセキュリティの黄金律は、信頼できない情報源からのデータを決して信頼しないことです。時には、信頼されていない媒体を通してデータを渡すことが有用な場合もあります。暗号的に署名された値は、改ざんが検知されることを前提に、信頼されていない経路を安全に通過させることができます。

Django は、値の署名に関する低レベル API と、Web アプリケーションでの署名の最も一般的な用途の一つである署名付きクッキーの設定と読み取りに関する高レベル API の両方を提供しています。

また、以下のような場合にも署名が役に立つでしょう:

- パスワードを紛失したユーザーに送信する「アカウントの回復」URLの生成。

- 非表示のフォームフィールドに保存されたデータが改ざんされていないことの確認。

- 保護されたリソースへの一時的なアクセスを許可するためのワンタイムシークレットURLの生成。例えばユーザーが料金を支払ったダウンロード可能なファイルなど。

## `SECRET_KEY` と `SECRET_KEY_FALLBACKS` の保護

`startproject` によって新しいプロジェクトを作る際、 `settings.py` が生成され、ランダムな `SECRET_KEY` を得ます。この値が、署名されたデータを安全に保つ鍵になります。このキーを安全に保存するとこは極めて重要で、さもなければ攻撃を行うひとたちが彼らの署名を生成するのにも使えるからです。

`SECRET_KEY_FALLBACKS` は秘密鍵のローテーションに使用できます。この値はデータへの署名には使用されませんが、指定された場合は署名されたデータを検証するために使用されるので、安全に保つ必要があります。

## 低レベルの API を利用する

Django の署名メソッドは `django.core.signing` モジュールにあります。値に署名するには、まず `Signer` インスタンスを作成します。

    >>> from django.core.signing import Signer
    >>> signer = Signer()
    >>> value = signer.sign("My string")
    >>> value
    'My string:v9G-nxfz3iQGTXrePqYPlGvH79WTcIgj1QIQSUODTW0'

署名が文字列の最後にコロンに続いて付加されます。元の値を取得するには、`unsign` メソッドを使用します。

    >>> original = signer.unsign(value)
    >>> original
    'My string'

もし `sign` に文字列でない値を渡すと、その値は署名される前に強制的に文字列に変換され、`unsign` の結果にはその文字列の値が返されます。

    >>> signed = signer.sign(2.5)
    >>> original = signer.unsign(signed)
    >>> original
    '2.5'

リスト、タプル、辞書を保護したい場合は 、`sign_object()` と `unsign_object()` メソッドを使います。

    >>> signed_obj = signer.sign_object({"message": "Hello!"})
    >>> signed_obj
    'eyJtZXNzYWdlIjoiSGVsbG8hIn0:bzb48DBkB-bwLaCnUVB75r5VAPUEpzWJPrTb80JMIXM'
    >>> obj = signer.unsign_object(signed_obj)
    >>> obj
    {'message': 'Hello!'}

詳細は 複雑なデータ構造の保護 を参照してください。

何らかの理由により署名の値が書き換わってしまった場合、`django.core.signing.BadSignature` 例外が起こります。

    >>> from django.core import signing
    >>> value += "m"
    >>> try:
    ...     original = signer.unsign(value)
    ... except signing.BadSignature:
    ...     print("Tampering detected!")
    ...

デフォルトでは、`Signer` クラスは `SECRET_KEY` 設定の値を署名生成に利用します。次のように `Signer` のコンストラクタに任意の値を渡すことで、異なる秘密鍵を使用することもできます。

    >>> signer = Signer(key="my-other-secret")
    >>> value = signer.sign("My string")
    >>> value
    'My string:o3DrrsT6JRB73t-HDymfDNbTSxfMlom2d8TiUlb1hWY'

class Signer(*, key=None, sep=':', salt=None, algorithm=None, fallback_keys=None)

  Returns a signer which uses `key` to generate signatures and `sep` to separate values. `sep` cannot be in the URL safe base64 alphabet. This alphabet contains alphanumeric characters, hyphens, and underscores. `algorithm` must be an algorithm supported by `hashlib`, it defaults to `'sha256'`. `fallback_keys` is a list of additional values used to validate signed data, defaults to `SECRET_KEY_FALLBACKS`.

### `salt` 引数を使用する

特定の文字列が出現するたびに同じ署名ハッシュを使用したくない場合は、オプションで `Signer` クラスの引数 `salt` を使用できます。ソルトを使用すると、署名ハッシュ関数にソルトと `SECRET_KEY` の両方をシードにします:

    >>> signer = Signer()
    >>> signer.sign("My string")
    'My string:v9G-nxfz3iQGTXrePqYPlGvH79WTcIgj1QIQSUODTW0'
    >>> signer.sign_object({"message": "Hello!"})
    'eyJtZXNzYWdlIjoiSGVsbG8hIn0:bzb48DBkB-bwLaCnUVB75r5VAPUEpzWJPrTb80JMIXM'
    >>> signer = Signer(salt="extra")
    >>> signer.sign("My string")
    'My string:YMD-FR6rof3heDkFRffdmG4pXbAZSOtb-aQxg3vmmfc'
    >>> signer.unsign("My string:YMD-FR6rof3heDkFRffdmG4pXbAZSOtb-aQxg3vmmfc")
    'My string'
    >>> signer.sign_object({"message": "Hello!"})
    'eyJtZXNzYWdlIjoiSGVsbG8hIn0:-UWSLCE-oUAHzhkHviYz3SOZYBjFKllEOyVZNuUtM-I'
    >>> signer.unsign_object(
    ...     "eyJtZXNzYWdlIjoiSGVsbG8hIn0:-UWSLCE-oUAHzhkHviYz3SOZYBjFKllEOyVZNuUtM-I"
    ... )
    {'message': 'Hello!'}

Using salt in this way puts the different signatures into different namespaces. A signature that comes from one namespace (a particular salt value) cannot be used to validate the same plaintext string in a different namespace that is using a different salt setting. The result is to prevent an attacker from using a signed string generated in one place in the code as input to another piece of code that is generating (and verifying) signatures using a different salt.

`SECRET_KEY` とは異なり、salt 引数は秘密にする必要はありません。

### タイムスタンプ値の検証

`TimestampSigner` は `Signer` のサブクラスで、値に署名されたタイムスタンプを追加します。これにより、署名された値が指定された期間内に作成されたことを確認できます:

    >>> from datetime import timedelta
    >>> from django.core.signing import TimestampSigner
    >>> signer = TimestampSigner()
    >>> value = signer.sign("hello")
    >>> value
    'hello:1stLqR:_rvr4oXCgT4HyfwjXaU39QvTnuNuUthFRCzNOy4Hqt0'
    >>> signer.unsign(value)
    'hello'
    >>> signer.unsign(value, max_age=10)
    SignatureExpired: Signature age 15.5289158821 > 10 seconds
    >>> signer.unsign(value, max_age=20)
    'hello'
    >>> signer.unsign(value, max_age=timedelta(seconds=20))
    'hello'

class TimestampSigner(*, key=None, sep=':', salt=None, algorithm='sha256')
  sign(value)

    値 `value` に署名し、現在のタイムスタンプを追加します。

  unsign(value, max_age=None)

    Checks if `value` was signed less than `max_age` seconds ago, otherwise raises `SignatureExpired`. The `max_age` parameter can accept an integer or a `datetime.timedelta` object.

  sign_object(obj, serializer=JSONSerializer, compress=False)

    複雑なデータ構造（例えば、リスト、タプル、または辞書）をエンコードし、オプションで圧縮し、現在のタイムスタンプを追加し署名します。

  unsign_object(signed_obj, serializer=JSONSerializer, max_age=None)

    Checks if `signed_obj` was signed less than `max_age` seconds ago, otherwise raises `SignatureExpired`. The `max_age` parameter can accept an integer or a `datetime.timedelta` object.

### 複雑なデータ構造の保護

リスト、タプル、辞書を保護したい場合は、 `Signer.sign_object()` と `unsign_object()` メソッド、あるいは署名モジュールの `dumps()` や `loads()` 関数 (`TimestampSigner(salt='django.core.signing').sign_object()/unsign_object()` のショートカットです) を使って保護できます。これらは JSON シリアライズを使っています。JSONにすることで、 `SECRET_KEY` が盗まれても、攻撃者が pickle フォーマットを悪用して任意のコマンドを実行できないようにします:

    >>> from django.core import signing
    >>> signer = signing.TimestampSigner()
    >>> value = signer.sign_object({"foo": "bar"})
    >>> value
    'eyJmb28iOiJiYXIifQ:1stLrZ:_QiOBHafwucBF9FyAr54qEs84ZO1UdsO1XiTJCvvdno'
    >>> signer.unsign_object(value)
    {'foo': 'bar'}
    >>> value = signing.dumps({"foo": "bar"})
    >>> value
    'eyJmb28iOiJiYXIifQ:1stLsC:JItq2ZVjmAK6ivrWI-v1Gk1QVf2hOF52oaEqhZHca7I'
    >>> signing.loads(value)
    {'foo': 'bar'}

JSONの性質上（ネイティブではリストとタプルの区別がありません）、タプルを渡すと `signing.loads(object)` からリストが返されます:

    >>> from django.core import signing
    >>> value = signing.dumps(("a", "b", "c"))
    >>> signing.loads(value)
    ['a', 'b', 'c']

dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer, compress=False)

  URL セーフな、署名付き base64 圧縮 JSON 文字列を返します。シリアライズされたオブジェクトは `TimestampSigner` を使って署名されます。

loads(string, key=None, salt='django.core.signing', serializer=JSONSerializer, max_age=None, fallback_keys=None)

  `dumps()` の逆で、署名に失敗した場合に `BadSignature` を返します。指定された場合は `max_age` (秒単位) をチェックします。