エラーメッセージ
unable to verify the first certificate
別の表記
- code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'
- UNABLE_TO_VERIFY_LEAF_SIGNATURE
最近の Node.js のリリース(ecdsa.com のテストでは 24.22)は、このメッセージの末尾に「; if the root CA is installed locally, try running Node.js with --use-system-ca」を追加します。fetch() では「TypeError: fetch failed」の原因として現れます。axios と got は、同じコードとともにこれをエラーメッセージとして表示します。
Node.js tls / https / fetch — code UNABLE_TO_VERIFY_LEAF_SIGNATURE (OpenSSL X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, 21)
このエラーの意味
Node.js はサーバー自身の(リーフ)証明書だけを受け取り、その発行者をどこにも見つけられませんでした。ハンドシェイクの中にも、信頼するルートの中にもありません。発行者がなければリーフ証明書の署名を確認することすらできないため、メッセージは「the first certificate」となっています。ほぼ必ず、サーバーが中間証明書を送信し忘れています。ブラウザーは中間証明書をダウンロードすることでこの問題を覆い隠しますが、Node.js は決してダウンロードしません。
発生する理由
サーバーが中間証明書を送信していない
よくあるリーフ証明書だけを含む証明書ファイル(fullchain.pem ではなく cert.pem、またはチェーンなしでアップロードしたロードバランサーの証明書)が原因です。再現環境が示すとおり、正しいルートを信頼していても、Node.js はリーフとルートの間をつなげません。
プライベート CA がリーフ証明書を発行しており、チェーン内に既知のものが何もない
よくある社内 CA や自作の CA から発行された証明書を単独で返す社内サービスでも、同じコードになります。Node.js は発行者も、その上位にあるものも知りません。
中間証明書はファイルにあるが使われていない
ときどき一部のサーバーは、ファイルの最初の証明書しか送信しないか、チェーンを別の設定で指定することを前提としています(古い Apache の SSLCertificateChainFile、クラウドのロードバランサーにある別のチェーン用フィールドなど)。中間証明書はディスク上に存在しますが、クライアントには届きません。
修正方法
- 1.
サーバーが送信する証明書の数を確認する
depth 0 しか表示されず、その発行者が中間 CA であれば、修正すべきなのはサーバー側です。
bash openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \ | grep -E '^ *[0-9]+ s:|^ *i:' # Only " 0 s:" present, with an i: that is an intermediate -> the server must send that intermediate - 2.
完全なチェーンを返す
リーフ証明書に続けてすべての中間証明書を含むバンドルをサーバーに設定してください。Node.js のサーバーでは cert オプション、nginx では ssl_certificate、クラウドのロードバランサーでは証明書チェーンのフィールドがそれにあたります。
js // Node.js HTTPS server: pass leaf + intermediates as the cert import https from "node:https"; import fs from "node:fs"; https.createServer({ key: fs.readFileSync("server.key"), cert: fs.readFileSync("fullchain.pem"), // leaf first, then intermediate(s) }, handler).listen(443); - 3.
サーバーを変更できない場合:クライアント側で中間証明書を追加する
NODE_EXTRA_CA_CERTS は中間証明書も受け付け、プロセスの起動時に一度だけ読み込まれます。ただし、サーバーを修正するほうが望ましい方法です。ほかのすべてのクライアント(モバイルアプリ、パートナー企業)も同じ問題を抱えているからです。
bash # When you cannot fix the server: trust the missing intermediate on the client too NODE_EXTRA_CA_CERTS=/etc/ssl/certs/issuing-ca.pem node app.js - 4.
一時的な診断:1 回の実行に限って検証を無効にする
NODE_TLS_REJECT_UNAUTHORIZED=0 は、プロセス内のすべての TLS 接続で証明書のチェックを無効にし、警告を出力します。診断の確認にだけ使ってください。本番環境で使うと、経路上の誰もが通信を読み取り、改ざんできるようになります。
bash # Temporary diagnostic only: disables certificate verification for the whole process NODE_TLS_REJECT_UNAUTHORIZED=0 node app.js
関連するエラー
unable to get local issuer certificateNode.js tls / https / fetchself-signed certificate in certificate chainNode.js tls / https / fetchnpm error code UNABLE_TO_VERIFY_LEAF_SIGNATUREnpm (Node.js)verify error:num=21:unable to verify the first certificateOpenSSL CLI (openssl s_client)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)