コース目次 / 第1章

分解する — 3つの部品と、読める中身

JWTがヘッダ・ペイロード・署名の3部品からなり、最初の2つはbase64urlエンコードなだけで誰でも読めることを、手元で確かめます。JWTは暗号化ではないと体で理解します。

第1章 / 全5章目安 約9分この章のゴール: JWTの構造を理解し、ヘッダとペイロードを自分でデコードできるようになる

JWT は、一見すると意味のない長い文字列です。でも、よく見るとピリオド . で3つに区切られています。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiaGFydXRvIiwicm9sZSI6InVzZXIifQ.3Y_k...(署名)

法と倫理ゲート

手を動かす前に

このコースは攻撃の技術に触れます。実習してよい相手は、次の3つだけです。

  • (a) 自分で作った練習用のやられ環境(このサイトが配る自作ターゲット)
  • (b) あなた自身の許可された環境(自分の Thousand SKY・secret-notes-ctf など)
  • (c) CTF 競技の中(規約の範囲で)

実在する第三者のシステムを許可なく触ることは、不正アクセス禁止法に触れます。 このゲートは、その線引きを毎回の入口で確かめるためのものです。詳しくは「セキュリティの法と倫理」へ。

3つの部品

. で割った3つは、それぞれ役割があります。

1つめ = ヘッダ(header) — このトークンの「作り方」。署名アルゴリズム(alg)などが入る。
2つめ = ペイロード(payload) — 中身。「誰か(user)」「権限(role)」「有効期限(exp)」など。
3つめ = 署名(signature) — 1つめと2つめが改ざんされていないことを保証する印。
前の2つは base64url というエンコード方式で、3つめだけが暗号的な署名です。

デコードしてみる — 鍵は要らない

いちばん大事な事実を、手を動かして確かめます。ヘッダとペイロードは、鍵がなくても読めます。 base64url は暗号ではなく、ただの文字の置き換えだからです。

ブラウザの開発者ツールのコンソールで、JWT のペイロード部分(2つめ)を atob でデコードできます。

// ブラウザのコンソールで。JWT の「2つめ」の部品を貼る。
// base64url は - _ を使うので、base64 の + / に戻してから atob する。
const part = "eyJ1c2VyIjoiaGFydXRvIiwicm9sZSI6InVzZXIifQ";
atob(part.replace(/-/g, '+').replace(/_/g, '/'));
// => '{"user":"haruto","role":"user"}'

node なら、もっと素直です。

// node で。base64url を直接デコードできる。
const part = "eyJ1c2VyIjoiaGFydXRvIiwicm9sZSI6InVzZXIifQ";
console.log(Buffer.from(part, 'base64url').toString());
// => {"user":"haruto","role":"user"}

{“user”:“haruto”,“role”:“user”} が、そのまま読めました。鍵も、パスワードも要りません。JWT のヘッダとペイロードは、誰でも——サーバも、ブラウザも、通信を覗いた第三者も——中身を読めます。これが試食で見た「見えるものは秘密ではない」の正体です。

JWT は「暗号化」ではない

ここを取り違えないことが、この章の芯です。

JWT は、暗号化されていません。署名(3つめ)は「改ざんされていないこと」を保証しますが、「中身を隠す」ものではない。だから——JWT のペイロードに、秘密を入れてはいけません。パスワード、クレジットカード番号、隠しておきたい個人情報。これらを JWT に入れると、トークンを持っている人(や覗いた人)全員に読まれます。
「署名 = 暗号化」という誤解が、実際の事故を生んでいます。JWT が守るのは完全性(改ざん検知)であって、秘匿性(中身を隠す)ではない。

持ち帰る一言

JWT の中身は、誰でも読める。 ヘッダとペイロードは base64url エンコードなだけで、鍵なしでデコードできます。JWT は暗号化ではなく、署名で改ざんを止めているだけ。だから秘密は入れない。では、その「改ざんを止める署名」は、どう働くのか。次の章で見ます。

こうなっていればOK

卒業まであと3章です。

この章はまだ完了していません。