現象
Node.jsで立てたWebサーバが不思議な挙動をとるようになった
発生する時間やエラーを再現する人はバラバラで一貫性がない
その場しのぎの対応としてブラウザの再起動やCookieの削除で解決することが多かったが、原因の詳細はわからずもやもやしていた
原因
Node.jsは、2018/11にDoS攻撃の脆弱性(https://nvd.nist.gov/vuln/detail/CVE-2018-12121)対応として、デフォルトの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
おわりに
今回リクエストヘッダのサイズが8kBを越えた主な原因は、かなりの数のCookieを使ってWebサーバにアクセスしていたことでした。 仕様上Cookieの数が多くなりHTTPリクエストのサイズに不安がある場合は、エラーハンドリングを正しく実装して、起動オプションでNode.jsが受け取るHTTPリクエストサイズの最大値を上げておくと良いかと思います。