Flask-JWTを使用して、JWT認証を実装してみる

Web

この記事の内容

この記事では、JWT(JSON Web Token)を使用した認証を、Flask-JWTを使用して実装する方法を紹介します。

また、そもそもJWT認証とはどういうものであるのかを解説します。

JWTとは

JSONデータに署名や暗号化を施す方法を定めた規格で、RFC 7519で標準化されています。

JWTを用いることで、認証や認可が行えるようになります。

JWTの構成

JWTは「ヘッダ」、「ペイロード」、「署名」の3つのブロックからなります。

実際のJWTはBase64でエンコードしてそれらを.(ドット)でつないだものです。

署名はどのように作られるのか

改竄などを検知するために、「ヘッダ」と「ペイロード」部分を秘密鍵でハッシュ化した情報を署名とします。

これにより、JWTを発行した人のみが、あっているのかを検証することができます。

ペイロードについて

JWT取得時のシーケンス

クライアントからサーバ側に対して、JWTを取得する場合のシーケンス例を示します。

JWTを使用した、WebAPIのシーケンス

クライアントからサーバ側に対して、JWT認証が必要なWebAPIを呼び出す場合のシーケンスを示します。

Flask-JWT

Flask-JWTを使用した実装方法を紹介します。

Flask-JWTを使用するために、以下のコマンドでFlask-JWTをインストールします。

pip install Flask-JWT

シンプルな実装サンプルに関しては、以下のURLに記載があります。こちらのサンプルを使用して解説していきます。

Flask-JWT — Flask-JWT 0.3.2 documentation

 

サンプル

from flask import Flask
from flask_jwt import JWT, jwt_required, current_identity
from werkzeug.security import safe_str_cmp

class User(object):
    def __init__(self, id, username, password):
        self.id = id
        self.username = username
        self.password = password

    def __str__(self):
        return "User(id='%s')" % self.id

users = [
    User(1, 'user1', 'abcxyz'),
    User(2, 'user2', 'abcxyz'),
]

username_table = {u.username: u for u in users}
userid_table = {u.id: u for u in users}

def authenticate(username, password):
    user = username_table.get(username, None)
    if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')):
        return user

def identity(payload):
    user_id = payload['identity']
    return userid_table.get(user_id, None)

app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'super-secret'

jwt = JWT(app, authenticate, identity)

@app.route('/protected')
@jwt_required()
def protected():
    return '%s' % current_identity

if __name__ == '__main__':
    app.run()

サンプルコードの解説

Postmanで呼び出してみる

Postmanを使用して、サンプルで作成したJWT認証を使用してみます。

まずは、/authにPOSTメソッドを使用して、ユーザ名とパスワードを送信します。

すると、JWTを取得できていることがわかります。

取得したJWTを使用して、今度はJWTが必要な/protectedのページにアクセスしてみます。

HeadersのAuthorizationに取得したアクセストークンを以下の様に記載します。

すると、レスポンスが正常に取得できていることがわかります。

署名を1文字変えてみた結果は以下の様に401エラーとなり、署名が不正というエラーがかえってきます。

また、タイムアウト時間も設定することができます。

JWT_EXPIRATION_DELTAクレームを設定することによりタイムアウト時間を設定できます。(デフォルトは500Sec)

例えば30秒にするには以下の様に設定します。

app.config[‘JWT_EXPIRATION_DELTA’] = timedelta(seconds=30)

30秒経過後にアクセスすると、401エラーとなり、Signature has expiredとなります。

最後に

今回は、Flask-JWTを使用して、JWT認証を実装してみました。ライブラリを使用することで、簡単に実装することができたと思います。JWTに関しては、設定によっては脆弱性が生まれる可能性があるので、その点に関しては押さえておく必要があります。例えば、algクレームに署名無し (none) を使用することで不正なトークンを処理してしまうといったことがありますので、適切な情報収集をして使用することをおすすめいたします。

コメント