移植到Buffer.from()
/Buffer.alloc()
API.node
本指南介紹瞭如何遷移到安全的Buffer
構造函數方法,遷移修復瞭如下棄用警告:git
因爲安全性和可用性問題,不建議使用Buffer()
和new Buffer()
構造函數,請改用new Buffer.alloc()
、Buffer.allocUnsafe()
或Buffer.from()
構造方法。
只需運行grep -nrE '[^a-zA-Z](Slow)?Buffer\s*\(' --exclude-dir node_modules
。github
它會在你本身的代碼中找到全部可能不安全的地方(有一些不太常見的例外)。npm
若是你使用的是Node.js ≥ 8.0.0(推薦使用),Node.js會公開多個選項,以幫助你找到相關的代碼片斷:segmentfault
--trace-warnings
將使Node.js顯示此警告的堆棧跟蹤以及Node.js打印的其餘警告。--trace-deprecation
執行相同的操做,但僅適用於棄用警告。--pending-deprecation
將顯示更多類型的棄用警告,特別是,它也會顯示Buffer()
棄用警告,即便在Node.js 8上。你可使用環境變量設置這些標誌:api
$ 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 ...]
ESLint規則no-buffer-constructor或node/no-deprecated-api也查找對不推薦使用的Buffer()
API的調用,這些規則包含在一些預設中。數組
可是有一個缺點,當Buffer
被重寫時,它並不老是正確工做,例如使用polyfill,所以建議將此與上述其餘方法結合使用。安全
這是目前推薦的解決方案,僅意味着最小的開銷。服務器
自2016年7月以來,Node.js 5.x版本系列一直未獲得支持,而且Node.js 4.x版本系列在2018年4月達到其生命週期結束(→計劃)。函數
這意味着即便出現安全問題,這些版本的Node.js也不會收到任何更新,所以若是可能的話,應該避免使用這些版本線。
在這種狀況下,你要作的是將全部new Buffer()
或Buffer()
調用轉換爲使用Buffer.alloc()
或Buffer.from()
,方法以下:
對於new Buffer(number)
,將其替換爲Buffer.alloc(number)
。
new Buffer(string)
(或new Buffer(string, encoding)
),將其替換爲Buffer.from(string)
(或Buffer.from(string, encoding)
)。Buffer.from(...arguments)
替換new Buffer(...arguments)
。請注意,Buffer.alloc()
在當前Node.js版本上的速度也比new Buffer(size).fill(0)
快,這是你須要確保零填充的緣由。
建議啓用ESLint規則no-buffer-constructor或node/no-deprecated-api以免意外的不安全Buffer
API使用。
還有一個JSCodeshift codemod,用於自動將Buffer
構造函數遷移到Buffer.alloc()
或Buffer.from()
,請注意,它目前僅適用於參數爲文字或使用兩個參數調用構造函數的狀況。
若是你當前支持那些較舊的Node.js版本而且沒法刪除對它們的支持,或者若是你支持包的舊分支,考慮在較舊的分支上使用變式2或變式3,所以使用這些舊分支的人也將收到修復。這樣,你將消除由不謹慎的Buffer
API使用引發的潛在問題,而且在Node.js 10上運行代碼時,你的用戶將不會觀察到運行時棄用警告。
有三種不一樣的polyfill可用:
Buffer
API的替代品,在使用new Buffer()
時會拋出。你將採用與變式1徹底相同的步驟,可是在使用新的Buffer
API的全部文件中都使用polyfill const Buffer = require('safer-buffer').Buffer
。
不要使用舊的new Buffer()
API,在添加上面一行的任何文件中,使用舊的new Buffer()
API將拋出。
Buffer
API各自部分的ponyfill,你只需添加與你正在使用的API相對應的包。你可使用適當的名稱導入所需的模塊,例如const bufferFrom = require('buffer-from')
而後使用它而不是調用new Buffer()
,例如:new Buffer('test')
變爲bufferFrom('test')
。
使用這種方法的一個缺點是從它們遷移出來的代碼更改略多(由於你將使用不一樣名稱下的Buffer.from()
)。
Buffer
API的替代品,但使用new Buffer()
仍然能夠像之前同樣工做。這種方法的缺點是它容許你在代碼中使用較舊的new Buffer()
API,這是有問題的,由於它可能會致使代碼中出現問題,並將從Node.js 10開始發出運行時棄用警告(在此處閱讀更多內容)。
請注意,在任何一種狀況下,你還必須手動刪除對舊Buffer
API的全部調用,只是投入safe-buffer
自己並不能解決問題,它只是爲新API提供了一個polyfill,我看到有人犯了這個錯誤。
建議啓用ESLint規則no-buffer-constructor或node/no-deprecated-api。
放棄對Node.js <4.5.0的支持後,不要忘記刪除polyfill使用。
若是你只在幾個地方(例如一個)建立Buffer
實例,或者你有本身的包裝器,這將很是有用。
Buffer(0)
這個用於建立空緩衝區的特殊狀況能夠安全地替換爲Buffer.concat([])
,它返回相同的結果一直到Node.js 0.8.x。
Buffer(notNumber)
以前:
const buf = new Buffer(notNumber, encoding);
之後:
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
是可選的。
請注意,typeof notNumber
必須在new Buffer()
以前,(對於notNumber
參數未進行硬編碼的狀況)而且不是由Buffer
構造函數的棄用引發的 - 這正是不推薦使用Buffer
構造函數的緣由。缺少此類型檢查的生態系統包致使了許多安全問題 - 當未通過處理的用戶輸入可能最終出如今Buffer(arg)
中時,會出現從DoS到從進程內存向攻擊者泄漏敏感信息等問題。
當notNumber
參數被硬編碼時(例如文字"abc"
或[0,1,2]
),能夠省略typeof
檢查。
另請注意,使用TypeScript
不能解決此問題 - 當從JS中使用用TypeScript
編寫的庫時,或者當用戶輸入結束時 - 它的行爲與純JS同樣,由於全部類型檢查只是轉換時間,而且不存在於TS編譯的實際JS代碼中。
Buffer(number)
對於Node.js 0.10.x(及如下)支持:
var buf; if (Buffer.alloc) { buf = Buffer.alloc(number); } else { buf = new Buffer(number); buf.fill(0); }
不然(Node.js ≥ 0.12.x):
const buf = Buffer.alloc ? Buffer.alloc(number) : new Buffer(number).fill(0);
Buffer.allocUnsafe()
使用Buffer.allocUnsafe()
時要格外當心:
若是你沒有充分的理由,請不要使用它。
Buffer.alloc()
可能會更快。若是使用它,請確保永遠不會以部分填充狀態返回buffer。
處理使用Buffer.allocUnsafe()
分配的緩衝區中的錯誤可能會致使各類問題,包括代碼的未定義行爲,以及泄露給遠程攻擊者的敏感數據(用戶輸入、密碼、證書)。
請注意,這一樣適用於沒有零填充的new Buffer()
用法,具體取決於Node.js版本(缺乏類型檢查也會將DoS添加到潛在問題列表中)。
Buffer
構造函數有什麼問題?
Buffer
構造函數可用於以多種不一樣方式建立緩衝區:
new Buffer(42)
建立一個42字節的Buffer
,在Node.js 8以前,因爲性能緣由,此緩衝區包含任意內存,其中可能包括從程序源代碼到密碼和加密密鑰的任何內容。new Buffer('abc')
建立一個Buffer
,其中包含字符串'abc'
的UTF-8編碼版本,第二個參數能夠指定另外一個編碼:例如,可使用new Buffer(string, 'base64')
將Base64字符串轉換爲它所表明的原始字節序列。這意味着在像var buffer = new Buffer(foo);
這樣的代碼中,在不知道foo
類型的狀況下,沒法肯定生成的緩衝區的確切內容。
有時,foo
的值來自外部來源,例如,此函數能夠做爲Web服務器上的服務公開,將UTF-8字符串轉換爲其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 }); }
請注意,此代碼不驗證req.body.string
的類型:
req.body.string
應該是一個字符串,若是是這種狀況,一切順利。req.body.string
由發送請求的客戶端控制。若是req.body.string
是數字50
,則rawBytes
將是50
個字節:
50
個字節,值爲0
。因爲缺乏類型檢查,攻擊者能夠故意發送一個號碼做爲請求的一部分,使用它,他們能夠:
500000000
做爲輸入值時,每一個請求將分配500MB的內存,這能夠用來徹底耗盡程序可用的內存並使其崩潰,或者顯着減慢程序速度(拒絕服務)。在現實世界的Web服務器環境中,這兩種狀況都被認爲是嚴重的安全問題。
當使用Buffer.from(req.body.string)
時,傳遞一個數字將老是拋出一個異常,提供可由程序始終處理的受控行爲。
Buffer()
構造函數已被棄用了一段時間,這真的是一個問題嗎?npm
生態系統中的代碼調查代表,Buffer()
構造函數仍然被普遍使用,這包括新代碼,而且此類代碼的整體使用實際上已經增長。