Porting ke Buffer.from()/Buffer.alloc() API
Pratinjau
Panduan ini menjelaskan cara bermigrasi ke metode konstruktor Buffer yang aman. Migrasi memperbaiki peringatan penghentian berikut:
Konstruktor Buffer() dan new Buffer() tidak direkomendasikan untuk digunakan karena masalah keamanan dan penggunaan. Mohon gunakan metode konstruksi Buffer.alloc(), Buffer.allocUnsafe(), atau Buffer.from() yang baru.
- Varian 1: Hilangkan dukungan untuk Node.js 4.4.x dan 5.0.0 — 5.9.x (direkomendasikan)
- Varian 2: Gunakan polyfill
- Varian 3: Deteksi manual, dengan pengaman
Menemukan bit kode yang bermasalah menggunakan grep
Jalankan saja grep -nrE '[^a-zA-Z](Lambat)?Buffer\s*\(' --exclude-dir node_modules.
Ini akan menemukan semua tempat yang berpotensi tidak aman dalam kode Anda sendiri (dengan beberapa yang sangat tidak mungkin pengecualian).
Menemukan bit kode yang bermasalah menggunakan Node.js 8
Jika Anda menggunakan Node.js 8.0.0 (yang direkomendasikan), Node.js memperlihatkan beberapa opsi yang membantu menemukan potongan kode yang relevan:
--trace-warningsakan membuat Node.js menampilkan jejak tumpukan untuk peringatan ini dan peringatan lain yang dicetak oleh Node.js.--trace-deprecationmelakukan hal yang sama, tetapi hanya untuk peringatan penghentian.--pending-deprecationakan menampilkan lebih banyak jenis peringatan penghentian. Secara khusus, ini akan menampilkan peringatan penghentianBuffer(), bahkan pada Node.js 8.
Anda dapat mengatur flag ini menggunakan variabel lingkungan:
$ export NODE_OPTIONS='--trace-warnings --pending-deprecation'
$ cat example.js
'use strict';
const foo = new Buffer('foo');
$ node example.js
(node:7147) [DEP0005] DeprecationWarning: The Buffer() and new Buffer() constructors are not recommended for use due to security and usability concerns. Please use the new Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() construction methods instead.
at showFlaggedDeprecation (buffer.js:127:13)
at new Buffer (buffer.js:148:3)
at Object.<anonymous> (/path/to/example.js:2:13)
[... more stack trace lines ...]Menemukan bit kode yang bermasalah menggunakan linter
Aturan ESLint no-buffer-constructor atau node/no-deprecated-api juga menemukan panggilan ke Buffer() API yang tidak digunakan lagi. Aturan-aturan itu termasuk dalam beberapa preset.
Namun, ada kekurangannya, itu tidak selalu bekerja dengan benar saat Buffer ditimpa misalnya dengan polyfill, jadi disarankan adalah kombinasi dari ini dan beberapa metode lainnya dijelaskan di atas.
Varian 1: Hilangkan dukungan untuk Node.js 4.4.x dan 5.0.0 — 5.9.x
Ini adalah solusi yang direkomendasikan saat ini yang hanya menyiratkan overhead minimal.
Jalur rilis Node.js 5.x tidak didukung sejak Juli 2016, dan jalur rilis Node.js 4.x mencapai Akhir Masa Pakainya pada April 2018 (→ Jadwal). Ini berarti bahwa versi Node.js ini tidak akan menerima pembaruan apa pun, bahkan jika ada masalah keamanan, jadi penggunaan jalur rilis ini harus dihindari, jika memungkinkan.
Apa yang akan Anda lakukan dalam kasus ini adalah mengonversi semua panggilan New Buffer() atau Buffer() untuk menggunakan Buffer.alloc() atau Buffer.from(), dengan cara berikut:
- Untuk
Buffer(number) baru, ganti denganBuffer.alloc(number). - Untuk
New Buffer(string)(ataunew Buffer(string, encoding)), ganti denganBuffer.from(string)(atauBuffer.from(string, encoding)). - Untuk semua kombinasi argumen lainnya (ini jauh lebih jarang), ganti juga
new Buffer(...arguments)denganBuffer.from(...arguments).
Perhatikan bahwa Buffer.alloc() juga lebih cepat pada versi Node.js saat ini daripada new Buffer(size).fill(0), yang seharusnya Anda perlukan untuk memastikan zero-filling.
Mengaktifkan aturan ESLint no-buffer-constructor atau node/no-deprecated-api direkomendasikan untuk menghindari penggunaan Buffer API yang tidak aman secara tidak sengaja.
Ada juga JSCodeshift codemod untuk memigrasikan konstruktor Buffer secara otomatis ke Buffer.alloc() atau Buffer.from(). Perhatikan bahwa saat ini hanya berfungsi dengan kasus di mana argumennya literal atau di mana konstruktor dipanggil dengan dua argumen.
Jika saat ini Anda mendukung versi Node.js yang lebih lama dan menghentikan dukungan untuk mereka tidak mungkin, atau jika Anda mendukung cabang paket Anda yang lebih lama, pertimbangkan untuk menggunakan Varian 2 atau Varian 3 di cabang lama, jadi orang yang menggunakan cabang lama itu juga akan menerima perbaikan. Dengan begitu, Anda akan menghilangkan potensi masalah yang disebabkan oleh penggunaan Buffer API yang tidak dijaga dan pengguna Anda tidak akan melihat peringatan penghentian runtime saat menjalankan kode Anda di Node.js 10.
Variant 2: Gunakan polyfill
Ada tiga polyfill berbeda yang tersedia:
-
safer-buffer adalah pengganti drop-in untuk seluruh
BufferAPI, yang akan dilempar saat menggunakannew Buffer().Anda akan mengambil langkah yang sama persis seperti pada Varian 1, tetapi dengan polyfill
const Buffer = require('safer-buffer').Bufferdi semua file tempat Anda menggunakanBufferAPI baru.Jangan gunakan API
new Buffer()yang lama. Dalam file apa pun di mana baris di atas ditambahkan, menggunakannew Buffer()API lama akan throw. -
buffer-from dan/atau buffer-alloc adalah ponyfills untuk masing-masing bagian dari
BufferAPI. Anda hanya perlu untuk menambahkan paket yang sesuai dengan API yang Anda gunakan.Anda akan mengimpor modul yang diperlukan dengan nama yang sesuai, mis.
const bufferFrom = require('buffer-from')lalu gunakan itu sebagai ganti panggilan keBuffer baru(), mis.new Buffer('test')menjadibufferFrom('test').Kelemahan dengan pendekatan ini adalah sedikit lebih banyak perubahan kode untuk dimigrasikan (seperti yang akan Anda lakukan menggunakan misalnya
Buffer.from()dengan nama yang berbeda). -
safe-buffer juga merupakan pengganti drop-in untuk seluruh
BufferAPI, tetapi menggunakannew Buffer()akan tetap berfungsi seperti sebelumnya.Kelemahan dari pendekatan ini adalah Anda juga dapat menggunakan
new Buffer()API . yang lebih lama dalam kode Anda, yang bermasalah karena dapat menyebabkan masalah dalam kode Anda, dan akan mulai memancarkan peringatan penghentian runtime dimulai dengan Node.js 10 (baca selengkapnya di sini).
Perhatikan bahwa dalam kedua kasus tersebut, Anda juga harus menghapus semua panggilan ke Buffer . yang lama API secara manual — hanya memasukkan safe-buffer tidak memperbaiki masalah dengan sendirinya, itu hanya menyediakan polyfill untuk API baru. Saya telah melihat orang-orang melakukan kesalahan itu.
Mengaktifkan aturan ESLint no-buffer-constructor atau node/no-deprecated-api direkomendasikan.
Jangan lupa untuk menghentikan penggunaan polyfill setelah Anda menghentikan dukungan untuk Node.js <4.5.0.
Varian 3 — Deteksi manual, dengan pengaman
Ini berguna jika Anda membuat instance Buffer hanya di beberapa tempat (mis. pembungkus di sekitar mereka.
Buffer(0)
Kasus khusus untuk membuat buffer kosong ini dapat diganti dengan aman dengan Buffer.concat([]), yang mengembalikan hasil yang sama hingga ke Node.js 0.8.x.
Buffer(bukanNumber)
Sebelum:
const buf = new Buffer(notNumber, encoding);Sesudah:
let buf;
if (Buffer.from && Buffer.from !== Uint8Array.from) {
buf = Buffer.from(notNumber, encoding);
} else {
if (typeof notNumber === 'number') {
throw new Error('The "size" argument must be not of type number.');
}
buf = new Buffer(notNumber, encoding);
}encoding adalah opsional.
Perhatikan bahwa typeof notNumber sebelum new Buffer() diperlukan (untuk kasus ketika argumen notNumber tidak hard-coded) dan tidak disebabkan oleh penghentian konstruktor Buffer — justru why the Konstruktor Buffer tidak digunakan lagi. Paket ekosistem yang tidak memiliki jenis pemeriksaan ini menyebabkan banyak masalah keamanan — situasi ketika input pengguna yang tidak bersih dapat berakhir di Buffer(arg) create masalah mulai dari DoS hingga membocorkan informasi sensitif ke penyerang dari memori proses.
Ketika argumen notNumber di-hardcode (misalnya literal "abc" atau [0,1,2]), pemeriksaan typeof dapat dihilangkan.
Juga, perhatikan bahwa menggunakan TypeScript tidak memperbaiki masalah ini untuk Anda — ketika libs ditulis dalam TypeScript digunakan dari JS, atau ketika input pengguna berakhir di sana — ia berperilaku persis seperti JS murni, seperti semua jenis pemeriksaan hanya waktu terjemahan dan tidak ada dalam kode JS aktual yang TS mengkompilasi ke.
Buffer(number)
Untuk dukungan Node.js 0.10.x (dan di bawah):
let buf;
if (Buffer.alloc) {
buf = Buffer.alloc(number);
} else {
buf = new Buffer(number);
buf.fill(0);
}Jika tidak (Node.js 0.12.x):
const buf = Buffer.alloc ? Buffer.alloc(number) : new Buffer(number).fill(0);Tentang Buffer.allocUnsafe()
Berhati-hatilah saat menggunakan Buffer.allocUnsafe():
- Jangan gunakan jika Anda tidak memiliki alasan yang baik untuk
- misalnya Anda mungkin tidak akan pernah melihat perbedaan kinerja untuk buffer kecil, pada kenyataannya, itu mungkin lebih cepat dengan
Buffer.alloc(), - jika kode Anda tidak berada di jalur kode panas — Anda mungkin juga tidak akan melihat perbedaan,
- perlu diingat bahwa pengisian nol meminimalkan potensi risiko.
- misalnya Anda mungkin tidak akan pernah melihat perbedaan kinerja untuk buffer kecil, pada kenyataannya, itu mungkin lebih cepat dengan
- Jika Anda menggunakannya, pastikan Anda tidak pernah mengembalikan buffer dalam keadaan terisi sebagian,
- jika Anda menulisnya secara berurutan — selalu potong ke panjang tulisan yang sebenarnya
Kesalahan dalam menangani buffer yang dialokasikan dengan Buffer.allocUnsafe() dapat mengakibatkan berbagai masalah, berkisar dari perilaku kode Anda yang tidak terdefinisi hingga data sensitif (input pengguna, kata sandi, sertifikat) bocor ke penyerang jarak jauh.
Perhatikan bahwa hal yang sama berlaku untuk penggunaan new Buffer() tanpa pengisian nol, tergantung pada Node.js versi (dan kurangnya pemeriksaan tipe juga menambahkan DoS ke daftar potensi masalah).
Pertanyaan Umum (FAQ)
Apa yang salah dengan konstruktor Buffer?
Konstruktor Buffer dapat digunakan untuk membuat buffer dengan berbagai cara:
New Buffer(42)membuatBuffersebesar 42 byte. Sebelum Node.js 8, buffer ini berisi memori sewenang-wenang untuk alasan kinerja, yang dapat mencakup apa saja mulai dari kode sumber program untuk kata sandi dan kunci enkripsi.new Buffer('abc')membuatBufferyang berisi versi UTF-8-encoded string ''abc'. Argumen kedua dapat menentukan pengkodean lain: misalnya,new Buffer(string, 'base64')dapat digunakan untuk mengonversi string Base64 menjadi yang asli urutan byte yang diwakilinya.- Ada beberapa kombinasi argumen lainnya.
Ini berarti bahwa dalam kode seperti var buffer = new Buffer(foo);, tidak mungkin untuk mengetahuinya apa sebenarnya isi buffer yang dihasilkan tanpa mengetahui jenis foo.
Terkadang, nilai foo berasal dari sumber eksternal. Misalnya, fungsi ini dapat diekspos sebagai layanan di server web, mengubah string UTF-8 menjadi bentuk Base64:
function stringToBase64(req, res) {
// The request body should have the format of `{ string: 'foobar' }`.
const rawBytes = new Buffer(req.body.string);
const encoded = rawBytes.toString('base64');
res.end({ encoded });
}Perhatikan bahwa kode ini tidak memvalidasi jenis req.body.string:
req.body.stringdiharapkan berupa string. Jika ini masalahnya, semuanya berjalan dengan baik.req.body.stringdikendalikan oleh klien yang mengirimkan permintaan.- Jika
req.body.stringadalah number50,rawBytesakan menjadi50byte:- Sebelum Node.js 8, konten tidak akan diinisialisasi
- Setelah Node.js 8, konten akan menjadi
50byte dengan nilai0
Karena pemeriksaan tipe yang hilang, penyerang dapat dengan sengaja mengirim nomor sebagai bagian dari permintaan. Dengan menggunakan ini, mereka dapat:
- Baca memori yang tidak diinisialisasi. Ini akan membocorkan kata sandi, kunci enkripsi, dan lainnya jenis informasi sensitif. (Kebocoran informasi)
- Memaksa program untuk mengalokasikan sejumlah besar memori. Misalnya, saat menentukan
500000000sebagai nilai input, setiap permintaan akan mengalokasikan 500MB memori. Ini dapat digunakan untuk menghabiskan memori yang tersedia dari suatu program sepenuhnya dan membuatnya crash, atau memperlambatnya secara signifikan. (Kegagalan layanan)
Kedua skenario ini dianggap sebagai masalah keamanan yang serius di dunia nyata konteks server web.
Saat menggunakan Buffer.from(req.body.string) sebagai gantinya, melewatkan nomor akan selalu melempar pengecualian sebagai gantinya, memberikan perilaku terkontrol yang selalu bisa ditangani oleh program.
Konstruktor Buffer() telah ditinggalkan untuk sementara waktu. Apakah ini benar-benar masalah?
Survei kode di ekosistem npm telah menunjukkan bahwa konstruktor Buffer() masih banyak digunakan. Ini termasuk kode baru, dan penggunaan keseluruhan kode tersebut sebenarnya telah increasing.