LoginSignup
3
4

More than 5 years have passed since last update.

日本語文字列をnode.jsのcryptoでハッシュ化する時はエンコーディングを明示すべき

Posted at

環境

  • node
    • v4.4.4
    • v6.0.0

概要

同じ日本語を crypto でハッシュ化する時にencodingを指定しないと、 v4.4.4v6.0.0 で結果に違いが出るから、有無を言わず指定しておかないと後で怖い思いするかも

正しいHash値

echo -n "ほげえ" | sha1sum
05ac5a4890e77393c279714ea453a00bec0cd994  -

:interrobang: 何も考えず node で実行すると...

$ node -e "console.log(require('crypto').createHash('sha1').update('ほげえ').digest('hex'))"
12009e955051b6f293e873ef48ce3786af673cfb

05ac5a4890e77393c279714ea453a00bec0cd994 != 12009e955051b6f293e873ef48ce3786af673cfb

なぜ違うのだ...

:white_check_mark: 解決方法

update関数の引数でencodingを明示的に指定する

$ node -e "console.log(require('crypto').createHash('sha1').update('ほげえ', 'utf8').digest('hex'))"
05ac5a4890e77393c279714ea453a00bec0cd994

原因

v4.4.4 において update 関数の実装は↓

$ node -e "console.log(require('crypto').createHash('sha1').update.toString())"
function (data, encoding) {
  encoding = encoding || exports.DEFAULT_ENCODING;
  if (encoding === 'buffer' && typeof data === 'string')
    encoding = 'binary';
  this._binding.update(data, encoding);
  return this;
}

encodingが未指定だと、DEFAULT_ENCODING が使われている。
DEFAULT_ENCODINGには buffer が入っている。

$ node -e "console.log(require('crypto').DEFAULT_ENCODING.toString())"
buffer

なので結局 ↓ を通り

  if (encoding === 'buffer' && typeof data === 'string')
    encoding = 'binary';

binary エンコードとして渡ってしまう。
binaryを指定するとどうなるのか公式ドキュメント1にあたってみると、

'binary' - A way of encoding the buffer into a one-byte (latin-1) encoded string. The string 'latin-1' is not supported. Instead, pass 'binary' to use 'latin-1' encoding.

latin-1 エンコードに変換されるという。
つまりは、↓のようにバイトコードが変わってしまう。

$ node -e "console.log(new Buffer('ほげえ', 'utf8'))"
<Buffer e3 81 bb e3 81 92 e3 81 88>
$ node -e "console.log(new Buffer('ほげえ', 'binary'))"
<Buffer 7b 52 48>

なので、全く別の文字列をハッシュ化したことになる。

v6.0.0 の場合

ちなみに node.js v6.0.0 だと utf8 を明示的にしていしなくても良いようになっている。

v6.0.0の場合
$ node -e "console.log(require('crypto').createHash('sha1').update('ほげえ').digest('hex'))"
05ac5a4890e77393c279714ea453a00bec0cd994

v6.0.0 では実装が変わっていて、 encodingが未定義だと exports.DEFAULT_ENCODING がそのまま渡されるようになっている。

$ node -e "console.log(require('crypto').createHash('sha1').update.toString())"
function (data, encoding) {
  encoding = encoding || exports.DEFAULT_ENCODING;
  this._handle.update(data, encoding);
  return this;
}

DEFAULT_ENCODINGbuffer のままで変わらず。

$ node -e "console.log(require('crypto').DEFAULT_ENCODING)"
buffer

new Buffer('ほげえ', 'buffer') はエラーになるので、 デフォルトのencodingを使う、という意味で、new Buffer('ほげえ') が渡されていると思う(想像)
デフォルトのencodingはutf8なので、v6.0.0では何も考えず日本語をハッシュ化できる。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4