以下の答えは主に、(Webアプリケーションで使用される)セッションの概念の実装である署名付きCookieに関連しています。Flaskは、通常の(署名されていない)Cookie(およびを介した)および署名されたCookie(を介した)の両方を提供します。答えは2つの部分に分かれています。1つは署名付きCookieの生成方法を示し、2つ目はスキームのさまざまな側面に対応するQAの形式で提示されます。例で使用されている構文はPython3ですが、概念は以前のバージョンにも適用されます。request.cookies
response.set_cookie()
flask.session
何ですかSECRET_KEY
(または署名付きCookieを作成する方法)?
Cookieへの署名は、Cookieの改ざんに対する予防策です。Cookieへの署名プロセス中に、SECRET_KEY
は、「塩」を使用してパスワードをハッシュする前にパスワードを混乱させる方法と同様の方法で使用されます。これは、(非常に)単純化された概念の説明です。例のコードは、説明のためのものです。手順の多くは省略されており、すべての機能が実際に存在するわけではありません。ここでの目標は、一般的なアイデアを理解することです。実際の実装は、もう少し複雑になります。また、Flaskはこれのほとんどをバックグラウンドで実行することに注意してください。したがって、(セッションAPIを介して)Cookieに値を設定してを提供する以外にSECRET_KEY
、これを自分で再実装することはお勧めできませんが、そうする必要はありません。
貧乏人のクッキー署名
ブラウザに応答を送信する前に:
(1)最初にa SECRET_KEY
が確立されます。これはアプリケーションにのみ認識される必要があり、アプリケーションの再起動を含め、アプリケーションのライフサイクル中は比較的一定に保つ必要があります。
# choose a salt, a secret string of bytes
>>> SECRET_KEY = 'my super secret key'.encode('utf8')
(2)クッキーを作成する
>>> cookie = make_cookie(
... name='_profile',
... content='uid=382|membership=regular',
... ...
... expires='July 1 2030...'
... )
>>> print(cookie)
name: _profile
content: uid=382|membership=regular...
...
...
expires: July 1 2030, 1:20:40 AM UTC
(3)署名を作成SECRET_KEY
し、をCookieバイト文字列に追加(またはプリペンド)してから、その組み合わせからハッシュを生成します。
# encode and salt the cookie, then hash the result
>>> cookie_bytes = str(cookie).encode('utf8')
>>> signature = sha1(cookie_bytes+SECRET_KEY).hexdigest()
>>> print(signature)
7ae0e9e033b5fa53aa....
(4)次にcontent
、元のCookieのフィールドの一端に署名を貼り付けます。
# include signature as part of the cookie
>>> cookie.content = cookie.content + '|' + signature
>>> print(cookie)
name: _profile
content: uid=382|membership=regular|7ae0e9... <--- signature
domain: .example.com
path: /
send for: Encrypted connections only
expires: July 1 2030, 1:20:40 AM UTC
これがクライアントに送信されます。
# add cookie to response
>>> response.set_cookie(cookie)
# send to browser -->
ブラウザからCookieを受信すると、次のようになります。
(5)ブラウザーがこのCookieをサーバーに返すときに、Cookieのcontent
フィールドから署名を削除して、元のCookieを取得します。
# Upon receiving the cookie from browser
>>> cookie = request.get_cookie()
# pop the signature out of the cookie
>>> (cookie.content, popped_signature) = cookie.content.rsplit('|', 1)
(6)アプリケーションの元のCookieを使用しSECRET_KEY
て、手順3と同じ方法で署名を再計算します。
# recalculate signature using SECRET_KEY and original cookie
>>> cookie_bytes = str(cookie).encode('utf8')
>>> calculated_signature = sha1(cookie_bytes+SECRET_KEY).hexdigest()
(7)計算結果を、直前に受け取ったCookieから以前にポップした署名と比較します。それらが一致する場合、Cookieが乱されていないことがわかります。ただし、Cookieにスペースが追加されているだけでも、署名は一致しません。
# if both signatures match, your cookie has not been modified
>>> good_cookie = popped_signature==calculated_signature
(8)一致しない場合は、任意の数のアクションで応答したり、イベントをログに記録したり、Cookieを破棄したり、新しいCookieを発行したり、ログインページにリダイレクトしたりできます。
>>> if not good_cookie:
... security_log(cookie)
ハッシュベースのメッセージ認証コード(HMAC)
一部のコンテンツの整合性を保証するために秘密鍵を必要とする、上記で生成された署名のタイプは、暗号化においてメッセージ認証コードまたはMACと呼ばれます。
先ほど、上記の例はその概念の単純化し過ぎであり、独自の署名を実装することは良い考えではないと指定しました。それは、FlaskでCookieに署名するために使用されるアルゴリズムがHMACと呼ばれ、上記の単純なステップバイステップよりも少し複雑であるためです。一般的な考え方は同じですが、この説明の範囲外の理由により、一連の計算は少し複雑になります。DIYの作成にまだ興味がある場合は、通常そうですが、Pythonには、開始するのに役立ついくつかのモジュールがあります:)ここに開始ブロックがあります。
import hmac
import hashlib
def create_signature(secret_key, msg, digestmod=None):
if digestmod is None:
digestmod = hashlib.sha1
mac = hmac.new(secret_key, msg=msg, digestmod=digestmod)
return mac.digest()
hmacとhashlibのドキュメント。
SECRET_KEY
:) の「神秘化」
このコンテキストの「署名」とは何ですか?
これは、一部のコンテンツが、変更を許可された人またはエンティティ以外の誰かによって変更されていないことを確認するための方法です。
署名の最も単純な形式の1つは「チェックサム」で、2つのデータが同じであることを単純に検証します。たとえば、ソースからソフトウェアをインストールする場合、ソースコードのコピーが作成者のものと同一であることを最初に確認することが重要です。これを行う一般的な方法は、暗号化ハッシュ関数を介してソースを実行し、その出力をプロジェクトのホームページで公開されているチェックサムと比較することです。
たとえば、プロジェクトのソースをWebミラーからgzip圧縮ファイルでダウンロードしようとしているとします。プロジェクトのWebページで公開されているSHA1チェックサムは「eb84e8da7ca23e9f83 ....」です。
# so you get the code from the mirror
download https://mirror.example-codedump.com/source_code.tar.gz
# you calculate the hash as instructed
sha1(source_code.tar.gz)
> eb84e8da7c....
両方のハッシュは同じです。同じコピーを持っていることがわかります。
クッキーとは?
クッキーに関する広範な議論は、この質問の範囲を超えます。ここでは概要を説明します。最小限の理解は、どのように、そしてなぜSECRET_KEY
役立つのかをよりよく理解するのに役立ちます。HTTP Cookieに関する個人的な読み物をフォローアップすることを強くお勧めします。
Webアプリケーションでの一般的な方法は、クライアント(Webブラウザー)を軽量キャッシュとして使用することです。Cookieはこのプラクティスの1つの実装です。Cookieは通常、サーバーによってヘッダーを介してHTTP応答に追加されるデータです。これはブラウザによって保持され、リクエストを発行するときに、HTTPヘッダーを介してサーバーに送信されます。Cookieに含まれるデータは、いわゆるステートフルネスをエミュレートするために使用できます、サーバーがクライアントとの継続的な接続を維持しているような錯覚。この場合のみ、接続を「維持」するためのワイヤーの代わりに、クライアントの要求を処理した後のアプリケーションの状態のスナップショットを取得するだけです。これらのスナップショットは、クライアントとサーバーの間でやり取りされます。リクエストを受信すると、サーバーはまずCookieのコンテンツを読み取り、クライアントとの会話のコンテキストを再確立します。次に、そのコンテキスト内で要求を処理し、クライアントに応答を返す前に、Cookieを更新します。したがって、進行中のセッションの幻想が維持されます。
クッキーはどのように見えますか?
典型的なCookieは次のようになります。
name: _profile
content: uid=382|status=genie
domain: .example.com
path: /
send for: Encrypted connections only
expires: July 1 2030, 1:20:40 AM UTC
Cookieは、最新のブラウザから閲覧するのは簡単です。たとえば、Firefoxでは、[設定]> [プライバシー]> [履歴]> [個々のCookieを削除]に移動します。
このcontent
フィールドは、アプリケーションに最も関連しています。その他のフィールドには、さまざまな影響範囲を指定するためのメタ命令が含まれています。
なぜCookieを使用するのですか?
短い答えはパフォーマンスです。Cookieを使用すると、さまざまなデータストア(メモリキャッシュ、ファイル、データベースなど)を検索する必要性が最小限に抑えられるため、サーバーアプリケーション側の速度が向上します。Cookieが大きいほど、ネットワーク上のペイロードが重いため、サーバー上のデータベースルックアップに保存したものはネットワーク上で失われる可能性があることに注意してください。Cookieに何を含めるかを慎重に検討してください。
Cookieに署名する必要があるのはなぜですか?
Cookieはあらゆる種類の情報を保持するために使用されますが、その中には非常に機密性が高いものもあります。また、それらは本質的に安全ではなく、クライアントとサーバーの両方にとって、何らかの方法で安全であると見なされるように、いくつかの補助的な予防策を講じる必要があります。署名付きCookieは、サーバーアプリケーションをだまそうとする試みでいじられる可能性がある問題に特に対処します。他の種類の脆弱性を軽減するための他の方法があります。Cookieについて詳しく読むことをお勧めします。
Cookieを改ざんするにはどうすればよいですか?
Cookieはクライアントにテキスト形式で常駐し、簡単に編集できます。サーバーアプリケーションが受信したCookieは、いくつかの理由で変更されている可能性がありますが、その一部は無害ではない可能性があります。ユーザーに関する許可情報をCookieに保持し、その情報に基づいて特権を付与するWebアプリケーションを想像してみてください。Cookieがいじくり回されない場合は、誰でも変更してステータスを「role = visitor」から「role = admin」に昇格させることができ、アプリケーションは賢くなりません。
なぜSECRET_KEY
クッキーに署名する必要があるのですか?
Cookieの検証は、前述の方法でソースコードを検証する場合とは少し異なります。ソースコードの場合、元の作成者は、参照フィンガープリント(チェックサム)のトラスティと所有者であり、公開されます。信頼できないのはソースコードですが、公開署名は信頼できます。したがって、ソースのコピーを確認するには、計算されたハッシュをパブリックハッシュと一致させるだけです。
ただし、Cookieの場合、アプリケーションは署名を追跡せず、そのを追跡しますSECRET_KEY
。SECRET_KEY
参照フィンガープリントです。クッキーは合法であると主張する署名を付けて移動します。ここでの正当性とは、署名がCookieの所有者、つまりアプリケーションによって発行されたことを意味します。この場合、信頼できないと主張し、署名の有効性を確認する必要があります。そのためには、あなただけが知っている署名に要素を含める必要があります。それがSECRET_KEY
です。誰かがクッキーを変更する可能性がありますが、有効な署名を適切に計算するための秘密の要素がないため、偽装することはできません。少し前に述べたように、このタイプのフィンガープリントは、チェックサムに加えて秘密鍵も提供します。
セッションについてはどうですか?
従来の実装のセッションは、content
フィールド内のIDのみを運ぶCookieですsession_id
。セッションの目的は、署名付きCookieとまったく同じです。つまり、Cookieの改ざんを防ぐためです。クラシカルセッションでは、アプローチが異なります。セッションCookieを受信すると、サーバーはIDを使用して、データベース、ファイル、またはメモリ内のキャッシュなどの独自のローカルストレージでセッションデータを検索します。通常、セッションCookieは、ブラウザが閉じられたときに期限切れになるように設定されています。ローカルストレージのルックアップステップのため、このセッションの実装では、通常、パフォーマンスが低下します。署名付きCookieは推奨される代替手段になりつつあり、それがFlaskのセッションの実装方法です。つまり、Flaskセッションは署名付きCookie、およびFlaskで署名付きCookieを使用するには、そのSession
APIを使用するだけです。
なぜクッキーも暗号化しないのですか?
Cookieの内容は、署名される前に暗号化される場合があります。これは、機密性が高すぎてブラウザから表示できないと思われる場合に行われます(暗号化によってコンテンツが非表示になります)。ただし、単にCookieに署名するだけで、別のニーズに対応できます。つまり、ブラウザのCookieをある程度の可視性と使いやすさを維持しつつ、干渉されるのを防ぎたいというニーズがあります。
を変更するとSECRET_KEY
どうなりますか?
を変更すると、以前のキーで署名されたすべての Cookie SECRET_KEY
が無効になります。アプリケーションは、以前SECRET_KEY
ので署名されたCookieでリクエストを受け取ると、新しいSECRET_KEY
で署名を計算しようとしますが、両方の署名が一致せず、このCookieとそのすべてのデータは拒否されます。ブラウザは初めてサーバーに接続します。ユーザーはログアウトされ、古いCookieは内部に保存されているものとともに忘れられます。これは、期限切れのCookieの処理方法とは異なります。有効期限が切れたcookieは、署名がチェックアウトされた場合、リースが延長される可能性があります。無効な署名は、単純な無効なCookieを意味します。
したがって、署名されたすべてのCookieを無効にしたくない場合を除き、SECRET_KEY
長期間同じに保つようにしてください。
何がいいのSECRET_KEY
?
秘密鍵は推測しにくいものでなければなりません。セッションに関するドキュメントには、ランダムなキーを生成するための優れたレシピがあります。
>>> import os
>>> os.urandom(24)
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
キーをコピーして、構成ファイルにの値として貼り付けますSECRET_KEY
。
ランダムに生成されたキーを使用するのではなく、単語、数字、記号の複雑な組み合わせを使用し、おそらくあなただけが知っている文章にバイト形式でエンコードして配置できます。
呼び出されるたびに異なるキーを生成する関数でを直接設定しないでくださいSECRET_KEY
。たとえば、これを行わないでください。
# this is not good
SECRET_KEY = random_key_generator()
アプリケーションを再起動するたびに新しいキーが与えられるため、以前のキーは無効になります。
代わりに、インタラクティブなPythonシェルを開いて関数を呼び出し、キーを生成してから、コピーして構成に貼り付けます。