本文へスキップ
ecdsa.com
Node.js tls / https / fetchNode.js の crypto

エラーメッセージ

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 は決してダウンロードしません。

発生する理由

修正方法

  1. 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. 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. 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. 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

関連するエラー

署名エラーデータベースをすべて見る