🚀

Webアプリを作成する際にJWTでセキュリティを強化してみた🚀

2023/07/23に公開1

やりたかったこと

パスワードを保存する際にJWTでトークンを発行してセキュリティを強化したかった

JWTとはなんぞや

JWTの概要

JWT(JSON Web Token)は、ウェブアプリケーションやAPIなどで認証や情報の安全性を確保するために広く使用されているトークンベースの認証手法で、トークンはJSON形式でエンコードされており、情報が署名されているため信頼性があり、改ざんを防ぎます。

JWTの基本構造

JWTは3つのセクション(ヘッダー、ペイロード、署名)から構成されています。

  1. ヘッダー(Header):
    • トークンのタイプや使用する署名アルゴリズムなどのメタデータを含んでいます。例えば、以下のようなJSON形式です。
    {
     "alg": "HS256", // 署名アルゴリズム (HMAC SHA-256)
     "typ": "JWT" // トークンのタイプ
    }
    
  2. ペイロード(Payload):
    • 実際に送信したい情報(クレーム)を含んでいます。例えば、ユーザーIDやロールなどを含めることができます。
    {
      "sub": "user123", // サブジェクト (Subject) : ユーザーID
      "name": "John Doe",
      "role": "admin"
    }
    
  3. 署名(Signature):
    • ヘッダーとペイロードを指定したアルゴリズム(例:HMAC SHA-256)で署名したものです。署名によってトークンの改ざんを防ぎます。

JWTの動作仕組み

JWTが何となくそういうものだんだなとなったところで
どうやって動いてんの?ですね

  1. 認証フローの開始:

    • ユーザーがログインなどの認証処理を行い、認証が成功した場合、サーバーはJWTを生成します。
  2. トークンの生成:

    • サーバーは、ヘッダー、ペイロード、署名を組み合わせてJWTを生成します。署名には秘密鍵が使われます。
  3. クライアントへの送信:

    • サーバーは生成されたJWTをクライアントに送信します。通常、レスポンスのヘッダーかクッキーに含まれます。
  4. リクエストの認証:

    • クライアントがサーバーにリクエストを送信する際に、JWTをリクエストヘッダーやクッキーに含めて送信します。
  5. トークンの検証:

    • サーバーは受け取ったJWTの署名を検証し、信頼性を確認します。署名が正しい場合、ペイロードの情報を使ってユーザーの認証や認可を行います。
  6. 必要に応じて再発行:

    • トークンの有効期限が切れた場合や、ログアウトなどのイベントが発生した場合には、再度JWTを生成してクライアントに送信します。

一応図も残しておきます。

こちらから引用させて頂きました
https://scrapbox.io/fendo181/JWT(JSON_Web_Token)を理解する。

実装方法

index.ts
import crypto from "crypto-js";
import User from "./src/models/user.js";
import jwt from "jsonwebtoken";

// ユーザー新規登録用API
app.post(
  "/register",
  async (req: Request, res: Response) => {
    let password: string = req.body.password;
    try {
      // パスワードの暗号化
      req.body.password = crypto.AES.encrypt(password, process.env.SECRET_KEY!);
      // ユーザーの新規作成
      const user = await User.create(req.body);
      // JWTの発行
      const token = jwt.sign({ id: user._id }, process.env.TOKEN_SECRET_KEY!, {
      //有効な時間
        expiresIn: "24h",
      });
      return res.status(200).json({ user, token });
    } catch (error: unknown) {
      console.log(error);
      return res.status(500).json(error);
    }
  }
);

JWTを発行したResponse(POSTMANとかで見れば楽かも)

{
    "user": {
        "username": "hiros",
        "password": "U2FsdGVkX1+GXJ1Y/5zBUWilAOpPCorTBVlEMY2Rqmo=",
        "_id": "64bbf808def3c6c3056e1a51",
        "__v": 0
    },
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY0YmJmODA4ZGVmM2M2YzMwNTZlMWE1MSIsImlhdCI6MTY5MDA0MDMyOCwiZXhwIjoxNjkwMTI2NzI4fQ.bOP5g_5-dvw_QCi7ZcQ8TGDYNLG07MCd_HXItBL0Xj4"
}

まとめ

  1. ユーザーが認証フローを開始し、認証が成功した場合、サーバーはJWTを生成します。
  2. サーバーは生成されたJWTをクライアントに送信します。
  3. クライアントはリクエスト時にJWTを送信し、サーバーはJWTの署名を検証して認証や認可を行います。
  4. トークンの有効期限が切れた場合やログアウトなどのイベントが発生した場合、再度JWTを生成してクライアントに送信します。

JWTの導入により、パスワードを保存する際のセキュリティが向上し、安全性が高まります。
ただし、トークンの適切な有効期限設定やセキュリティ上の注意点にも注意が必要かなと思います。
アプリの仕様や設計で決めていく感じだと。

JWTは現代のウェブアプリケーション開発において重要なセキュリティ手法であり、情報の安全性を確保するために積極的に活用されるべきですかね(既によく使われてるけど)

実装している箇所の抜粋だけでわからなければ僕のgitを見てもらえればと思います🙇‍♂️

Discussion

Hidden comment