ファイルのアップロードを処理する特定のWebアプリケーションのコードを見ています。何らかの理由で、暗号化ハッシュ関数(この場合はSHA-256)を使用する代わりに、IDをそこから導出し、代わりにどこにでも使用して、ファイルを一意に識別します。
必要な手順は次のとおりです。
0-9a-zA-Z => 0 - 62
)。例えば:
hash (file) = 26ba0a896923d2de4cad532a3f05da725d9cc08d371eaf96905f5bbc1901b56f
26b -------> 9Z
a0a -------> Fs
896 -------> zs
923 -------> BJ
d2d -------> Sp
e4c -------> X2
ad5 -------> IJ
32a -------> d4
3f0 -------> gg
5da -------> oa
725 -------> tv
d9c -------> Uc
c08 -------> NG
d37 -------> Sz
1ea -------> 7U
f96 -------> 12m
905 -------> Bf
f5b -------> 11p
bc1 -------> Mx
901 -------> Bb
b56 -------> KO
f -------> f
ID = 9ZFszsBJSpX2IJd4ggoatvUcNGSz7U12mBf11pMxBbKOf
私にとって、これはハッシュを切り捨てる安全な方法ではないようです。特に、衝突の確率がこのように増加するように思えます。*
上記の操作は問題を引き起こしますか、それともSHA256の暗号強度に干渉しませんか?
* SHA-2機能の抵抗により、攻撃者がこれを悪用することを防ぐことができます。ただし、機能自体の前提だけが気になります。
これはほぼ完全に優れたプラクティスですが、少し欠点があります。
一般的に、ハッシュは単なる数値であり、好きなベースで表現できます。たとえば、ハッシュをバイナリに変換して、base64として表すことができます。
2 6 b a ...
| | | |
0010011010111010 ...
| |
T u
ただし、ここでのアプローチの深刻な問題は、出力のクラスタリングです。 3桁の16進数は、1、2、または3つのbase62桁に変換されます。 base62値をクラスター化する方法を決定する信頼できる方法はありません。先行ゼロがある場合(つまり、3つの16進数を3つのbase62桁に変換した場合)、および/またはより大きな基数を使用した場合(たとえば、3つの16進数字は、先行ゼロのある2つのbase128桁にマップできます)、この問題を回避できます。
これの実際的な例を見るには、hex f43
がbase62 111
にマップし、03f
がbase62 11
にマップすることを考慮してください。次のハッシュのbase62形式を区別できないことを考慮してください。
f43f43f43f43f43f43f43f43f43f4303f03f03f03f03f03f03f03f03f03f9991
03f03f03f03f03f03f03f03f03f03ff43f43f43f43f43f43f43f43f43f439991
03ff4303ff4303ff4303ff4303ff4303ff4303ff4303ff4303ff4303ff439991
これらのハッシュはすべて、
11111111111111111111111111111111111111111111111111CC1
どの1
sが3文字グループの一部であり、どれが2文字グループの一部であるかを知る方法はありません。明らかにこれは極端な例ですが、あいまいな先頭の1
がグループにある場合は常に問題が発生します。
ただし、3桁および1桁の出力グループは、グループが取り得る4096の値のうち314に対してのみ発生し、これらのケースの一部についてのみあいまいさが生じます。以下の Gilles からのコメントは、結果の切り捨てられた値が254ビットを保持すると推定しています。
私たちが知る限り、SHA-2ハッシュのビットは独立しています。この切り捨てはビットを正確に除去するわけではありませんが、独立している必要があるほど十分に近くなっています。非一意性は、16進3桁あたりlg(12³-62²)≈0.1ビットのみに関係するため、結果はおおよそ254ビットハッシュの強度を持つはずです。
2ビットの損失は明らかに最適ではありませんが、壊滅的な損失には程遠いものです。
私が見ることができるものから、これはまったく切り捨てではありません。各12ビットセクション(3 ASCII 16進文字)は、対応するbase62表現に変換されます。これは 全単射 演算です。右側の値とそれらを左側の値に戻します。
操作は値をtruncateしませんが、生のハッシュのbase64値を計算するのと同じように、より効率的なエンコーディングを使用して結果の長さを減らしますバイトでしょう。
「切り捨て」とは、一部を完全に削除することを意味します。この例では、ハッシュ文字の右半分を切り捨てると、残りは次のようになります。26ba0a896923d2de4cad532a3f05da72
つまり、切り捨てによって衝突が増えることになりますが、ここではそうではありません。
ハッシュの16進数表現の長さが許容できず、制限された文字セットを使用して短い文字列でハッシュを一意に表現したい場合、base-64ではなくbase-64を使用すると、簡単にマッピングできます(交換する .
および/
異なる文字を含む);許容できる文字数が62文字しかない場合、データを64ビットのチャンクに分割し、11の基本62文字を使用してそれぞれを44の固定合計長で格納し、43文字を使用した最適な固定長エンコーディングよりも1文字だけ多い(あなたのエンコーディングは時々43文字を使用しますが、時にはそれ以上を必要とし、一意ではありません)。 base-62で64ビットをエンコードすることは、64ビットの符号なし整数型を持つすべてのプラットフォームでかなり簡単です。そうでないプラットフォームでは、53ビットを11 base-31文字としてエンコードし、残りの11ビットの1つをbase-31文字のそれぞれに追加して、base-32文字を生成できます。
本当に良い答えを出すのに十分な情報がないと思います。このアプローチで考えられる「弱点」は、表現の長さを短くすることで、衝突の変化が大きくなることです。ハッシュが異なる2つのファイルは、同じ変換されたコードになる可能性があります。ただし、アプリケーションによっては、これは問題にならない場合があります。または、表示の長さを短くする必要よりも、衝突の危険性の方が重要ではない場合があります。判断するのに十分な情報が本当にありません。
ただし、一見すると、表現の長さが最小限であることを考えると、衝突の可能性が高まることを正当化するのは難しいようです。特に、表現の長さが問題である場合は、保存するこれらのハッシュの多くは、衝突が発生する可能性が高いことを意味します。繰り返しになりますが、衝突の可能性が問題にならない場合は、短いシグネチャを比較することで、効率が向上するのではないでしょうか。