日本語

エンコード、エスケープ、ハッシュ化

Base64 は暗号化ではなく、btoa は UTF-8 ではなく、encodeURI は encodeURIComponent ではなく、SHA-256 はパスワード ハッシュではありません。ここでは、これらのそれぞれが実際に何を行うのか、また、そうでないと仮定した場合に生じる具体的な間違いを示します。

エンコードは暗号化でも圧縮でもありません

エンコーディングにより、データの書き込み方法が変わります。暗号化により、それを読み取ることができる人が変わります。圧縮により、必要なスペースが変化します。これらは 3 つの異なるジョブであり、base64 は最初のジョブのみを実行します。他の 2 つのいずれかを期待していた場合は残念です。

Base64 は一度に 3 バイトを受け取り、64 記号のアルファベットから描画された 4 つの文字として書き換えます。 3 バイトを運ぶ 4 文字は、出力が常に入力よりも約 33% 大きくなり、パディングも加えられることを意味します。これが存在するのは、電子メール ヘッダー、HTTP ヘッダー、JSON 文字列値、URL、XML 属性など、多くのインフラストラクチャがテキストや任意のバイトを壊したり拒否したりするように設計されているためです。 Base64 は、テキスト形状のパイプを通じてバイトをプッシュできるようにするアダプターです。

キーがないので、誰でもキーなしで瞬時に元に戻すことができます。パスワードを Base64 化すると、やや不便な形式でパスワードを公開したことになります。人間の目には Base64 出力がスクランブルされているように見えるため、これは重要です。これがまさに、できないことを人々に信頼させる特性です。

btoa() が壊れる理由と、それが壊れる 2 つの異なる方法

ブラウザーでは btoa() と atob() が提供されますが、これらは最新のテキスト API よりも古いものです。 btoa は、「バイナリ文字列」、つまり、0 から 255 までの、すべてのコード単位が単一バイトである文字列に対して定義されます。テキストはそういうものではありません。

最初の失敗はうるさいです。 btoa("世界") を呼び出すと、U+4E16 がバイトに収まらないため、InvalidCharacterError が返されます。大きな失敗は良いものです。すぐに気づき、修正を探します。

2 番目の失敗は沈黙しており、本番環境に到達する失敗です。文字 é は U+00E9 で、1 バイトに収まります。したがって、 btoa("café") は、é をシングルバイト 0xE9 としてエンコードして正常に戻ります。ただし、UTF-8 の é は 0xC3 0xA9 の 2 バイトです。作成したばかりの Base64 は、地球上の他のすべてのシステムで、テキストではないものにデコードされます。データベース内の名前が置換文字に変わったことが数週間後にわかります。

この修正は、テキストをバイトとして扱うのをやめ、明示的に変換することです。 TextEncoder は UTF-8 バイトを生成します。それらをエンコードします。 TextDecoder はバイトをテキストに戻し、{ fatal: true } を使用して構築すると、U+FFFD を静かに置き換えるのではなく、無効なシーケンスをスローするため、正しいとは思えないデコードは、もっともらしく見えるナンセンスを返すのではなく失敗します。これがこのツールキットが使用するパイプラインであり、絵文字、マークの組み合わせ、および右から左へのスクリプトがすべて正確に往復する理由です。

  1. TextEncoder を使用してテキストをバイトに変換します。文字列にインデックスを付けないでください。
  2. バイトをbase64にエンコードします。
  3. 逆にするには、base64 をバイトにデコードし、そのバイトを致命的: true で UTF-8 としてデコードします。
  4. UTF-8 ステップが失敗した場合、ペイロードはテキストではなくバイナリです。ふりをするのではなく、六角形として示します。

Base64 と Base64URL、およびパディングの質問

標準のbase64では、最後の2つの記号として+と/を使用します。 URL では両方とも意味があります。+ はクエリ文字列内のエンコードされたスペースとして読み取ることができ、/ はパス区切り文字です。そこで、RFC 4648 は、代わりに - と _ を置き換える 2 番目のアルファベット、base64url を定義します。 JWT は、ほとんどのトークン形式や多くの API と同様に、これを使用します。

パディングはもう 1 つの変数です。標準の Base64 は = でパッドされるため、出力の長さは常に 4 の倍数になります。 = 自体が URL 内の厄介な文字であり、長さは算術的に回復できるため、base64url では通常、パディングが削除されます。パディングを要求するデコーダーは、完全に有効な JWT セグメントを拒否します。

実践的なアドバイス: 何を渡されるかを制御することはほとんどないため、デコーダは両方のアルファベットを受け入れ、パディングの欠落を許容する必要があります。受信者はおそらく気にするので、エンコーダは何を出力するかを明示する必要があります。ここでの Base64 ユーティリティはまさにそれを行います。妥当なものはすべて受け入れ、生成するものを正確に選択できます。

encodeURI と encodeURIComponent: 一文の違い

どちらも UTF-8 を使用してパーセント エンコードします。それらの違いは、どの文字をそのままにするかだけであり、その違いが全体です。encodeURIComponent は予約された区切り文字をエスケープしますが、encodeURI はエスケープしません。

予約された区切り文字は、URL にその構造を与える文字です: : / ? # [ ] @ ! $ & ' ( ) * + , ; =。 encodeURI は、すでに正しく構造化されており、その状態を維持する必要がある URL を渡されたものと想定しているため、それらを保持します。https:// を https%3A%2F%2F に変換することはありません。 encodeURIComponent は、スロットにドロップされる 1 つのピースが渡されたと想定しているため、それらをエスケープし、そのピースがスロットから抜け出せないようにします。

これによって発生するバグは完全に機械的なものです。 a&b=c の検索値を取得します。これを encodeURI でエンコードし、?q=a&b=c として追加すると、2 つのパラメーターが黙って作成されます。q は単なる "a" になり、b=c が出現します。 encodeURIComponent でエンコードすると、?q=a%26b%3Dc、1 つのパラメーター、正しい値が得られます。同じクラスのバグにより、細工された値によって、コードが構築する URL にパラメータが挿入されます。これが、「値にコンポーネント フォームを使用する」ことが単なる正確性のルールではなく、セキュリティ ルールである理由です。

フォーム エンコーディングは、2 番目のルールと似た 3 番目のルールです。 application/x-www-form-urlencoded は、スペースを %20 ではなく + として書き込みます。プレーンな decodeURIComponent を使用してフォーム本体をデコードすると、データ内のすべてのプラス記号がスペースになります。これまでに "C++" を「C」に壊した検索ボックスはすべてこのバグです。

HTML エンティティ、および innerHTML を使用してエンティティをデコードすることが悪い習慣である理由

HTML のエスケープは範囲が狭く、よく理解されています。& は &amp; になり、< は &lt; になり、> は &gt; になり、属性値の内側の " と ' もエスケープする必要があります。5 文字です。それ以上のエスケープ (すべてのアクセント付き文字を名前付きエンティティに変換する) は、文字エンコーディングが不確実だった時代の回避策であり、現在は安全性ではなくオプションのスタイル設定です。

デコードには悪い習慣が潜んでいます。すべての回答に現れる 1 行のトリックは、切り離された要素の innerHTML に文字列を割り当て、その textContent を読み戻すことです。それはうまくいきますが、それは悪いアイデアです。信頼できない入力を HTML パーサーに渡し、そこから実際の DOM ノードを構築します。その文字列内の <img src=x onerror=...> は、実際のエラー ハンドラーがアタッチされた実際の画像要素になります。そのサブツリーがドキュメントに挿入されると、そのサブツリーが実行されます。また、データをサイレントに破棄します。入力内のタグは、パーサーがテキストではなくマークアップとして解釈したため、ラウンドトリップではなく消去されます。

エンティティを適切にデコードするには、パーサーはまったく必要ありません。参照を照合したり、テーブル内で名前を検索したり、数値参照の算術演算を行ったりする必要はありません。これは数十行であり、何も実行できず、忠実に往復します。このツールキットはそのように実行するため、スクリプト タグをエンティティ デコーダーに貼り付けるとスクリプト タグが表示されます。

ハッシュの選択とそれを決定する 3 つの質問

暗号化ハッシュはあらゆる入力を固定長のダイジェストに変換するため、同じダイジェストを持つ 2 つの入力を見つけることは不可能になります。このプロパティにより、署名、整合性チェック、またはコンテンツ アドレスにおいてダイジェストがデータの代わりをすることができます。

最初の質問: 事故や敵から身を守っていますか?破損したダウンロードを防ぐチェックサムは、ランダムな反転をキャッチするだけで十分です。 CRC32は大丈夫ですよ。攻撃者が衝突することで利益を得られるダイジェストには、まだ残っているハッシュが必要です。この違いが、SHA-1 が単なる "old" ではない理由です。

SHA-1 が壊れています。 2017 では、SHAttered 作業により、同じ SHA-1 ダイジェストを持つ 2 つの異なる PDF ファイルが生成されました。 2020 の「SHA-1 は修羅場です」は、選択されたプレフィックスの衝突を示しました。これは、攻撃者が慎重に構築された 2 つの BLOB ではなく、意味のある 2 つの異なるドキュメントを衝突させるため、より強力ではるかに危険な亜種です。システムのセキュリティが SHA-1 衝突耐性に基づいている場合、そのセキュリティは失われます。 SHA-1 は、git オブジェクト ID と従来の API シグネチャのロングテールで依然として使用されており、これらの値を再現できる必要があるため、このツールキットに残ります。値を再現することは、値に依存することと同じではありません。

2 番目の質問: 入力はパスワードですか?もしそうなら、これらはどれも答えではありません。 SHA-256 は高速になるように設計されていますが、パスワードにとって高速というのはまさに間違いです。つまり、攻撃者がデータベースを使用して 1 秒あたり数十億回の推測を試みることができるということです。パスワードには、ユーザーごとのソルト (Argon2id、scrypt、または bcrypt) を使用した、意図的に遅く、メモリに負荷がかかる関数が必要です。これはニュアンスではありません。パスワードに SHA-256 を使用することは、最も一般的な重大なハッシュ ミスです。

3 番目の質問: キー付きダイジェストが必要ですか?メッセージをフィンガープリントするのではなく認証する場合は、裸のハッシュではなく HMAC が必要です。シークレットを連結してハッシュすることは、長さ拡張攻撃に対する古典的なオウンゴールです。 HMAC が存在するのは、その構築を正しく行うのが見た目よりも難しいためです。

ファイル、コンテンツ アドレス、整合性属性のフィンガープリンティングなど、他のすべての場合は、SHA-256 が賢明なデフォルトであり、64 ビットのハードウェアでは SHA-512 の方がより高速であり、より幅広いダイジェストを提供します。

ここのハッシュがブラウザから取得される理由

このツールキットのダイジェストは、このサイトから提供される JavaScript ではなく、ブラウザー独自の Web Crypto 実装である SubtleCrypto によって計算されます。これは意図的な選択です。ブラウザの実装は監査され、保守され、通常は最適化されたネイティブ コードとして実行されます。ページ バンドル内の手書きの SHA-256 は、何の利益もなく信頼すべきコードです。

それには目に見える結果が 1 つあります。 Web 暗号は、https:// または localhost を意味する安全なコンテキストでのみ公開されます。 LAN アドレス上のプレーン HTTP 経由でこのページを開くと、crypto.subtle は未定義になるため、ハッシュ ユーティリティは、黙って失敗したり、より弱いもので置き換えたりするのではなく、そのことを明確に通知します。

同じ理由で UUID ジェネレーターが駆動されます。 crypto.randomUUID() もセキュア コンテキスト専用であるため、これが利用できない場合、ツールキットは同じ暗号的に安全なソースである crypto.getRandomValues() にフォールバックし、バージョンとバリアント ビット自体を設定します。 Math.random() にフォールバックすることは決してありません。これは高速な非暗号化 PRNG であり、その内部状態は出力の短期間の実行から回復できますが、識別子はセッション キーやパスワード リセット リンクに昇格されるという残念な傾向があります。安全なソースが存在しない場合、このツールは何も生成せず、その理由を示します。

貼り付けたものはどうなりますか

  • すべての変換、ハッシュ、デコード、差分はブラウザーのタブで実行されます。ページが読み込まれるとサーバーは関与しないため、入力はサーバーにアップロード、記録、または保存されません。
  • ハッシュはブラウザー独自の Web Crypto 実装から取得され、UUID は暗号的に安全なランダム ジェネレーターから取得されます。どちらもネットワーク通話を必要としません。
  • 入力した内容はローカル ストレージや Cookie に書き込まれません。ページをリロードするとページは破棄されます。タブを閉じると破棄されます。
  • サイト全体の分析は、構成された正規実稼働ホスト上でのみ実行され、プライバシー ポリシーで開示されます。ローカルホストとプレビューホストはそれを拒否します。貼り付けられた値、トークン、URL、およびファイルの内容は、ToolAcre 独自の分析イベントから除外されます。現在の構成では広告は無効になっています。
  • つまり、JWT または API キーはライブ認証情報です。安全な習慣は、この主張を含め、その主張がどれほど信頼できるものであっても、自分が書いていない Web ページに貼り付けることは決してしないことです。

質問

Base64はデータを隠す方法ですか?

いいえ、これはキーのない可逆的なテキスト表現であり、誰でもほんの数秒で解読できます。これにより、データはテキストのみのチャネルでも存続します。それは秘密にはなりません。本当に機密性の高いものはすべて暗号化が必要であり、暗号化された結果は転送用に Base64 でエンコードされることが多く、これが混乱の原因です。

Base64 が入力より長いのはなぜですか?

4 つの出力文字には 3 つの入力バイトが含まれるため、出力のサイズはおよそ 4/3 に最大 2 つのパディング文字を加えたものになります。それはフォーマットに固有のものです。サイズが重要な場合は、エンコード前に圧縮してください。base64 出力の圧縮率が低いため、エンコード後は決して圧縮しないでください。

どの URL エンコード関数を使用すればよいですか?

URL に挿入する単一の要素 (クエリ値、パス セグメント、フラグメント) に対して encodeURIComponent を使用します。 encodeURI は、スペースまたは非 ASCII のみを含む、すでに構造化された URL 全体がある場合にのみ使用してください。クエリ文字列を作成する場合は、URLSearchParams をお勧めします。これにより、正しいルールが適用され、スペースプラスの差分が処理されます。

デコーダが「不正な URI」をスローするのはなぜですか?

入力内の % の後に 2 つの 16 進数が続いていないためです。通常、テキストには、エンコードされていないリテラルのパーセント記号 (「50% off」) が含まれています。リテラルのパーセントは %25 として記述する必要があります。ここの URL ユーティリティは、単に拒否するのではなく、問題のあるエスケープの正確な位置を報告します。

SHA-256 を使用してパスワードを保存できますか?

いいえ、SHA-256 は設計上高速です。つまり、データベースを盗む攻撃者は、汎用ハードウェア上で 1 秒あたり数十億のパスワード候補をテストできることになります。パスワードには、Argon2id、scrypt、または bcrypt など、低速でメモリに負荷がかかるソルト関数が必要です。これは、この分野で最もよくある重大な間違いです。

壊れているのに、SHA-1 がまだ存在しているのはなぜですか?

すでに存在する SHA-1 値 (git オブジェクト ID、古い TLS 証明書フィンガープリント、レガシー API リクエスト署名) を再現する必要があるためです。相互運用性の値を計算できることは、セキュリティのためにその値に依存することとは異なります。このツールキット内で SHA-1 が表示されるすべての場所には、それに応じたラベルが付けられます。

2 つのツールが同じテキストに対して異なるハッシュを与えるのはなぜですか?

ほとんどの場合、アルゴリズムではなくバイトの違いが生じます。通常の原因は、末尾の改行 (ファイルは改行で終わりますが、テキスト ボックスはそうでない場合があります)、テキスト エンコーディングの違い、行末の CRLF と LF です。このツールは、入力した内容の UTF-8 bytes をハッシュし、バイト数を表示します。これにより、通常、不一致が明らかになります。

制限事項

  • 名前付き HTML エンティティ テーブルは、実用的なサブセット (マークアップに重要な文字、タイポグラフィ、通貨、矢印、数学、ギリシャ文字、ラテン文字 1) をカバーしますが、すべての 2,231 HTML5 名前付き参照ではありません。認識できない名前は報告され、推測ではなく書かれたとおりに残されます。
  • エンティティのデコードには終了セミコロンが必要です。 HTML5 は、少数の従来の参照をそのまま許容しますが、それらを正しくデコードできるかどうかは、スタンドアロンのテキスト ツールにはない周囲のマークアップ コンテキストに依存します。
  • Web 暗号化が公開されないため、ハッシュと UUID の生成には安全なコンテキスト (https:// または localhost) が必要です。このツールは、弱い実装を置き換えるのではなく、これを報告します。
  • SHA-1、SHA-256、SHA-384、および SHA-512 のみが利用可能です。これらは SubtleCrypto が実装しているものであるためです。 MD5 は選択的にも必然的にも存在しません。
  • ここには HMAC、キーの導出、暗号化はありません。これらにはキー管理が必要ですが、これはインターネット上で見つけたページで処理すべきものではありません。
  • すべてが 1 つのブラウザ タブで実行されるため、すべてがデバイスのメモリによって制限されます。入力はユーティリティごとに数メガバイトに制限されており、ツールはサイズを超える作業をフリーズするのではなく拒否します。