現在JavaScriptの設定が無効になっています。このページを利用するにはJavaScriptの設定を有効にしてください。2016の4日目の記事です。一覧はこちらこんにちは。Node.js言語サポートチームの加藤佑典です。普段はYahoo!ブックストアの開発/運用を主にしています。2016の発表でも少し触れていますが、今期から社内でNode.jsの言語サポートチームが発足しました。APIの変更点について触れたいと思います。Documentationをベースに書いています。はじめに勉強会は、以下のようにAPIをカテゴリ分けし、基礎的なAPIであるカテゴリー1のものから順に進めています。APIの担当になった際に「今までBufferは(fsなどで利用してはいたものの、あまり意識しては)使ったことないな...」とつぶやいたところ、「Bufferを使ってないのはNode.jsを使っていないのと同じだ」と怖いおじさんに指摘されました。そのBufferの概要について軽く触れたいと思います。Bufferことはじめ(基礎的な内容のため、ご存知の方は飛ばして頂いて構いません。)Bufferは、バイナリデータをNode.jsで取り扱うためのクラスです。SystemモジュールやTLS/SSLモジュールなど、Node.jsの中で広く利用されています。JavaScriptでバイナリデータというと、Uint8ArrayなどのTypedArrayが想像できると思います。APIをベースとした実装になっています。ES6導入以前は独自実装されていました。そのため、今もBufferには様々メソッドが生えており、TypedArrayの仕様とは微妙に互換性がありません。一例として、slice()の挙動の違いが挙げられます。Buffer#sliceの実装は、既存のBufferのコピーなしで作成するのに対し、TypedArray#sliceの実装はコピーを作成するため、動作に違いが出ます。Buffer()について今までBufferを扱うためには、以下のようにBufferコンストラクタを使って生成する必要がありました。共に非推奨になりました。Buffer()の仕様について確認したいと思います。以下のコードを御覧ください。break;}console.log(string);そして実行した結果です。Buffer(sec)した内容が丸見えになっているのがわかると思います。byte分の初期化されていない領域を確保します。この初期化されていないBufferのデータには、コードの別のところで定義されているパスワードや個人情報などの重要なデータが含まれてしまっている可能性があります。Buffer(size)を利用する場合は.fill(0)などを用いて初期化するのが一般的です。Buffer(size)の挙動のおさらいをしました。Buffer(size)はデフォルトでは初期化されていないデータを確保します。未初期化データがコード内に留まっている場合は問題ないのですが、使い方によっては思わぬ危険を招くことになります。以下は簡易的にサーバを立てたサンプルコードですが、このコードには危険な箇所が含まれています。})})server.listen(8080)(引用元:https://github.com/nodejs/node/issues/4660)問題なさそうに見えるコードですが、body.strに注目してみましょう。body.strはString型が来るように見えますが、bodyは外部からのリクエストを受け付けているため、ここの値がStringである保証はありません。}のようなデータが送信されてくる可能性もあります。あとはわかりますよね。Buffer(size)のように初期化されていない領域が見えてしまいます。(悪用される可能性の一例)このように、入力チェックが不十分で、意図せず数値が渡った場合、上図のような危険性をはらみます。Buffer()が引数に様々な型を受けることができる仕様のため起きる問題です。この危険性は、記憶に残っているエンジニアも多いと思いますが、OpenSSLのHeartbleedのように機密性の高いデータが見ることができてしまう可能性があるということです。事実として、今年1月、WebSocketモジュールでこのBufferの危険性が元になった脆弱性が見つかり、修正されました。WebSocketモジュールでは、client.ping()時の引数に数値が送られた場合、初期化されていないバッファが返される状態になっていました。Buffer()の挙動を変更すべきかどうかは大きな議論になりました。初期化をしてきたコードに対しては2重に初期化をされてしまうことになり、少なからずパフォーマンスに悪影響を与えることになるからです。Buffer()の挙動の変更する方向ではなく、新しいAPIを追加することになりました。Buffer.from/Buffer.alloc/Buffer.allocUnsafeその新しく追加されたAPIがBuffer.fromとBuffer.allocとBuffer.allocUnsafeです。Buffer()はBuffer.from()へ移行が推奨されています。Buffer(size)と同様の問題が起きないかを確認しましょう。(events.js:101:20)>このように、Buffer.fromにはNumber型は用意されていません。alloc,allocUnsafeは関数名からもわかるように、allocの場合は0フィルされるように、allocUnsafeは初期化されていないものとなっています。v4.x系へのバックポート今回のBuffer.fromとBuffer.alloc,Buffer.allocUnsafeに関しては6系だけではなく、v4.5.0に対してもバックポートされています。Buffer()の件がセキュリティ上の深刻な問題のため、LTS(v4)へも例外的に新規APIとして追加された形になります。そのため、現在4系を利用している場合は4系(LTS)のまま置き換えることが可能です。それ以前のバージョンにはバックポートされていないため、v6.9.x(LTS)へ移行しましょう。Buffer()は新しく作成されたAPIへの移行するのが推奨されています。npmからインストールされているモジュールが利用している場合もありえますし、修正すべてを短期間で行うことは現実的ではない場合もあると思います。そんなときは—zero-fill-buffersを起動時につけることによって、簡単にこれらの危険を排除することができます。SlowBufferなどを含めたすべてのBufferは自動的に0フィルされるようになります。SlowBufferなど)のデフォルトの動作を変更するオプションです。0フィルしている場合は、前述した通り2重で0フィルすることになるため、パフォーマンスに少なからず影響が出る可能性があることを考慮の上、利用しましょう。個人的には、修正範囲が多岐にわたる場合や外部モジュールの修正をしないといけない場合は、この起動オプションを用いて危険性を取り除いたあとにきちんとBuffer.fromなどへ移行することをオススメしたいと思います。パフォーマンスが悪化する可能性が高いため、そのあたりは時と場合によって判断しましょう。こちらの—zero-fill-buffersオプションも、新しいAPIと同様にv4系へのバックポートがされています。繰り返しになりますが、それ以前のバージョンにはバックポートされていないため、v6.9.x(LTS)へ移行しましょう。ついて—zero-fill-buffersオプションについてという、Buffer周りのトピックを取り扱いました。「今年の危険性は今年のうちに」ということで、ここまで読まれた方は今年中に手元のコードを一度見直していただければと思います。言語サポートチームから情報発信していく予定です。JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。JAPANのサービス開発を支える技術やデザイン、文化を発信しています。Reserved.このページの先頭へ。