关于Node.js的HTTP请求头的最大大小问题耽搁了我一段时间
目前出现的情况。
-
- Node.js(v12.3.1)で立てたWebサーバにアクセスすると、時折HTTPリクエストに失敗する
- Cookieを削除したり、ブラウザを再起動すると治ることもあるが、根本的な原因がわからない
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World');
});
server.listen(8080);
造成这种情况的理由是什么?
-
- Node.jsの最大HTTPリクエストヘッダサイズのデフォルト値である8kBを越えるHTTPリクエストヘッダサイズを送信していたことが原因だった
-
- Node.jsは、2018/11にDoS攻撃の脆弱性対応として、デフォルトのHTTPリクエストヘッダの最大サイズを変更前の80kBから8kB(8192Bytes)に変更する修正が加えられた
- デフォルトでは、HTTPリクエストヘッダのサイズが8kBを越えるとソケットが強制破棄されて「431 Request Header Fields Too Large」を返す
$ npm start
> sample-nodejs-header-overflow@1.0.0 start /../../../sample-nodejs-header-overflow
> node index.js
// curlで8kB以上のHTTPリクエストを送信
ErrorCode: HPE_HEADER_OVERFLOW
BytesParsed: 8559
// curlで8kB以上のHTTPリクエストを送信
ErrorCode: HPE_HEADER_OVERFLOW
BytesParsed: 8559
// curlで8kB以上のHTTPリクエストを送信
ErrorCode: HPE_HEADER_OVERFLOW
BytesParsed: 9085
对策
- HTTPリクエストヘッダのサイズが超えた場合に起こるclientErrorイベントを補足して、ソケットが強制的に破棄されないようにエラーハンドリングを行う
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World');
});
server.on('clientError', (err, socket) => {
console.log('ErrorCode: ', err.code);
console.log('BytesParsed: ', err.bytesParsed);
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.listen(8080);
- アプリケーション起動時に「–max-http-header-size」という起動オプションを設定して、Node.jsが受け取る最大のHTTPリクエストヘッダサイズを増やす
$ node --max-http-header-size=16384 index.js
最后
多数的Cookie使得本次请求头的大小超过了8kB,这是主要原因。如果在规范上Cookie的数量很大,并且对HTTP请求大小存在担忧的情况下,建议正确实施错误处理,并在启动选项中将Node.js接收的HTTP请求大小的最大值提高。
请看
-
- https://github.com/nodejs/node/commit/a8532d4d23
-
- https://www.nearform.com/blog/protecting-node-js-from-uncontrolled-resource-consumption-headers-attacks/
-
- https://github.com/nodejs/node/issues/25528
- https://nodejs.org/docs/v12.3.1/api/http.html#http_event_clienterror