アクセストークンを使用してAPIエンドポイントを保護するために、JWT(JSON Web Token)ベースの認証に頭を悩ませています。上記のリソースを取得するために、ユーザーに有効なアクセストークンを要求することで、これらのエンドポイントを保護しています。
ただし、リフレッシュトークンの使用を理解するのに苦労しています。
POST
リクエストを受け入れる/ auth/loginエンドポイントがあります。
class UserLogin(Resource):
def post(self):
data = parser.parse_args()
current_user = User.find_by_username(data['username'])
if not current_user:
return {'message': 'Unsuccessful. You do not have an account'}
if User.verify_hash(data['password'], current_user.password):
access_token = create_access_token(current_user)
refresh_token = create_refresh_token(current_user)
return {
'message': 'Login successful {}'.format(current_user.username),
'roles': 'Role: {}'.format(current_user.role),
'access token': access_token,
'refresh token': refresh_token
}
else:
return {'message': 'Something went wrong'}
エンドポイントは基本的に受信資格情報をチェックして、ユーザーがユーザーストアに存在することを確認します。サポートしている場合は、アクセスと更新トークンを返します。それ以外の場合は、クライアント側で処理されるエラーメッセージ。
ここで、ユーザーがログインし、アクセストークンを要求することで保護された別のエンドポイントからプライベートリソースにアクセスしようとしたとします。クライアントはリクエストを行い、保護されたエンドポイントは、アクセストークンがないことを示すエラーメッセージを返します。クライアントは、ログイン時に取得した更新トークンを使用して、新しいアクセストークンの生成を試みます。
class TokenRefresh(Resource):
@jwt_refresh_token_required
def post(self):
current_user = get_jwt_identity()
access_token = create_access_token(identity=current_user)
return {'access token': access_token}
上記のリソースにアクセスすると、有効期限が定義された新しいアクセストークンが生成されます。これが私の現在の流れです。
私の質問は、認証済みユーザーがアクセスできるようにするには、更新トークンをどのように保存すればよいですか?生成された更新トークンをその特定のユーザーに保存する追加のロジックをログインリクエストに含める必要がありますか?
その場合、クライアントがアクセストークンを更新しようとするたびに、新しい更新トークンを生成し、ユーザーにアタッチされている既存の更新トークンを上書きする必要がありますか?
現在、攻撃者が更新トークンを取得した場合、プロセスが危険にさらされる可能性があると感じています。
アクセストークンを持つことのポイントは、無効化をチェックせずに使用できることです。ユーザーがトークンを使用してアクセスできるフロントエンドサーバーを10000にすることができます。トークンが無効かどうかをデータベースに確認する必要はありません。しかし、しばらくすると、トークンの有効期限が切れます。ユーザーは新しいアクセストークンを必要とし、更新トークンを送信します。この更新トークンは、いくつかのデータベースでチェックされます。期限切れのアクセストークンをチェックしたり、状態を確認したりする必要はありませんが、悪用はトークンの有効期間に限定します。
データベースで有効期限を確認せずにトークンを受け入れる必要がない場合は、2つの異なるトークンは必要ありません。アクセスごとに更新トークンを使用できます。
ワークフローの例は次のとおりです。
ユーザーはログインし、アクセス権を取得してトークンを更新します。アクセストークンの有効期間15分、更新トークン5日。
ユーザーはアクセストークンを使用してサービスにアクセスします。サービスは署名と有効期間のみをチェックします。データベース接続がありません。
システム全体は、セッションを無効化するのにかかる時間と、必要な共有/同期データソースへの接続の量との間のトレードオフです。
更新のたびに更新トークンを置き換えることができますが、有効期限が切れるまですべての期限切れの更新トークンを保管する必要があることに注意してください。
セキュリティの観点からは、新しいトークンを作成することは理にかなっていますが、それはセキュリティとデータベース内のデータ量との間のトレードオフです。
最悪の場合(更新トークンの存続期間がない、neverそれを行う)代わりに、数分ごとに1つのトークンを各ユーザーのデータベースに保存する必要がありますユーザーごとに1つのトークンが割り当てられ、それらを再び削除することはできません。
更新トークンは、私の経験では、実践と理論が一致しないテクノロジの1つです。理論的には、ログインリクエストを作成し、アクセストークン(有効期間が短い)と更新トークン(有効期限が長く、有効期限がなく、いつでも新しいアクセストークンを取得するために使用できる)を取得する)。クライアントが期限切れのアクセストークンを送信しようとして、サーバーから拒否を受けた場合、クライアントは更新トークンを送信し、新しいアクセストークンを取得して、続行できます。
この場合、更新トークンが本当に強力であり、慎重に保存する必要があることは非常に明確です(たとえば、ブラウザーのCookieではなく、モバイルデバイスの資格情報ストレージに、または慎重に保護されたサーバー側ストアに)。ただし、この仕様では、認証サーバーによって更新トークンを無効にすることができます。
したがって、実際には、ログイン時に更新トークンが発行され、ログアウト時または非アクティブな期間が経過した後に無効化されることは珍しくありません。この場合、サーバー上のセッションデータの一部になる可能性があるため、自動的に現在のユーザーにリンクされます。他のユーザーは、認証されたセッション内からのみ更新トークンを使用できます。つまり、それを特定のユーザーにリンクするロジックも失敗します。
上記のコードスニペットから、これはほとんどあなたが持っているものです。たとえば、更新トークンは、アカウントの作成時に生成された後にストレージから取得されるのではなく、ログインごとに生成されます。欠落している唯一の部分は、リフレッシュトークンのクエリ方法にあります。これは、認証されたセッションとリフレッシュトークンの組み合わせではなく、リフレッシュトークンがアクセストークンを取得するのに十分であると見なされることを意味します。これは、ご想像のとおり、安全ではありません。
更新トークンは、新しいアクセストークンを取得するための承認を提供しますが、アクセストークンを要求する人がアクセス権を持つべき人であることを認証しません。承認を受け入れる前に認証ステップを提供する必要があります。これは、更新トークンが使用されるたびに使用されることを確認してください。開いているセッションで十分な場合があります。
すべての新しいアクセストークンで更新トークンを置き換えることを選択できます。これには、複数のユーザーがリフレッシュトークンを使用しようとすると(2番目のトークンが失敗して)、明らかに同時使用が制限されるという明らかな副作用があります。ただし、古いトークンを期限切れにすることなく新しい更新トークンを発行しないでください。これにより、トークンが侵害される可能性が高くなります。リフレッシュトークンの有効期限がセッションで切れる場合(セッションの有効期間が短いと想定)、メリットは限定的ですが、より長いセッション(「remember me」機能など)には役立ちます。