# Django 5.0.9.dev20240806133106 ドキュメント

# 暗号署名

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

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:GdMGD6HNQ_qdgxYP8yBZAdAIV1w'

署名が文字列の最後にコロンに続いて付加されます。元の値を取得するには、`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:Xdc-mOFDjs22KsQAqfVfi8PQSPdo3ckWJxPWwQOFhR4'
    >>> 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:EkfQJafvGyiofrdGnuthdxImIJw'

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

  署名の生成に `key` を使用し、値の区切りに `sep` を使用する signer （署名器）を返します。 `sep` には URL セーフな base64 英数字 を使用することはできません。このアルファベットには英数字、ハイフン、アンダースコアが含まれます。 `algorithm` は `hashlib` がサポートするアルゴリズムでなければなりません。デフォルトは `'sha256'` です。`fallback_keys` は署名されたデータを検証するために使用する追加の値のリストです。デフォルトは `SECRET_KEY_FALLBACKS` です。

  バージョン 4.2 で非推奨: 位置引数のサポートは非推奨になりました。

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

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

    >>> signer = Signer()
    >>> signer.sign("My string")
    'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w'
    >>> signer.sign_object({"message": "Hello!"})
    'eyJtZXNzYWdlIjoiSGVsbG8hIn0:Xdc-mOFDjs22KsQAqfVfi8PQSPdo3ckWJxPWwQOFhR4'
    >>> signer = Signer(salt="extra")
    >>> signer.sign("My string")
    'My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw'
    >>> signer.unsign("My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw")
    'My string'
    >>> signer.sign_object({"message": "Hello!"})
    'eyJtZXNzYWdlIjoiSGVsbG8hIn0:-UWSLCE-oUAHzhkHviYz3SOZYBjFKllEOyVZNuUtM-I'
    >>> signer.unsign_object(
    ...     "eyJtZXNzYWdlIjoiSGVsbG8hIn0:-UWSLCE-oUAHzhkHviYz3SOZYBjFKllEOyVZNuUtM-I"
    ... )
    {'message': 'Hello!'}

この方法でソルトを使用すると、異なる署名が異なる名前空間に配置されます。 ある名前空間（特定のソルト値）に由来する署名は、異なるソルト設定を使用している別の名前空間で、同じ平文文字列を検証するためには使用できません。その結果、攻撃者がコードのある場所で生成された署名付き文字列を、異なるソルトを使用して署名を生成（検証）している別のコードの入力として使うことを防止できます。

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

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

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

    >>> from datetime import timedelta
    >>> from django.core.signing import TimestampSigner
    >>> signer = TimestampSigner()
    >>> value = signer.sign("hello")
    >>> value
    'hello:1NMg5H:oPVuCqlJWmChm1rA2lyTUtelC-c'
    >>> 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)

    値が `max_age` 秒前より後に署名されたことを確認します。パラメータ `max_age` には整数または `datetime.timedelta` オブジェクトを指定します。

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

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

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

    値が `signed_obj` 秒前より後に署名されたことを確認します。パラメータ `max_age` には整数または `datetime.timedelta` オブジェクトを指定します。

  バージョン 4.2 で非推奨: 位置引数のサポートは非推奨になりました。

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

リスト、タプル、辞書を保護したい場合は、 `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:1kx6R3:D4qGKiptAqo5QW9iv4eNLc6xl4RwiFfes6oOcYhkYnc'
    >>> signer.unsign_object(value)
    {'foo': 'bar'}
    >>> value = signing.dumps({"foo": "bar"})
    >>> value
    'eyJmb28iOiJiYXIifQ:1kx6Rf:LBB39RQmME-SRvilheUe5EmPYRbuDBgQp2tCAi7KGLk'
    >>> 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` (秒単位) をチェックします。