22title : SubtleCrypto の暗号以外の使用法
33slug : Web/API/Web_Crypto_API/Non-cryptographic_uses_of_subtle_crypto
44l10n :
5- sourceCommit : e259c14eb7c72abb6007478aadd52aa25c7976d8
5+ sourceCommit : 26fb7eaa7b398a35c2463fa15ab6ccfa46a9e06d
66---
77
8- {{APIRef("Web Crypto API")}}
9- この記事では、[ SubtleCrypto インターフェイス] ( /ja/docs/Web/API/SubtleCrypto ) の [ ` digest ` ] ( /ja/docs/Web/API/SubtleCrypto/digest ) メソッドの使い道に焦点を当てます。[ Web Crypto API] ( /ja/docs/Web/API/Web_Crypto_API ) に含まれる他の多くのメソッドの用途は暗号関係のかなり限られたものですが、データのハッシュを求めること (これが ` digest ` メソッドが行うことです) には多くの便利な利用法があります。
8+ {{DefaultAPISidebar("Web Crypto API")}}
9+
10+ この記事では、[ SubtleCrypto インターフェイス] ( /ja/docs/Web/API/SubtleCrypto ) の [ ` digest ` ] ( /ja/docs/Web/API/SubtleCrypto/digest ) メソッドの使い道に焦点を当てます。[ ウェブ暗号化 API] ( /ja/docs/Web/API/Web_Crypto_API ) に含まれる他の多くのメソッドの用途は暗号関係のかなり限られたものですが、データのハッシュを求めること (これが ` digest ` メソッドが行うことです) には多くの便利な利用法があります。
1011
1112この記事では、[ SubtleCrypto インターフェイス] ( /ja/docs/Web/API/SubtleCrypto ) の暗号関係の使い道は扱いません。この記事から学ぶべき重要なことは、強力で低レベルなので、製品での暗号関係の目的には ** この API を使ってはいけない** ということです。正しく使うためには、暗号関係のタスクを正しく成し遂げるために多くの状況依存の手順を踏まなければならないでしょう。もしこれらの手順のうち 1 個でも間違って行われると、よくてあなたのコードは動かず、より悪い場合にはそれは _ 動き_ 、知らないうちに安全でない製品でユーザーを危険に晒すことになるでしょう。
1213
13- [ Web Crypto API] ( /ja/docs/Web/API/Web_Crypto_API ) を使う必要はまったくないかもしれません。暗号を使いたくなるであろう目的の多くは、既に解決され、ウェブプラットフォームの一部になっています。例えば、クライアントとサーバーの間で情報を読んでいる Wi-Fi ホットスポットのような中間者攻撃が心配なのであれば 、[ HTTPS] ( /ja/docs/Glossary/HTTPS ) を確実に正しく使うことで解決できます。ユーザー間で情報を安全に送信したいですか?それなら、標準の一部として暗号化することになっている [ WebRTC Data Channels ] ( /ja/docs/Web/API/WebRTC_API/Using_data_channels ) を用いてユーザー間のデータ接続を構築できます。
14+ [ ウェブ暗号化 API] ( /ja/docs/Web/API/Web_Crypto_API ) を使う必要はまったくないかもしれません。暗号を使いたくなるであろう目的の多くは、既に解決され、ウェブプラットフォームの一部になっています。例えば、クライアントとサーバーの間で情報を読んでいる Wi-Fi ホットスポットのような [ 中間者攻撃 (MITM) ] ( /ja/docs/Web/Security/Attacks/MITM ) が心配なのであれば 、[ HTTPS] ( /ja/docs/Glossary/HTTPS ) を確実に正しく使うことで解決できます。ユーザー間で情報を安全に送信したいですか?それなら、標準の一部として暗号化することになっている [ WebRTC データチャンネル ] ( /ja/docs/Web/API/WebRTC_API/Using_data_channels ) を用いてユーザー間のデータ接続を構築できます。
1415
1516[ SubtleCrypto インターフェイス] ( /ja/docs/Web/API/SubtleCrypto ) は暗号を扱うための低レベルのプリミティブを提供しますが、それらの道具を用いてシステムを実装することは複雑なタスクです。間違いに気付くのは難しく、結果としてユーザーのデータがあなたが考えるより安全でなくなる可能性があります。このことにより、ユーザーがセンシティブなデータや重要なデータを共有している場合、悲惨な結果が生じる可能性があります。
1617
1718疑問がある場合は、自分でやろうとせず、経験者を雇い、ソフトウェアがセキュリティの専門家によって確実に監査されるようにしてください。
1819
1920## ファイルのハッシュ化
2021
21- これは、[ Web Crypto API] ( /ja/docs/Web/API/Web_Crypto_API ) を用いてできる最も単純で役立つものです。鍵や証明書の生成は不要で、1 個だけの手順でできます。
22+ これは、[ ウェブ暗号化 API] ( /ja/docs/Web/API/Web_Crypto_API ) を用いてできる最も単純で役立つものです。鍵や証明書の生成は不要で、1 個だけの手順でできます。
2223
23- [ ハッシュ化 ] ( /ja/docs/Glossary/Hash_function ) は、大きなバイト列を小さい文字列に変換し、この長い列の中の小さな変更が小さい文字列の大きな変化を生む技術です。この技術は、それぞれのファイルの全バイトをチェックせずに 2 個の同一のファイルを特定するのに便利です。これは、比較対象として単純な文字列が手に入るので、非常に便利です。明確にしておくと、ハッシュ化は ** 一方向の** 操作です。ハッシュからもとのバイト列を生成することはできません。
24+ {{glossary("Hash function", "ハッシュ化")}} は、大きなバイト列を小さい文字列に変換し、この長い列の中の小さな変更が小さい文字列の大きな変化を生む技術です。この技術は、それぞれのファイルの全バイトをチェックせずに 2 個の同一のファイルを特定するのに便利です。これは、比較対象として単純な文字列が手に入るので、非常に便利です。明確にしておくと、ハッシュ化は ** 一方向の** 操作です。ハッシュからもとのバイト列を生成することはできません。
2425
2526生成された 2 個のハッシュが同じだが、それらを生成するのに用いたファイルが違う場合は、_ ハッシュ衝突_ として知られます。これは偶然起こる可能性は非常に小さく、SHA256 のような安全なハッシュ関数では、人工的に起こすこともほぼ不可能です。そのため、2 個の文字列が同じ場合は、2 個のもとのファイルも同一であると合理的に確信することができます。
2627
@@ -46,12 +47,19 @@ SubtleCrypto の [`digest()`](/ja/docs/Web/API/SubtleCrypto/digest) メソッド
4647 >ハッシュ化するファイルを選択 (複数可)
4748 <input type =" file" id =" file" name =" file" multiple
4849/></label >
49- <output style =" display :block ;font-family :monospace ;" ></output >
50+ <output ></output >
51+ ```
52+
53+ ``` css hidden
54+ output {
55+ display : block ;
56+ font-family : monospace ;
57+ }
5058```
5159
5260次に、SubtleCrypto インターフェイスを用いて処理します。以下のように動作します。
5361
54- - [ FileReader ] ( /ja/docs/Web/API/FileReader ) を用い、ファイルを [ ArrayBuffer ] ( /ja/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer ) に読み込みます 。
62+ - ファイルを {{jsxref("ArrayBuffer")}} に、{{domxref("File")}} オブジェクトの {{domxref("Blob.arrayBuffer()", "arrayBuffer()")}} メソッドを用いて読み込みます 。
5563- ` crypto.subtle.digest('SHA-256', arrayBuffer) ` を用いて ` ArrayBuffer ` のダイジェストを求めます。
5664- 結果のハッシュ (別の ` ArrayBuffer ` ) を表示できるように文字列に変換します。
5765
@@ -62,20 +70,27 @@ const file = document.getElementById("file");
6270// ユーザーが 1 個以上のファイルを選択した時、ハッシュ化関数を実行する
6371file .addEventListener (" change" , hashTheseFiles);
6472
65- // digest 関数は非同期であり、Promise を返す
73+ // digest 関数は非同期であり、プロミスを返す
6674// コードを単純にするため、async/await 構文を用いる
6775async function fileHash (file ) {
6876 const arrayBuffer = await file .arrayBuffer ();
6977
70- // subtle crypto API を用い、ファイルが入った ArrayBuffer の SHA256 を求める
71- // 結果のハッシュは ArrayBuffer に格納される
78+ // subtle crypto API を用い、ファイルが入った ArrayBuffer の
79+ // SHA256 を求める。 結果のハッシュは ArrayBuffer に格納される
7280 const hashAsArrayBuffer = await crypto .subtle .digest (" SHA-256" , arrayBuffer);
7381
74- // 文字列として表示するため、ArrayBuffer の各バイトの十六進表現を求める
75- // まず、ArrayBuffer の各バイトそれぞれを要素とする配列を作成する
82+ // これを文字列として表示させるには、配列バッファーのそれぞれの
83+ // バイトの 16 進値を取得する。これにより、配列バッファーの
84+ // それぞれのバイトが配列の 1 つのアイテムとなる配列が得られる。
7685 const uint8ViewOfHash = new Uint8Array (hashAsArrayBuffer);
77- // 次に、各要素を十六進文字列に変換するため、通常の配列に変換する
78- // 0-9 または a-f の各文字が 0 と 16 の間の数値を表し、4 ビットの情報を保持するので、2 文字で 8 ビット (1 バイト) を表す
86+ if (uint8ViewOfHash .toHex ) {
87+ // 下記ロジックは、2025 年に導入された toHex() メソッドと同等
88+ return uint8ViewOfHash .toHex ();
89+ }
90+ // 次に、各要素を十六進文字列に変換するため、
91+ // 通常の配列に変換する 0-9 または a-f の各文字が
92+ // 0 と 15 の間の数値を表し、4 ビットの情報を保持するので、
93+ // 2 文字で 8 ビット (1 バイト) を表す
7994 const hashAsString = Array .from (uint8ViewOfHash)
8095 .map ((b ) => b .toString (16 ).padStart (2 , " 0" ))
8196 .join (" " );
@@ -87,9 +102,9 @@ async function hashTheseFiles(e) {
87102 // ファイル選択欄の各ファイルについて処理を行う
88103 for (const file of this .files ) {
89104 // ハッシュを求め、出力要素に並べる
90- outHTML += ` ${ file .name } ${ await fileHash (file)} ` ;
105+ outHTML += ` ${ file .name } ${ await fileHash (file)} \n ` ;
91106 }
92- output .innerHTML = outHTML;
107+ output .innerText = outHTML;
93108}
94109```
95110
@@ -100,7 +115,7 @@ async function hashTheseFiles(e) {
100115この時点で、「_ これを自分のウェブサイトで使って、ユーザーがファイルをダウンロードするとき、ハッシュが一致することを保証し、ユーザーに自分のダウンロードが安全であると安心させることができる_ 」と自分で考えているかもしれません。残念ながら、ここにはすぐに頭に浮かぶ 2 個の懸念点があります。
101116
102117- 実行可能ファイルのダウンロードは ** 常に** HTTPS で行われるべきです。これにより中間者は攻撃できなくなるため、冗長かもしれません。
103- - 攻撃者がもとのサーバー上のダウンロードファイルを置き換えることができる場合、SubtleCrypto インターフェイスを呼び出すコードも単に置き換え、バイパスし、全部大丈夫ですとだけ表示するようにすることもできるでしょう。自分のコードで見つけるのは難しいような[ 厳密な等価性比較] ( /ja/docs/Web/JavaScript/Guide/Equality_comparisons_and_sameness#_による厳密な等価性 ) の置き換えなど、こっそりと行われる可能性が高いです。
118+ - 攻撃者がもとのサーバー上のダウンロードファイルを置き換えることができる場合、SubtleCrypto インターフェイスを呼び出すコードも単に置き換え、バイパスし、全部大丈夫ですとだけ表示するようにすることもできるでしょう。自分のコードで見つけるのは難しいような[ 厳密な等価性比較] ( /ja/docs/Web/JavaScript/Guide/Equality_comparisons_and_sameness#による厳密な等価性 ) の置き換えなど、こっそりと行われる可能性が高いです。
104119
105120 ``` diff
106121 --- if (checksum === correctCheckSum) return true;
@@ -132,7 +147,7 @@ SHA1 ハッシュのあり得る種類は非常に多いです。そのため、
132147
133148Git は、SHA1 ハッシュを 2 通りの興味深い使い方をする、素晴らしい例です。ファイルが Git に保存されるときは、SHA1 ハッシュによって参照されます。これにより、Git がデータを素早く探し、ファイルを取り出せるようになります。
134149
135- ただし、単にファイルの内容をハッシュに使っているわけではなく、UTF8 の文字列 ` "blob " ` 、十進数で現したバイト単位のファイルサイズ、ナル文字 (JavaScript では ` "\0" ` と書ける) をこの順で前につけます。JavaScript の文字列は UTF16 なので、[ Encoding API] ( /ja/docs/Web/API/Encoding_API ) の [ TextEncoder インターフェイス] ( /ja/docs/Web/API/TextEncoder ) を用いて UTF8 の文字列をエンコードできます。
150+ ただし、単にファイルの内容をハッシュに使っているわけではなく、UTF8 の文字列 ` "blob " ` 、十進数で現したバイト単位のファイルサイズ、ヌル文字 (JavaScript では ` "\0" ` と書ける) をこの順で前につけます。JavaScript の文字列は UTF16 なので、[ Encoding API] ( /ja/docs/Web/API/Encoding_API ) の [ TextEncoder インターフェイス] ( /ja/docs/Web/API/TextEncoder ) を用いて UTF8 の文字列をエンコードできます。
136151
137152以下のコードは、SHA256 の例と同様に、ファイルからこれらのハッシュを求めるのに用いることができます。ファイルをアップロードするための HTML は同じですが、Git が行うのと同様のサイズ情報を前につける処理を追加しています。
138153
@@ -144,7 +159,14 @@ Git は、SHA1 ハッシュを 2 通りの興味深い使い方をする、素
144159 <input type =" file" id =" file" name =" file" multiple
145160/></label >
146161
147- <output style =" display :block ;font-family :monospace ;" ></output >
162+ <output ></output >
163+ ```
164+
165+ ``` css hidden
166+ output {
167+ display : block ;
168+ font-family : monospace ;
169+ }
148170```
149171
150172``` js
@@ -155,17 +177,18 @@ file.addEventListener("change", hashTheseFiles);
155177async function fileHash (file ) {
156178 const arrayBuffer = await file .arrayBuffer ();
157179
158- // Git はナル終端の文字列 'blob 1234' (1234 はファイルサイズを表す)
180+ // Git はヌル終端の文字列 'blob 1234' (1234 はファイルサイズを表す)
159181 // をハッシュ化前に前置するので、これを再現する
160182
161183 // まず、ファイルのバイト単位のサイズを求める
162184 const uint8View = new Uint8Array (arrayBuffer);
163185 const length = uint8View .length ;
164186
165187 // git は端末では文字列に UTF8 を用いるが、ウェブでは UTF16 を用いる
166- // メッセージ中の文字のバイナリ表現が違うとハッシュも変わるので、エンコーダーを使う必要がある
188+ // メッセージ中の文字のバイナリー表現が違うとハッシュも変わるので、
189+ // エンコーダーを使う必要がある
167190 const encoder = new TextEncoder ();
168- // ナル終端とは、文字列がナル文字 (JavaScript では '\0') で終わるということ
191+ // ヌル終端とは、文字列がヌル文字 (JavaScript では '\0') で終わるということ
169192 const view = encoder .encode (` blob ${ length} \0 ` );
170193
171194 // そして、2 個の ArrayBuffer を合体して 1 個の ArrayBuffer にする
@@ -190,9 +213,9 @@ function hashToString(arrayBuffer) {
190213async function hashTheseFiles (e ) {
191214 let outHTML = " " ;
192215 for (const file of this .files ) {
193- outHTML += ` ${ file .name } ${ await fileHash (file)} ` ;
216+ outHTML += ` ${ file .name } ${ await fileHash (file)} \n ` ;
194217 }
195- output .innerHTML = outHTML;
218+ output .innerText = outHTML;
196219}
197220```
198221
@@ -208,9 +231,9 @@ async function hashTheseFiles(e) {
208231
209232出典: [ How is git commit sha1 formed] ( https://gist.github.com/masak/2415865 )
210233
211- 本質的には、以下の UTF8 文字列です。(ナル文字は ` \0 ` で表現されています)
234+ 本質的には、以下の UTF8 文字列です。(ヌル文字は ` \0 ` で表現されています)
212235
213- ```
236+ ``` plain
214237commit [size in bytes as decimal of this info]\0tree [tree hash]
215238parent [parent commit hash]
216239author [author info] [timestamp]
0 commit comments