security

Security ツール

ハッシュ、パスワードのエントロピー、JWTのクレーム、CSPの記述、共有前のマスキング——Web開発で繰り返し発生するセキュリティ実務について、各プリミティブが何を保証し何を保証しないかを整理し、用途別のツールの使い分けと典型的な落とし穴をまとめる。

11 ツール

§01 分野ガイド

Webセキュリティの「日常業務」層

通常の開発で発生するセキュリティ作業の多くは攻防ではない。ダイジェストを選び、 認証情報を生成し、認証失敗の理由をトークンから読み、CSPを書き、共有前にキャプチャ から機密を削る。機械的だが事故はここから始まる。原因は技術力ではなく習慣だ。

区別すべきは3つ。エンコード(Base64、Punycode)は可逆で鍵を使わず、機密性を 生まない。ハッシュ(SHA-256)は一方向だがこれも鍵を使わず、同一性しか示さない。 鍵を使う操作(HMAC、JWT署名、TLS)だけが「誰が作ったか」を証明でき、それも鍵が 秘密の間だけだ。生のダイジェストもデコード済みJWTも、裏付けのない主張にすぎない。

もう一つの軸は方向だ。出力するもの(CSP、ヘッダー)は配信中のレスポンスで確認する まで正しさが確定せず、検査するもの(トークン、HAR)ではリスクが中身に移る。機密の 典型的な流出経路はバグ報告に添付されたHARだ。

各プリミティブが保証するもの

プリミティブ証明できることできないことよくある誤用
Base64 / URLエンコードテキスト経路での運搬機密性(誰でも復元可)APIキーの「隠蔽」
SHA-256 / SHA-512完全性(同入力=同結果)秘匿性、総当たり耐性パスワードの保存
SHA-1旧チェックサム互換のみ衝突耐性(実用的に破綻)署名
HMAC(ハッシュ+鍵)鍵が秘密な間の真正性機密性(本文は読める)鍵をブラウザへ配布
Argon2id / bcrypt / scryptソルト付きの低速な保存速度(遅いことが機能)高速ハッシュへの差し替え
JWT署名(HS256/RS256)発行者(検証後のみ)デコードだけでは何もデコード結果を信頼
Content-Security-Policy読み込み許可リストの強制注入自体の修正'unsafe-inline'の残置
TLS + Strict-Transport-Security経路の機密性、httpへの降格防止アプリのロジックの正しさ鍵アイコンを「安全」と読む

エントロピーはどれだけ必要か

エントロピーは長さ × log2(プールサイズ)。プール選択は長さと同じだけ効く。

文字プール1文字8文字14文字20文字
a–z(26)4.70386694
a–z0–9(36)5.174172103
a–zA–Z0–9(62)5.954883119
4種すべて(87)6.445290129

40bit未満は破綻、重要アカウントの下限は80bit、112bit以上で安心(UUID v4は122bit分)。

用途別の使い分け

フィンガープリントはハッシュ生成(Web Cryptoで4種同時、 shasum -a 256と一致、MD5は非実装)。秘密情報は「誰が持つか」で選び、人や パスワードマネージャーならパスワード生成、機械向けの不透明ID ならUUIDジェネレーター。どちらもCSPRNG由来で、前者は crypto.getRandomValues、後者はcrypto.randomUUID()を使う(Math.randomではない)。

JWTデコーダーheader.payload.signatureを分解し iat/exp/nbfをISO 8601と失効判定で表示する。断片だけなら Base64で、Base64urlの-/_+//に直す。

記述と検証は分ける。CSPジェネレーターが14ディレクティブと 堅い初期値(default-src 'self'frame-ancestors 'none'等)からヘッダーを組み、 詳細はCSPハブ。実際に乗っているかは HTTPヘッダーで見る(サーバー側取得=生ヘッダー)。CDNは 書き換えるし、<meta>ではframe-ancestorsを指定できない。

共有前はHARビューアーで読み、外へ出すコピーは HARサニタイザーへ(cookie等を[REDACTED]化、本文を空に、 トークン風パラメータをマスク)。URL1本ならURLパーサー、類似 ドメインはIDN変換

HTTPヘッダーチェッカー以外はブラウザ内完結で、同ツールのみ入力URLを api.sitekits.devへ送る。関連は職種別セットプライバシーハブ

実際に事故になる落とし穴

デコードは検証ではない

分かるのはBase64urlとして妥当かだけだ。検証には鍵と固定したアルゴリズム、サーバー側 のexp/nbf/audチェックが要る。algヘッダー任せはalg: noneやRS256→HS256混同 を踏む。

高速ハッシュはパスワード保存ではない

生のSHA-256はGPUベンチマークで、ソルトが無ければ1つのレインボーテーブルで全 ユーザーが破られる。ファイル比較なら逆に高速ダイジェストが正解だ。

'unsafe-inline'はCSPの大半を無効化する

CSPが止めるべきインラインスクリプトを許可し直すため、ポリシーが文書に成り下がる。 nonceかハッシュを使う。ワイルドカードのconnect-srcも同じ穴だ。

マスキングはパターン一致にすぎない

対象は固定のヘッダー名とtoken/key/secret/password/passwd/pwd/auth/session/sig/signatureを含むパラメータ名だけで、 パスセグメント内やtのような名前は残る。置換0件は検出漏れのサインだ。

エントロピーは生成方法の性質であって、文字列の性質ではない

式が成り立つのは全文字をランダムに選んだ場合だけだ。P@ssw0rd!2024は13文字で4種 すべてを含むが、どのクラック用辞書にも載っている。メーターの点数に意味があるかを 決めるのは、その文字列がどう生成されたかだけだ。

FAQ
パスワードの保存にSHA-256で十分ですか?
不十分です。SHA-256は高速に設計されており、市販GPUは毎秒数十億回のダイジェストを計算できるため、ハッシュ一覧が漏れれば短いパスワードや定番のパスワードは数時間で割られます。保存にはソルト付きで意図的に遅いKDF(Argon2id、bcrypt、scrypt)をワークファクター調整のうえ使ってください。SHA-256が正解なのはファイルの完全性確認や変更検知であり、認証情報ではありません。
JWTをデコードできれば内容を信頼していいですか?
いけません。JWTのヘッダーとペイロードはBase64urlエンコードされているだけで暗号化も保護もされておらず、誰でも読めて誰でも任意のクレームを持つトークンを作れます。発行者を証明するのは鍵に対する署名検証だけです。デコードはあくまでデバッグ用で、expやnbf、audienceの判定はサーバー側で行ってください。クライアントの時計は狂っていることも偽っていることもあります。
パスワードは何文字必要ですか?
文字プールによって変わります。エントロピーは「長さ × log2(プールサイズ)」だからです。大文字・小文字・数字・記号の4種(87文字プール)なら1文字あたり約6.44bitで、13文字で約84bit、20文字で約129bitになります。小文字だけなら1文字4.70bitなので、8文字は約38bitで総当たり可能です。重要なアカウントは80bit以上を目安にしてください。
CSPを入れればXSSは防げますか?
防ぐのではなく被害を封じ込める仕組みです。厳格なポリシーは注入されたスクリプトの実行やデータ持ち出しを止めますが、CSPを強制するブラウザ上でのみ、かつscript-srcに'unsafe-inline'や'unsafe-eval'を含めていない場合に限られます。注入自体は出力エスケープで直し、CSPは取り逃した分の被害範囲を狭める用途に使ってください。導入はContent-Security-Policy-Report-Onlyから始め、最後に強制ヘッダーが実際のレスポンスに乗っているか確認します。
APIキーはBase64エンコードして送れば隠せますか?
隠せません。Base64は暗号ではなくエンコードで、鍵を使わないため、ペイロードを持つ人は誰でもコマンド1回で元のバイト列に戻せます(デコーダーが何の入力も要求しないのはこのためです)。より本質的な問題は形式ではなく置き場所で、ブラウザやモバイルアプリのバイナリに配った秘密は、何で包んでも既に開示済みです。長期の鍵はサーバー側に置き、クライアントには秘密そのものではなく真正性を示すもの(短命のトークン、あるいは鍵を自組織から出さないHMAC署名)を渡してください。