Node.js HTTPS リクエスト時の self-signed certificate in certificate chain エラーに対応する (https、fetch、Axios 対応)
Node.js でビルトインの https
モジュール、fetch
、サードパーティーでよく使われている axios
パッケージで https インターネットアクセスする際に TLS インスペクションが入っている環境で発生する self-signed certificate in certificate chain
エラーに対応する方法を紹介します。
やりたいこと
企業の環境等で TLS インスペクションが入っている環境において Node.js のビルトインの https
モジュール等で https インターネットアクセスする際に self-signed certificate in certificate chain
というエラーが発生することがあります。このエラーの対応方法をビルトインの https
モジュール、fetch
、サードパーティー「でよく使われている axios
パッケージそれぞれで紹介します。
環境
- OS
- Microsoft Windows 10 22H2
- Node.js
- v22.6.0
- axios
- 1.7.7
事象
例えば TLS インスペクションがある環境で以下のようなコードで Yes か No を返してくれる YESNO API を叩いてみます。
- Node.js のビルトインの
https
モジュール - Node.js ビルトインの
fetch
axios
パッケージ
すると何れのパターンでも以下のようなエラーが発生します。
このエラーは TLS インスペクションが入っていることにより証明書検証でエラーとなっていることがほとんどです。 TLS インスペクションの簡単な説明と、後ほど使用する証明書の特定、取得方法を こちらの記事 で紹介していますので必要であればご参照ください。
対応方法
証明書検証をオフにする (非推奨)
Node.js では環境変数 NODE_TLS_REJECT_UNAUTHORIZED
に '0'
を設定することで証明書検証をオフにすることができます。
ただしあらゆる HTTPS における証明書検証がなされなくなるので、この方法はおすすめしません。
NODE_TLS_REJECT_UNAUTHORIZED=value
If value equals'0'
, certificate validation is disabled for TLS connections. This makes TLS, and HTTPS by extension, insecure. The use of this environment variable is strongly discouraged.
Command-line API | Node.js v22.8.0 Documentation
一応設定方法について書いておきます。
- PowerShell の場合
- コマンドプロンプトの場合
で設定できます。
先ほどのコードを実行してみると、https
、fetch
、axios
何れのパターンでも以下のワーニングが表示されますが、
API のレスポンスは受け取れていることがわかります。
上述した通りセキュリティ的によろしくないので、試した場合は NODE_TLS_REJECT_UNAUTHORIZED
環境変数を削除しておきましょう。
- PowerShell の場合
- コマンドプロンプトの場合
で削除できます。
環境変数で自己署名証明書を指定する
本質的にはプロキシの自己署名証明書を追加することがあるべき姿です。
環境変数 NODE_EXTRA_CA_CERTS
で追加のルート証明書を指定することができます。
繰り返しになりますが、追加する証明書の特定、取得方法を こちらの記事 で紹介していますので必要であればご参照ください。
NODE_EXTRA_CA_CERTS=file
When set, the well known “root” CAs (like VeriSign) will be extended with the extra certificates infile
. The file should consist of one or more trusted certificates in PEM format. A message will be emitted (once) withprocess.emitWarning()
if the file is missing or malformed, but any errors are otherwise ignored.
(中略) TheNODE_EXTRA_CA_CERTS
environment variable is only read when the Node.js process is first launched. Changing the value at runtime usingprocess.env.NODE_EXTRA_CA_CERTS
has no effect on the current process.
Command-line API | Node.js v22.7.0 Documentation
Node.js を実行する場所からの相対パスで指定可能ですが、絶対パスの方がよいでしょう。
- PowerShell の場合
- コマンドプロンプトの場合
で設定できます。
Node.js https.Agent
を使用する (https
モジュール、Axios の場合)
ここまで紹介した証明書検証のオフやルート証明書の追加は https.Agent
を使用することで、コード内でこれらを指定することができます。ただしこれができるのは Node.js の https
モジュールや Axios を使用する場合で、fetch
では使用できません。
fetch
でコード内で指定する方法は後述します。
まず https.Agent
を見ていきます。Node.js 公式ドキュメントの https.Agent
の項では触れられていませんが、以下の例から https.Agent
のコンストラクターオプションには tls.connect()
のオプションも使用できることが伺えます。
Example using options from tls.connect():
余談ですが、TypeScript の Node.js のビルトイン型セットである @types/node
の AgentOptions
を見てみると、確かに tls.ConnectOptions
が継承されています。
さて、 tls.connect()
のオプションを見てみると、証明書検証有無を指定できる rejectUnauthorized
プロパティーが見つかります。
rejectUnauthorized
If notfalse
, the server certificate is verified against the list of supplied CAs. An'error'
event is emitted if verification fails;err.code
contains the OpenSSL error code. Default: true.
TLS (SSL) | Node.js v22.8.0 Documentation
さらに tls.createSecureContext()
のオプションも含められる記載もあります。
tls.createSecureContext()
options that are used if thesecureContext
option is missing, otherwise they are ignored.
TLS (SSL) | Node.js v22.8.0 Documentation
再度余談ですが、@types/node
でもそのように定義されています。
というわけで tls.createSecureContext()
のオプションを見てみると ca
プロパティーで証明書を指定できることがわかります。
ca
| <string[]> | | <Buffer[]>
Optionally override the trusted CA certificates. Default is to trust the well-known CAs curated by Mozilla. Mozilla’s CAs are completely replaced when CAs are explicitly specified using this option. The value can be a string orBuffer
, or anArray
of strings and/orBuffers
. Any string orBuffer
can contain multiple PEM CAs concatenated together. The peer’s certificate must be chainable to a CA trusted by the server for the connection to be authenticated. When using certificates that are not chainable to a well-known CA, the certificate’s CA must be explicitly specified as a trusted or the connection will fail to authenticate. If the peer uses a certificate that doesn’t match or chain to one of the default CAs, use theca
option to provide a CA certificate that the peer’s certificate can match or chain to. For self-signed certificates, the certificate is its own CA, and must be provided. For PEM encoded certificates, supported types are “TRUSTED CERTIFICATE”, “X509 CERTIFICATE”, and “CERTIFICATE”. See also tls.rootCertificates.
TLS (SSL) | Node.js v22.8.0 Documentation
では https.Agent
を使用して実装してみます。プロキシの自己署名証明書は pem 形式であらかじめ用意してください。
繰り返しになりますが、追加する証明書の特定、取得方法を こちらの記事 で紹介していますので必要であればご参照ください。
なお、証明書を使用せず証明書検証をスキップする場合は、以下のコードで ca: cert
を rejectUnauthorized: false
としてください。ただしこの方法はおすすめしません。
Axios の場合はオプションの httpsAgent
プロパティーに https.Agent
を渡すことで設定できます。
Axios での実装例はこちら。
Undici の Agent
を使用する (fetch
の場合)
さて、fetch
の場合はどうするか、ですが公式ドキュメントでは以下の通り、仕様はブラウザの fetch
の互換実装である、としか書かれておらず、そのブラウザの fetch
の使用には証明書検証スキップや追加証明書の指定をするオプションはありません。(RequestInit - Web API | MDN)
fetch
A browser-compatible implementation of thefetch()
function.
Global objects | Node.js v22.8.0 Documentation
ここで Node.js の fetch
がどう実装されているのかをお話ししますが、実は Node.js の低レベル API を使用して JavaScript で一から作られた別モジュール Undici (Node.js Undici) を内包して実装されています。「低レベル API を使用して」とは Node.js の http
モジュールや https
モジュールは使用していないということです。
さて、この Undici のドキュメントを見てみると以下のような例があります。
You can pass an optional dispatcher to fetch as:
ここで fetch
のオプションに dispatcher
というプロパティーがあることに注目です。このプロパティーはブラウザの fetch
にはありません。
Undici のソースを見てみると WHATWG の RequestInit
の仕様に沿って実装していると思いきや、最後に undici specific option とコメントされ dispatcher
というプロパティーが追加されています。
undici/lib/web/fetch/request.js at main · nodejs/undici · GitHub
Request
オブジェクトも WHATWG の仕様に沿っていると思いきや、dispatcher
の Symbol である kDispatcher
プロパティーが追加されています。
node/deps/undici/src/lib/web/fetch/request.js at main · nodejs/node · GitHub
上記の Undici ドキュメントでは dispatcher
というプロパティーに Agent
オブジェクトを渡していたので、試しに以下の通り Node.js の https.Agent
を渡してみましたがダメでした。
以下エラーがでます。https.Agent
と undice.Agent
は別物のようです。
しょうがないので undici
パッケージを追加します。
https.Agent
を undici.Agent
に置き換えます。
これで無事にアクセスすることができました。ただ自己署名証明書の追加のために undici
パッケージを追加するのもいけてないのと本番環境では証明書追加は不要であることがほとんどではと思いますので、fetch
の場合は環境変数での対応が無難だと思います。
あとがき
TLS インスペクションが入った環境化における Node.js での HTTPS リクエスト時に発生する self-signed certificate in certificate chain
エラーの対応方法について纏めました。企業環境は何かと不便ですが、ちゃんと原因と対応方法がわかっているとサクッと対応できるので本記事がお役に立てると幸いです。