1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JWTが最近使われている理由とjsonwebtoken

Last updated at Posted at 2025-06-01

JWTとは

Web開発において認証・認可の仕組みは避けて通れない重要な要素です。その中でもJWT(JSON Web Token)は、現代のWeb APIやSPA(Single Page Application)で広く使われている認証トークンの標準規格です。
JWTは「ジョット」と読むそうです。


EメールとパスワードでログインするとWebアプリからユーザーにJWTが発行されます。

そのJWTにより、Webアプリがログインユーザーとわかり、なかったらログイン画面へ遷移させる。という処理を行うことができます。

JWTはログインしたらもらえる、テーマパークのチケットみたいなものとイメージしていただけるとわかりやすいと思います。

image.png

なぜJWTが使われるのか

JWTって最近聞きますが、ログインといえばセッションというイメージがある方もいるかと思います。Reactなどで作られるSPAのサイトではJWTが利用されています。

従来のセッション認証とJWT認証の違いをまとめてみます。

coin_bronze_douka.png

従来のセッション認証との違い

従来のセッション認証

  • サーバー側でセッション情報を保持
  • クッキーにセッションIDを保存
  • サーバーがセッションストレージを管理する必要がある

JWT認証

  • トークン自体に必要な情報を含む
  • サーバー側でセッション情報を保持しない(ステートレス)
  • クライアント側でトークンを管理

JWT認証が普及した背景と理由

従来のセッション認証では、サーバー側でユーザーのログイン状態を管理する必要がありました。これはサーバーにメモリやデータベースでセッション情報を保持することを意味し、サーバーの負荷となっていました。

JWTでは、認証に必要な情報をトークン自体に含めるため、サーバー側でセッション情報を保持する必要がありません。この「ステートレス」な特性により、サーバーの運用が大幅に簡素化されます。

また、SPAではページ遷移がないため、従来のセッション管理では認証状態の維持が困難になります。各画面遷移でサーバーにセッション確認をする必要がなく、クライアント側で認証状態を自己完結的に管理できるJWTは、SPAのアーキテクチャに非常に適しています。

JWTは必要な認証情報をトークン内に含むため、「自己完結」しています。サーバーはトークンを受け取った時点で、データベースに問い合わせることなく、そのトークンが有効かどうか、どのユーザーのものかを判断できます。これにより、認証処理の高速化とデータベースへの負荷軽減を実現できます。

keiyaku_contract.png

JWTは標準化された仕様に基づいているため、Web、モバイルアプリ、デスクトップアプリなど、異なるプラットフォーム間で同じ認証方式を使用できます。

そのため、最近はJWT認証が広く使われるようになりました。
JWTという技術自体は2015年ごろに標準化されたようですが、実際に一般的な技術として広く採用されるようになったのは、ここ5年程度の話です。

syokuji_computer.png

JWTの構造

JWTは3つの部分がピリオド(.)で区切られた文字列です:

ヘッダー.ペイロード.署名

1. ヘッダー(Header)

署名アルゴリズムとトークンタイプを指定します。

{
  "alg": "RS256",
  "typ": "JWT"
}

2. ペイロード(Payload)

トークンに含める情報(クレーム)を定義します。

{
  "jti": "1daf94f0-42c7-4338-a721-e232cceefc4a202505870026",
  "iss": "test001.co.jp",
  "sub": "user_12345",
  "aud": "https://api.example.com",
  "exp": 1748399594,
  "iat": 1748395994,
  "role": "admin",
  "permissions": ["read", "write", "delete"]
}

標準クレーム(Registered Claims)

クレーム 説明
iss 発行者(Issuer) "myapp.co.jp"
sub 主体(Subject) "user_12345"
aud 対象者(Audience) "https://api.example.com"
exp 有効期限(Expiration Time) 1748399594
nbf 有効開始時刻(Not Before) 1748395994
iat 発行時刻(Issued At) 1748395994
jti JWT ID "unique-token-123"

カスタムクレーム

アプリケーション固有の情報を含めることができます。

{
  "user_id": "12345",
  "username": "john_doe",
  "role": "admin",
  "permissions": ["read", "write", "delete"],
  "organization_id": "org_456"
}

3. 署名(Signature)

トークンの真正性と完整性を保証します。

RSASHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  private_key
)

JWTは署名によって改ざん検出が可能な仕組みになっています。
ヘッダーやペイロードの内容が一文字でも変更されると、署名の検証が失敗するため、改ざんを確実に検出できます。

サーバーは署名を検証することで、トークンが改ざんされていないことを確認し、そのJWTを持つユーザーが正当にログインしたユーザーであることを安全に認識できます。
一致しない場合は、改ざんまたは不正なトークンとして判断することができます。

nigaoe_eratosthenes.png

アルゴリズム

署名を生成・検証するアルゴリズムには種類があります。
いくつか代表的なものをご紹介します。

HMACアルゴリズム(共通鍵)
  • 共通鍵。同じキーで署名と検証をする
  • 高速で軽量
  • シンプルに実装できる
HS256
const token = jwt.sign(payload, sharedSecret, { algorithm: 'HS256' });
jwt.verify(token, sharedSecret); // 同じ鍵
RSAアルゴリズム(公開鍵と秘密鍵)
  • 秘密鍵で署名。公開鍵で検証をする
  • 公開鍵を安全に配布できる
  • HMACより処理が重い
  • レガシーシステムとの互換性
RSA256
// 署名(秘密鍵)
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });

// 検証(公開鍵)
jwt.verify(token, publicKey, { algorithm: 'RS256' });
ECDSAアルゴリズム(公開鍵と秘密鍵)
  • 秘密鍵で署名。公開鍵で検証をする
  • 公開鍵を安全に配布できる
  • RSAより高速
  • 楕円曲線暗号
  • RSAより小さなキーサイズで同等のセキュリティ
ES256
// 署名(秘密鍵)
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });

// 検証(公開鍵)
jwt.verify(token, publicKey, { algorithm: 'RS256' });

アルゴリズムによって、処理に時間がかかったり、 セキュリティ面で弱くなったりとメリット・デメリットはあります。 アプリケーションによって適したアルゴリズムを選択することが必要です。

セキュリティ考慮事項

実はJWTはBASE64でエンコードされているだけで、暗号化されていません。
つまり、誰でもペイロード部分をみることできます。

RSA256などのアルゴリズムは改ざんされていないことを確認するための署名に使用されます。
ペイロードにパスワードを書いたりするのは危険です。

ペイロードのデコード

BASE64をデコードするにはatob()関数を使えば簡単にできます。(ASCII文字の場合)

https://jwt.io/の初期設定にあるJWTをデコードしてみましょう。

JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWTはBASE64にエンコードされたテキストが.(ピリオド)でつながっています。
ペイロードは2つ目なので、それをデコードしてみます。

ペイロード
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
デコードテスト
const payload = atob("eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ");
console.log(payload);

// 結果
'{"sub":"1234567890","name":"John Doe","iat":1516239022}'

デコードされました。
JWTが暗号化されているわけではなく、簡単に情報を見ることができるとお分かりいただけたかと思います。

alg=none攻撃

JWTは秘密鍵を知らない攻撃者が、ヘッダーやペイロードを改ざんしても検証時にエラーになりますが、JWTのヘッダーに記載されたアルゴリズムをalg=noneとすることで、署名なしでトークンを受け入れさせる攻撃があります。

{
    "typ": "JWT",
    "alg": "none"
}

現在はライブラリなどでは、対応が取られていますが(と思われますが。。)古いバージョンの場合、攻撃が成功してしまう可能性があります。

他にも様々な攻撃手法があり、セキュリティへの攻撃は常に進化しているので、

  • ライブラリのバージョンを更新する
  • 検証時のオプションでアルゴリズムを明示して検証する
  • 有効期限を長くしすぎない
  • 強い鍵を使う

ことなども重要です。

computer_hacker_black1.png

jsonwebtoken

ここからはJWTのライブラリjsonwebtokenの使い方を解説していきます。
jsonwebtokenは、JWT(JSON Web Token)の生成、署名、検証を簡単に行えるNode.js用のライブラリです。

JWTのライブラリはjsonwebtoken以外にもたくさんの種類があります。
https://jwt.io/libraries

インストール

npm install jsonwebtoken

基本的な使い方

jsonwebtokenでできることは、JWTの生成と検証、デコードです。

トークンの生成

jwt.sign(payload, secretOrPrivateKey, [options, callback]);

JWTを生成します。
署名のアルゴリズムはデフォルトでHS256が使用されます。
RSA256などのアルゴリズムを使用する場合はオプションに明記します。

const token = jwt.sign(payload, privateKey, { alogorithim: 'RSA256'});

アルゴリズム以外にもオプションを付与することで、セキュリティの向上が期待できます。

有効期限を指定する
const token = jwt.sign(
  { userId: 123 }, 
  'secret', 
  { expiresIn: '1h' }  // ← 1時間に指定
);

// ペイロード
// { userId: 123, iat: 1689675632, exp: 1689679232 }
オプション 説明
audience トークンの対象者を指定
issuer トークンの発行者を指定
jwtid トークンの一意識別子
subject トークンの主体(通常はユーザーID)
expiresIn トークンの有効期限
notBefore トークンの有効開始時刻
algorithm 署名アルゴリズム
noTimestamp iatクレームを含めない
header JWTヘッダーにカスタム情報追加
keyid ヘッダーのkid設定のショートカット
mutatePayload 元のペイロードオブジェクトを変更
allowInsecureKeySizes 2048未満の係数を持つ小さな鍵サイズを許可
allowInvalidAsymmetricKeyTypes 非対称鍵の型チェック無効化

トークンの検証

jwt.verify(token, secretOrPublicKey, [options, callback]);

JWTを検証します。
verify()の返り値は真偽値ではなく、デコードされたペイロードになります。

const decoded = jwt.verify(token, 'secret');
console.log(decoded);
// 出力: { userId: 123, role: 'user', iat: 1689675632 }

検証のアルゴリズムはデフォルトでは、そのJWTから読み取ったアルゴリズムが使用されます。
アルゴリズムを明示する場合は、オプションに明記します。

アルゴリズムを明記
// 特定のアルゴリズムのみ許可
jwt.verify(token, secret, { algorithms: ['HS256'] });

// 複数のアルゴリズムを許可
jwt.verify(token, publicKey, { algorithms: ['RS256', 'RS384'] });

アルゴリズム以外にもオプションを付与することで、セキュリティの向上が期待できます。

jwt.verify(token, secret, {
  algorithms: ['RS256'], //アルゴリズム
  issuer: 'qiita', //発行者
  audience: 'qiita-users', //対象者
  maxAge: '1h' //有効期限
});
オプション 説明
algorithms 許可するアルゴリズムのリスト`
audience 検証する対象者(aud クレーム)
issuer 検証する発行者(iss クレーム)
subject 検証する主体(sub クレーム)
jwtid 検証するJWT ID(jti クレーム)
clockTolerance 時刻のずれ許容範囲(秒)
maxAge トークンの最大有効期間
clockTimestamp 検証時の基準時刻を指定
ignoreExpiration 有効期限を無視するか
ignoreNotBefore notBefore クレームを無視するか
complete ヘッダー情報も含めて返すか
nonce OpenID Connect の nonce 検証
allowInvalidAsymmetricKeyTypes 非対称鍵の型チェック無効化

トークンのデコード

jwt.decode(token [, options])

JWTトークンを検証せずにデコードします。

const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMywiZW1haWwiOiJ1c2VyQGV4YW1wbGUuY29tIn0.signature';

const decoded = jwt.decode(token);
console.log(decoded);
// 結果
{ userId: 123, email: 'user@example.com', iat: 1689675632 }

ヘッダーや署名も含んだ情報を取得したい場合はcompleteオプションをつけると取得できます。

token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30";

jwt.decode(token, { complete: true }));

// 結果
{
  header: { alg: 'HS256', typ: 'JWT' },
  payload: { sub: '1234567890', name: 'John Doe', admin: true, iat: 1516239022 },
  signature: 'KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30'
}

基本的に使用するのはsign()とverify()だけになるかと思います。
decode()の結果だけで認証するのは署名検証なしで認証してしまうため、危険です。

まとめ

  • JWTは認証で使われるトークンの標準規格
  • ステートレスのため、サーバー側で認証状態を保持する必要がない
  • JWTは異なるプラットフォームでの同じ認証方式を使用できる
  • JWTはヘッダーとペイロード、署名で構成される
  • JWT自体は暗号化されていない
  • JWTを生成するときはsign()、検証するときはvefify()、デコードするだけはdecord()
  • オプションをつけてセキュリティを強くしよう

実際にログイン機能を実装する場合 bcryptなどでハッシュ化したパスワードをデータベースに保存し、認証できたら、JWTをユーザーに渡し認証・認可を可能にすることになります。

JWTを正しく理解し、適切に実装することで、安全で効率的な認証システムを構築できます。

sign_shikishi.png

参考サイト

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?