Skip to main content

SnykはJWTのセキュリティ問題を検出できる?

著者
blog feature ai green

2025年3月4日

0 分で読めます

従来のCookieベースのセッションではなく、認証の実装にJWTを利用する開発者は少なくありません。しかし、JWTのセキュリティに対する理解が不十分な場合、深刻な認証機能の不備につながる脆弱性が発生する可能性があります。

JWTとは?

JSON Web Token(JWT)は、JSONオブジェクトとして当事者間で情報を安全に送信するための、コンパクトで自己完結型の仕組みです。多くの場合、証明書などを用いてデジタル署名されるため、情報の検証と信頼性の確認が可能です。アプリケーションに認証を追加し、従来のブラウザーベースのCookieによるセキュリティやセッション管理を避けたいとき、JWTはWeb開発者にとって日常的に使われる選択肢です。

JWTの構造

前述のとおり、JWTはデータを格納する一般的なJSONオブジェクトです。JWTはヘッダー、ペイロード、署名で構成されます。各要素をBase64でエンコードし、ドット(.)で連結してJWT文字列を作成します。

1. ヘッダー:通常、ヘッダーは2つの要素で構成されます。トークンの種類(JWT)と、HMAC SHA256やRSAなど、使用する署名アルゴリズムです。

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

2. ペイロード:ペイロードにはクレームが含まれます。クレームとは、エンティティ(通常はユーザー)に関する記述や追加データです。ただし、機密情報や秘匿情報を格納しないよう注意してください。JWTは暗号化されていないことが多く、誰でもデコードしてデータにアクセスできるためです。クレームには登録済み、パブリック、プライベートの3種類があります。

{
     "sub": "1234567890",
     "name": "John Doe",
     "admin": true
   }

3. 署名:署名を作成するには、エンコード済みのヘッダーとペイロード、シークレット、ヘッダーで指定されたアルゴリズムを使って署名します。

 HMACSHA256(
     base64UrlEncode(header) + "." +
     base64UrlEncode(payload),
     secret)

JWTは次のような形式になります。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

APIとマイクロサービスにおけるJWTの役割

フロントエンドとバックエンドの開発者は、認証を実装し、APIやマイクロサービスを構築するうえでJWTを重要な要素として活用しています。サーバーがデータベースを通じて永続的なセッション状態を保持する従来のセッションベース認証とは異なり、JWTはステートレスであるため、よりスケーラブルで効率的な方法を実現できます。ユーザーがログインすると、サーバーはユーザー情報をエンコードしたトークンを生成し、シークレットキーで署名します。そのトークンはクライアントに送信され、以降のリクエストのヘッダーに含めて送られます。

サーバーはセッションデータを保存することなく、トークンの署名を検証してユーザーを認証できます。JWTのステートレスな特性は、特に分散システムやマイクロサービスアーキテクチャに適しています。こうした環境では、セッションストアの一元管理がボトルネックや単一障害点になる可能性があるためです。

Node.jsで人気のオープンソースJWT npmパッケージ

Node.jsでJWTを扱う開発者は、さまざまなオープンソースライブラリを利用できますが、人気のあるjsonwebtoken npmパッケージを選ぶことがよくあります。これらのライブラリを使うと、トークンの生成、署名、検証を簡単に行えます。ただし、安全でない使い方によってセキュリティ上の問題を招かないよう、機能やセキュリティ面の注意事項の違いを理解することが重要です。

ほかのJSON npmパッケージとしては、node-joseや、passport-jwt、@fastify/jwtなどのWebフレームワーク向けインテグレーションがあります。

トークンのデコードに潜むJWTのセキュリティリスク

JWTを使った認証で起こりやすい典型的なミスを説明するため、多くの開発者が選ぶ人気の組み合わせを使います。WebフレームワークのExpressと、JWTライブラリのjsonwebtokenです。

次のNode.jsコードに潜むセキュリティ脆弱性を見つけられますか?

const express = require('express');
const jsonWebToken = require('jsonwebtoken');
require('dotenv').config();

const webApp = express();

const securityKey = process.env.JWT_SECRET;

const validateAccess = (request, response, next) => {
  const authorizationHeader = request.headers.authorization;

  if (!authorizationHeader) {
    return response.status(401).json({ error: 'Missing authentication credentials' });
  }

  try {
    const decodedToken = jsonWebToken.decode(authorizationHeader,{complete: true});
    request.userContext = decodedToken;
    next();
  } catch (authError) {
    return response.status(403).json({ error: 'Invalid or expired token' });
  }
};

webApp.get('/privileged-area', validateAccess, (request, response) => {
  const { username } = request.userContext;
  if (username === 'admin') {
    // Access to sensitive data is now available.
    return response.status(200).json({ status: 'Access permitted to admin' });
  }
  response.status(403).json({ status: 'Insufficient permissions' });
});

webApp.get('/issue-token', (request, response) => {
  const userClaims = { username: 'user' };
  const authenticationToken = jsonWebToken.sign(userClaims, securityKey, { expiresIn: '1h' });
  response.status(200).json({ authenticationToken });
});

webApp.listen(3001, () => {
  console.log('Application listening on port 3001');
});

このNode.jsコードでは、jsonWebToken.decode()を使ってJWTをデコードしています。このメソッドはトークンをデコードするだけで、署名の検証は行いません。そのため、decodedTokenからデータを取得できても、そのデータがサーバーで設定されたものと同じであることや、第三者によって改ざんされていないことは保証されません。つまり、誰でもトークンを改ざんでき、アプリケーションはそれを有効なものとして受け入れてしまいます。代わりにjsonWebToken.verify()を使用し、トークンをデコードしてシークレットキーで検証してください。

SnykがJWTのセキュリティ問題を検出

上記の脆弱なNode.jsコードをSnykでスキャンすると、jsonwebtokenライブラリ内で安全でないJSON Web Tokenメソッドjwt.decode()を使用しているというJWTのセキュリティ問題を検出できます。

Snykがトークンを安全でない方法でデコードするJWTのセキュリティ問題を検出

Snykとの連携方法に応じて、問題を検出できます。上のスクリーンショットでは、SnykのWebアプリからGitHubのコードリポジトリをインポートしました。インポートすると、SnykはPythonのrequirements.txtやnpmのpackage.jsonなどのマニフェストファイルをスキャンし、脆弱な依存関係を検出します。また、TerraformなどのIaCファイルも検出し、コード自体をスキャンする静的解析を実行します。

上のスクリーンショットのように、Snykはjsonwebtokenライブラリのコードの誤用を検出しました。安全でないjwt.decode関数を使用しており、認証機能の不備につながる可能性があります。

JWTを安全にデコードする方法は次のとおりです。

   try {
     const decodedToken = jsonWebToken.verify(authorizationHeader, securityKey);
     request.userContext = decodedToken;
     next();
   } catch (authError) {
     return response.status(403).json({ error: 'Invalid or expired token' });
   }

上記のNode.jsコードには、ほかにも次のようなJWTセキュリティ上の問題があります。

  • 機密データのハードコード:コードでは、環境変数を使ってシークレットキーを保存しています。環境変数を安全に管理し、バージョン管理システムに公開されないようにしてください。また、キーをローテーションし、安全に管理できることも確認しましょう。

  • ログ記録の不足:認証の試行やエラーを記録するログを実装しましょう。潜在的なセキュリティインシデントの監視や対応に役立ちます。

  • レート制限:ブルートフォースによるログイン試行や、認証APIへのその他の悪用をどのように制御しますか?リソースの浪費や制限のない認証試行を防ぐため、レート制限を実装しましょう。

  • トークンの有効期限:コードではトークンの有効期限を設定していますが、リクエストごとにトークンの有効期限を検証し、確実に適用することが重要です。

JWTセキュリティのベストプラクティス

JWTを使ってREST APIを保護するには、トークンを発行して検証するだけでは不十分です。潜在的な脆弱性を軽減するため、複数のセキュリティ対策を慎重に検討する必要があります。たとえば、トークンの署名には必ず強固なシークレットキーを使用し、できれば非対称鍵を使いましょう。また、傍受を防ぐため、トークンがHTTPS経由で送信されるようにしてください。

さらに、トークンの有効期限と失効の適切な戦略を実装することが不可欠です。侵害された場合の悪用リスクを最小限に抑えるため、トークンの有効期間は短く設定しましょう。リフレッシュトークンを使えば、ユーザーが再ログインしなくても新しいアクセストークンを発行できますが、リフレッシュトークンも安全に管理する必要があります。

詳しくは、REST APIの保護に関する包括的なガイドをご覧ください。

AI生成コードのセキュリティ

生成AIコードセキュリティの購入ガイド

Snykの生成AIコードセキュリティの購入ガイドで、AI生成コードを安全に保つ方法をご紹介します。