crypto-js vs jose vs jsonwebtoken vs jws vs node-jose
JWTおよび暗号化ライブラリの技術的比較
crypto-jsjosejsonwebtokenjwsnode-jose類似パッケージ:

JWTおよび暗号化ライブラリの技術的比較

crypto-jsjosejsonwebtokenjwsnode-joseはいずれもJavaScript環境での暗号化やJWT(JSON Web Token)操作を目的としたnpmパッケージですが、それぞれ対応範囲や設計思想が大きく異なります。crypto-jsはAESやSHAなどの汎用暗号化アルゴリズムを提供し、JWTの操作はできません。joseはIETF標準に準拠したJWS/JWT/JWEのフルサポートを実現し、ブラウザとNode.jsの両方で動作します。jsonwebtokenはNode.js向けのJWT署名・検証に特化したシンプルなライブラリです。jwsはJWTの署名部分(JWS)のみを扱う低レベルなライブラリです。node-joseはかつてJWE/JWSをサポートしていましたが、現在は公式に非推奨(deprecated)となっており、新規プロジェクトでの使用は推奨されません。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
crypto-js016,385487 kB2783年前MIT
jose07,643258 kB12ヶ月前MIT
jsonwebtoken018,17043.4 kB2026ヶ月前MIT
jws072118.8 kB336ヶ月前MIT
node-jose0721353 kB733年前Apache-2.0

JWTおよび暗号化ライブラリの比較:crypto-js、jose、jsonwebtoken、jws、node-jose

フロントエンド開発において、トークンの生成・検証やデータの暗号化はセキュリティの要です。しかし、crypto-jsjosejsonwebtokenjwsnode-joseといったnpmパッケージはそれぞれ設計思想や用途が異なり、適切な選択を誤ると実装の複雑さや脆弱性につながります。ここでは、これらのライブラリを実際のコード例を交えて深く比較し、現場でどう使い分けるべきかを解説します。

🔐 ライブラリの目的と対応範囲

まず、各ライブラリが何を得意としているかを整理しましょう。

  • crypto-js:汎用的な暗号化ライブラリ。AES、SHA-256、HMACなど幅広いアルゴリズムをサポート。JWTの操作はできません。
  • jose:IETF標準(RFC 7515〜7519)に準拠したJWS/JWT/JWEのフルサポート。現代的なWeb Crypto API互換。
  • jsonwebtoken:Node.js向けのJWT署名・検証に特化。シンプルで使いやすいが、JWE(暗号化JWT)非対応。
  • jws:JWTの署名部分(JWS)のみを扱う低レベルライブラリ。jsonwebtokenの内部で使われることも。
  • node-jose:古くからあるJWE/JWS実装だが、公式に非推奨(deprecated)。新規プロジェクトでの使用は避けるべき。

💡 注:node-joseはnpmページおよびGitHubリポジトリで「This library is deprecated.」と明記されており、メンテナンスも停止されています。代わりにjoseの使用が推奨されています。

🧪 署名付きJWTの生成と検証

最も一般的なユースケースである「署名付きJWT(JWS)の作成・検証」を各ライブラリで実装してみましょう。

jose(推奨)

joseはTypeScript対応で、Web Crypto API互換のためブラウザ・Node.js両方で動作します。

// JWTの署名(HS256)
import { SignJWT } from 'jose';

const secret = new TextEncoder().encode('your-secret');
const jwt = await new SignJWT({ userId: 123 })
  .setProtectedHeader({ alg: 'HS256' })
  .setIssuedAt()
  .setExpirationTime('1h')
  .sign(secret);

// JWTの検証
import { jwtVerify } from 'jose';

const { payload } = await jwtVerify(jwt, secret);
console.log(payload.userId); // 123

jsonwebtoken

Node.js環境で広く使われてきましたが、ブラウザでは動作しません(Node固有のcryptoモジュール依存)。

// JWTの署名
import jwt from 'jsonwebtoken';

const token = jwt.sign({ userId: 123 }, 'your-secret', { expiresIn: '1h' });

// JWTの検証
const decoded = jwt.verify(token, 'your-secret');
console.log(decoded.userId); // 123

jws

低レベルな操作が必要な場合に使われますが、高レベルなJWT機能(有効期限チェックなど)は含まれません。

// JWSの署名(JWTではない)
import jws from 'jws';

const signature = jws.sign({
  header: { alg: 'HS256' },
  payload: JSON.stringify({ userId: 123 }),
  secret: 'your-secret'
});

// 検証
const isValid = jws.verify(signature, 'HS256', 'your-secret');

crypto-jsnode-jose

  • crypto-jsはJWTを扱えません。ハッシュや暗号化には使えますが、JWTの構造(ヘッダー・ペイロード・署名のBase64Urlエンコード)を自前で実装する必要があります。
  • node-joseは非推奨のため、コード例は省略します。

🔒 暗号化JWT(JWE)のサポート

機密情報を含むJWTを暗号化したい場合、JWE対応が必要です。

  • jose:完全対応。RSA-OAEP、AES-GCMなどのアルゴリズムをサポート。
// JWEの暗号化
import { EncryptJWT, importJWK } from 'jose';

const publicKey = await importJWK({ kty: 'RSA', ... }, 'RSA-OAEP');
const jwe = await new EncryptJWT({ ssn: '123-45-6789' })
  .setProtectedHeader({ alg: 'RSA-OAEP', enc: 'A256GCM' })
  .encrypt(publicKey);

// 復号
import { jwtDecrypt } from 'jose';
const privateKey = await importJWK({ kty: 'RSA', ... }, 'RSA-OAEP');
const { payload } = await jwtDecrypt(jwe, privateKey);
  • jsonwebtokenjwscrypto-js:JWE非対応。
  • node-jose:JWE対応していましたが、非推奨のため使用不可。

🌐 ブラウザ vs Node.js の互換性

ライブラリブラウザNode.js
jose
jsonwebtoken
jws⚠️(制限あり)
crypto-js
node-jose❌(非推奨)❌(非推奨)

jsonwebtokenはNode.jsのcryptoモジュールに依存しているため、ブラウザでは動作しません。一方、joseはWeb Crypto APIを使用するため、モダンブラウザでそのまま使えます。

🛠 実装の手間と安全性

  • jose:API設計が洗練されており、タイムスタンプの自動処理(nbfexp)、署名アルゴリズムの厳格な検証など、セキュリティベストプラクティスが組み込まれています。
  • jsonwebtoken:簡単ですが、{ algorithms: [...] }オプションを指定しないと脆弱性(署名なしのnoneアルゴリズム許容)が発生する可能性があります。
  • jws:低レベルすぎるため、開発者がJWTの仕様を正確に理解していないとバグや脆弱性を生みやすいです。
  • crypto-js:JWTの構造を自前で実装すると、Base64Urlエンコードのミスや署名検証漏れが起きやすいです。

📌 まとめ:どのライブラリを選ぶべきか?

  • 新規プロジェクトでJWTを使うなら → jose

    • ブラウザ・Node.js両対応
    • JWS/JWE完全サポート
    • セキュリティ面で堅牢
  • Node.js専用でシンプルなJWT署名が必要 → jsonwebtoken

    • ただし、ブラウザ非対応
    • JWE不要なら十分使える
  • 低レベルなJWS操作が必要 → jws

    • 高度なカスタマイズが必要な場合のみ
  • 汎用暗号化(JWT以外)→ crypto-js

    • AES、SHA、HMACなど
    • JWTの操作には不向き
  • node-jose → 使用禁止

    • 非推奨のため、新規プロジェクトでは絶対に使わないでください

💡 最終アドバイス

フロントエンド開発者がJWTを扱う場合、秘密鍵や署名キーをクライアント側に置かないことが大前提です。JWTの検証はサーバー側で行い、クライアントはトークンを安全に保管(HttpOnly Cookieなど)する役割に徹すべきです。その上で、クライアントでJWEの復号が必要な特殊ケースを除き、ほとんどの場合joseが最適解となります。

選び方: crypto-js vs jose vs jsonwebtoken vs jws vs node-jose

  • crypto-js:

    crypto-jsはJWTではなく、AES、SHA-256、HMACなどの汎用的な暗号化やハッシュ処理が必要な場合に選んでください。JWTの生成や検証には対応していないため、トークン操作には不向きです。ブラウザとNode.jsの両方で動作しますが、セキュリティ的に敏感な操作(例:秘密鍵の管理)はクライアント側で行わないよう注意が必要です。

  • jose:

    joseは現代的なJWT操作(JWS署名・検証、JWE暗号化・復号)を必要とするプロジェクトに最適です。IETF標準に完全準拠しており、ブラウザとNode.jsの両方で動作します。TypeScript対応で、セキュリティベストプラクティス(例:アルゴリズムの厳格な検証、タイムスタンプ自動処理)が組み込まれているため、新規プロジェクトでは第一候補として検討すべきです。

  • jsonwebtoken:

    jsonwebtokenはNode.js環境でシンプルなJWT署名・検証が必要な場合に適しています。ブラウザでは動作しないため、フロントエンド専用のプロジェクトには使えません。JWE(暗号化JWT)のサポートはありませんが、基本的な認証フロー(例:アクセストークンの発行)には十分です。ただし、algorithmsオプションを明示的に指定しないと脆弱性が発生する可能性がある点に注意してください。

  • jws:

    jwsはJWTの署名部分(JWS)を低レベルで操作したい場合に選んでください。高レベルなJWT機能(例:有効期限の自動検証)は含まれないため、開発者が仕様を正確に理解していないとバグや脆弱性を生みやすいです。通常はjsonwebtokenの内部実装として使われることが多く、直接使うケースは限定的です。

  • node-jose:

    node-joseは公式に非推奨(deprecated)となっており、新規プロジェクトでの使用は絶対に避けてください。代わりにjoseパッケージの使用が強く推奨されています。既存プロジェクトで使っている場合は、早急に移行を検討すべきです。

crypto-js のREADME

crypto-js

JavaScript library of crypto standards.

Discontinued

Active development of CryptoJS has been discontinued. This library is no longer maintained.

Nowadays, NodeJS and modern browsers have a native Crypto module. The latest version of CryptoJS already uses the native Crypto module for random number generation, since Math.random() is not crypto-safe. Further development of CryptoJS would result in it only being a wrapper of native Crypto. Therefore, development and maintenance has been discontinued, it is time to go for the native crypto module.

Node.js (Install)

Requirements:

  • Node.js
  • npm (Node.js package manager)
npm install crypto-js

Usage

ES6 import for typical API call signing use case:

import sha256 from 'crypto-js/sha256';
import hmacSHA512 from 'crypto-js/hmac-sha512';
import Base64 from 'crypto-js/enc-base64';

const message, nonce, path, privateKey; // ...
const hashDigest = sha256(nonce + message);
const hmacDigest = Base64.stringify(hmacSHA512(path + hashDigest, privateKey));

Modular include:

var AES = require("crypto-js/aes");
var SHA256 = require("crypto-js/sha256");
...
console.log(SHA256("Message"));

Including all libraries, for access to extra methods:

var CryptoJS = require("crypto-js");
console.log(CryptoJS.HmacSHA1("Message", "Key"));

Client (browser)

Requirements:

  • Node.js
  • Bower (package manager for frontend)
bower install crypto-js

Usage

Modular include:

require.config({
    packages: [
        {
            name: 'crypto-js',
            location: 'path-to/bower_components/crypto-js',
            main: 'index'
        }
    ]
});

require(["crypto-js/aes", "crypto-js/sha256"], function (AES, SHA256) {
    console.log(SHA256("Message"));
});

Including all libraries, for access to extra methods:

// Above-mentioned will work or use this simple form
require.config({
    paths: {
        'crypto-js': 'path-to/bower_components/crypto-js/crypto-js'
    }
});

require(["crypto-js"], function (CryptoJS) {
    console.log(CryptoJS.HmacSHA1("Message", "Key"));
});

Usage without RequireJS

<script type="text/javascript" src="path-to/bower_components/crypto-js/crypto-js.js"></script>
<script type="text/javascript">
    var encrypted = CryptoJS.AES(...);
    var encrypted = CryptoJS.SHA256(...);
</script>

API

See: https://cryptojs.gitbook.io/docs/

AES Encryption

Plain text encryption

var CryptoJS = require("crypto-js");

// Encrypt
var ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123').toString();

// Decrypt
var bytes  = CryptoJS.AES.decrypt(ciphertext, 'secret key 123');
var originalText = bytes.toString(CryptoJS.enc.Utf8);

console.log(originalText); // 'my message'

Object encryption

var CryptoJS = require("crypto-js");

var data = [{id: 1}, {id: 2}]

// Encrypt
var ciphertext = CryptoJS.AES.encrypt(JSON.stringify(data), 'secret key 123').toString();

// Decrypt
var bytes  = CryptoJS.AES.decrypt(ciphertext, 'secret key 123');
var decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));

console.log(decryptedData); // [{id: 1}, {id: 2}]

List of modules

  • crypto-js/core
  • crypto-js/x64-core
  • crypto-js/lib-typedarrays

  • crypto-js/md5
  • crypto-js/sha1
  • crypto-js/sha256
  • crypto-js/sha224
  • crypto-js/sha512
  • crypto-js/sha384
  • crypto-js/sha3
  • crypto-js/ripemd160

  • crypto-js/hmac-md5
  • crypto-js/hmac-sha1
  • crypto-js/hmac-sha256
  • crypto-js/hmac-sha224
  • crypto-js/hmac-sha512
  • crypto-js/hmac-sha384
  • crypto-js/hmac-sha3
  • crypto-js/hmac-ripemd160

  • crypto-js/pbkdf2

  • crypto-js/aes
  • crypto-js/tripledes
  • crypto-js/rc4
  • crypto-js/rabbit
  • crypto-js/rabbit-legacy
  • crypto-js/evpkdf

  • crypto-js/format-openssl
  • crypto-js/format-hex

  • crypto-js/enc-latin1
  • crypto-js/enc-utf8
  • crypto-js/enc-hex
  • crypto-js/enc-utf16
  • crypto-js/enc-base64

  • crypto-js/mode-cfb
  • crypto-js/mode-ctr
  • crypto-js/mode-ctr-gladman
  • crypto-js/mode-ofb
  • crypto-js/mode-ecb

  • crypto-js/pad-pkcs7
  • crypto-js/pad-ansix923
  • crypto-js/pad-iso10126
  • crypto-js/pad-iso97971
  • crypto-js/pad-zeropadding
  • crypto-js/pad-nopadding

Release notes

4.2.0

Change default hash algorithm and iteration's for PBKDF2 to prevent weak security by using the default configuration.

Custom KDF Hasher

Blowfish support

4.1.1

Fix module order in bundled release.

Include the browser field in the released package.json.

4.1.0

Added url safe variant of base64 encoding. 357

Avoid webpack to add crypto-browser package. 364

4.0.0

This is an update including breaking changes for some environments.

In this version Math.random() has been replaced by the random methods of the native crypto module.

For this reason CryptoJS might not run in some JavaScript environments without native crypto module. Such as IE 10 or before or React Native.

3.3.0

Rollback, 3.3.0 is the same as 3.1.9-1.

The move of using native secure crypto module will be shifted to a new 4.x.x version. As it is a breaking change the impact is too big for a minor release.

3.2.1

The usage of the native crypto module has been fixed. The import and access of the native crypto module has been improved.

3.2.0

In this version Math.random() has been replaced by the random methods of the native crypto module.

For this reason CryptoJS might does not run in some JavaScript environments without native crypto module. Such as IE 10 or before.

If it's absolute required to run CryptoJS in such an environment, stay with 3.1.x version. Encrypting and decrypting stays compatible. But keep in mind 3.1.x versions still use Math.random() which is cryptographically not secure, as it's not random enough.

This version came along with CRITICAL BUG.

DO NOT USE THIS VERSION! Please, go for a newer version!

3.1.x

The 3.1.x are based on the original CryptoJS, wrapped in CommonJS modules.