コンテンツへスキップ
Integration

REST APIを使用してSSL証明書の有効期限を監視する

| 6 min read

2 つの API エンドポイントを持つドメインの SSL 証明書の有効期限、発行者、セキュリティ ヘッダーを確認します。 GitHub Actions、Node.js、および Slack アラートの例が含まれています。

Green padlock icon on a browser address bar
Photo by Towfiqu barbhuiya on Unsplash

SSL 証明書の期限が切れるとサイトがオフラインになり、ブラウザに恐ろしい警告が表示されます 遠方のお客様。 Let's Encrypt 証明書は自動更新されますが、DNS の設定が正しくなく、cron ジョブが失敗しました。 手動証明書を忘れると、依然として機能停止が発生します。 有効期限を監視する方法が必要です すべてのドメインの日付を確認します。

Botoi API は、このために 2 つのエンドポイントを提供します。 証明書の詳細 (発行者、 有効期限、有効期限までの日数)。 もう 1 つは HTTPS サポートをチェックし、セキュリティをスキャンします ヘッダー。 これらは一緒に、有効期限の監視とセキュリティ体制の監査の両方をカバーします。

/v1/ssl-cert/certificate で証明書の詳細を取得します

このエンドポイントはドメインに接続し、TLS 証明書を読み取り、構造化された証明書を返します。 データはどの言語でも解析できます。

curl -X POST https://api.botoi.com/v1/ssl-cert/certificate \\
  -H "Content-Type: application/json" \\
  -d '{"domain": "stripe.com"}'

応答:

{
  "success": true,
  "data": {
    "domain": "stripe.com",
    "subject": "CN=stripe.com",
    "issuer": "C=US, O=Let's Encrypt, CN=E6",
    "valid_from": "2026-02-18T00:00:00.000Z",
    "valid_to": "2026-05-19T00:00:00.000Z",
    "days_until_expiry": 51,
    "serial": "04:A3:9B:7C:2D:1E:8F:00:5A:B2:C4:D6:E8:F0:12:34",
    "fingerprint": "A1:B2:C3:D4:E5:F6:78:90:AB:CD:EF:01:23:45:67:89",
    "san": ["stripe.com", "*.stripe.com"]
  }
}

days_until_expiry フィールドは、アラートを作成するフィールドです。 の san 配列は証明書がカバーするすべてのドメインを示し、検証に役立ちます ワイルドカード証明書には、期待するサブドメインが含まれていることを確認します。

/v1/ssl で HTTPS サポートとセキュリティ ヘッダーを確認します

証明書が有効であることがわかっているだけでは十分ではありません。 安全性も確認したい HSTS や CSP などのヘッダーが配置されています。 の /v1/ssl エンドポイントがそれを処理します。

curl -X POST https://api.botoi.com/v1/ssl \\
  -H "Content-Type: application/json" \\
  -d '{"domain": "stripe.com"}'

応答:

{
  "success": true,
  "data": {
    "domain": "stripe.com",
    "ssl_supported": true,
    "protocol": "TLSv1.3",
    "headers": {
      "strict-transport-security": "max-age=63072000; includeSubDomains; preload",
      "content-security-policy": "default-src 'self'; script-src 'self' js.stripe.com",
      "x-frame-options": "SAMEORIGIN",
      "x-content-type-options": "nosniff",
      "referrer-policy": "strict-origin-when-cross-origin"
    }
  }
}

ssl_supported ブール値は HTTPS が機能することを確認します。 の headers オブジェクト サーフェスは、HSTS、CSP、X-Frame-Options、X-Content-Type-Options、および Referrer-Policy です。 ヘッダーが欠落している場合は、セキュリティ構成にギャップがあることを示します。 1.2 未満の TLS プロトコル 赤信号です。

GitHub Actions: 自動作成された問題による毎週の SSL チェック

このワークフローは毎週月曜日に実行され、ドメインのリストをチェックし、次の場合には GitHub の問題をオープンします。 どの証明書も 30 日以内に期限切れになります。 作成する .github/workflows/ssl-check.yml:

name: SSL Expiry Check

on:
  schedule:
    # Every Monday at 9:00 UTC
    - cron: '0 9 * * 1'
  workflow_dispatch:

jobs:
  check-ssl:
    runs-on: ubuntu-latest
    steps:
      - name: Check SSL certificates
        run: |
          DOMAINS=("stripe.com" "api.stripe.com" "dashboard.stripe.com")
          THRESHOLD=30
          FAILURES=""

          for DOMAIN in "\\\${DOMAINS[@]}"; do
            RESPONSE=\$(curl -s -X POST https://api.botoi.com/v1/ssl-cert/certificate \\
              -H "Content-Type: application/json" \\
              -d "{\\"domain\\": \\"\$DOMAIN\\"}")

            DAYS=\$(echo "\$RESPONSE" | jq -r '.data.days_until_expiry')
            ISSUER=\$(echo "\$RESPONSE" | jq -r '.data.issuer')
            EXPIRY=\$(echo "\$RESPONSE" | jq -r '.data.valid_to')

            echo "## \$DOMAIN" >> \$GITHUB_STEP_SUMMARY
            echo "- Expires: \$EXPIRY" >> \$GITHUB_STEP_SUMMARY
            echo "- Days left: \$DAYS" >> \$GITHUB_STEP_SUMMARY
            echo "- Issuer: \$ISSUER" >> \$GITHUB_STEP_SUMMARY
            echo "" >> \$GITHUB_STEP_SUMMARY

            if [ "\$DAYS" -lt "\$THRESHOLD" ]; then
              FAILURES="\$FAILURES\\n- \$DOMAIN expires in \$DAYS days (\$EXPIRY)"
            fi
          done

          if [ -n "\$FAILURES" ]; then
            echo "::error::Certificates expiring within \$THRESHOLD days:\$FAILURES"
            exit 1
          fi

          echo "All certificates have more than \$THRESHOLD days remaining."

      - name: Open GitHub issue on failure
        if: failure()
        uses: actions/github-script@v7
        with:
          script: |
            await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: 'SSL certificate expiring soon',
              body: 'The weekly SSL check found certificates expiring within 30 days. See the [workflow run](' + context.serverUrl + '/' + context.repo.owner + '/' + context.repo.repo + '/actions/runs/' + context.runId + ') for details.',
              labels: ['infrastructure', 'urgent']
            });

ワークフローは各ドメインをループし、API をクエリし、失敗を蓄積します。 もし 証明書が 30 日のしきい値を下回ると、ジョブは失敗し、GitHub が作成されます。 タグ付けされた問題 infrastructure そして urgent。 ジョブサマリーには次のことが表示されます 各ドメインの完全なレポート。

調整する DOMAINS そして THRESHOLD セットアップに合わせて。 無料 この層は 1 日あたり最大 100 件のリクエストを処理し、毎週チェックされる最大 14 個のドメインをカバーします。

Node.js: スクリプトで複数のドメインを監視する

独自の監視スタックに統合するために、次の Node.js スクリプトをチェックします。 ドメインの配列を並行して処理し、有効期限が近づいている証明書にフラグを立てます。

const DOMAINS = [
  "stripe.com",
  "api.stripe.com",
  "dashboard.stripe.com",
  "docs.stripe.com",
];

const THRESHOLD_DAYS = 30;

async function checkCert(domain) {
  const res = await fetch("https://api.botoi.com/v1/ssl-cert/certificate", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": process.env.BOTOI_API_KEY,
    },
    body: JSON.stringify({ domain }),
  });
  const { data } = await res.json();
  return { domain, ...data };
}

async function checkAll() {
  const results = await Promise.all(DOMAINS.map(checkCert));
  const expiring = results.filter(
    (r) => r.days_until_expiry < THRESHOLD_DAYS
  );

  console.log("SSL Certificate Report");
  console.log("=".repeat(50));

  for (const r of results) {
    const status =
      r.days_until_expiry < THRESHOLD_DAYS ? "WARNING" : "OK";
    console.log(
      \`[\\\${status}] \\\${r.domain} - \\\${r.days_until_expiry} days left (expires \\\${r.valid_to})\`
    );
  }

  if (expiring.length > 0) {
    console.log(
      \`\\n\\\${expiring.length} certificate(s) expiring within \\\${THRESHOLD_DAYS} days.\`
    );
  }

  return { results, expiring };
}

checkAll();

これを cron スケジュールで実行するか、既存のヘルスチェック パイプラインに統合します。 の Promise.all この呼び出しはすべてのドメインを同時にチェックするため、合計 実行時間は単一の API 呼び出しのレイテンシに近いままです。

証明書の有効期限が間もなく切れる場合の Slack Webhook アラート

証明書チェックを Slack 受信 Webhook と組み合わせて、証明書チェックが行われたときにチームに通知します。 証明書には注意が必要です:

async function sendSlackAlert(domain, daysLeft, validTo) {
  const webhookUrl = process.env.SLACK_WEBHOOK_URL;

  await fetch(webhookUrl, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      text: \`:rotating_light: SSL certificate for \\\${domain} expires in \\\${daysLeft} days\`,
      blocks: [
        {
          type: "section",
          text: {
            type: "mrkdwn",
            text: [
              "*SSL Certificate Expiry Warning*",
              \`Domain: \\\`\\\${domain}\\\`\`,
              \`Days remaining: *\\\${daysLeft}*\`,
              \`Expires: \\\${validTo}\`,
            ].join("\\n"),
          },
        },
      ],
    }),
  });
}

// After running the certificate check
async function checkAndAlert() {
  const DOMAINS = ["stripe.com", "api.stripe.com"];

  for (const domain of DOMAINS) {
    const res = await fetch(
      "https://api.botoi.com/v1/ssl-cert/certificate",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Api-Key": process.env.BOTOI_API_KEY,
        },
        body: JSON.stringify({ domain }),
      }
    );
    const { data } = await res.json();

    if (data.days_until_expiry < 30) {
      await sendSlackAlert(domain, data.days_until_expiry, data.valid_to);
    }
  }
}

checkAndAlert();

メッセージには、ドメイン、残り日数、有効期限が含まれます。 あなたのチームが見ているのは、 Slack でアラートを生成し、証明書の有効期限が切れる前に調査できます。

完全なセキュリティ監査: 両方のエンドポイントを組み合わせる

全体像を把握するには、ドメインごとに両方のエンドポイントを並行して実行します。 これにより、 証明書の有効期限データとセキュリティ ヘッダーを 1 回のパスでカバーします。

async function auditSsl(domain) {
  const [cert, ssl] = await Promise.all([
    fetch("https://api.botoi.com/v1/ssl-cert/certificate", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Api-Key": process.env.BOTOI_API_KEY,
      },
      body: JSON.stringify({ domain }),
    }).then((r) => r.json()),

    fetch("https://api.botoi.com/v1/ssl", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Api-Key": process.env.BOTOI_API_KEY,
      },
      body: JSON.stringify({ domain }),
    }).then((r) => r.json()),
  ]);

  const issues = [];

  // Certificate checks
  if (cert.data.days_until_expiry < 30) {
    issues.push(\`Certificate expires in \\\${cert.data.days_until_expiry} days\`);
  }

  // Security header checks
  const headers = ssl.data.headers || {};
  if (!headers["strict-transport-security"]) {
    issues.push("Missing HSTS header");
  }
  if (!headers["content-security-policy"]) {
    issues.push("Missing Content-Security-Policy header");
  }
  if (!headers["x-frame-options"]) {
    issues.push("Missing X-Frame-Options header");
  }

  return { domain, cert: cert.data, ssl: ssl.data, issues };
}

// Run audit across all domains
const domains = ["stripe.com", "api.stripe.com"];
Promise.all(domains.map(auditSsl)).then((results) => {
  for (const r of results) {
    console.log(\`\\n\\\${r.domain}:\`);
    console.log(\`  Certificate: \\\${r.cert.days_until_expiry} days left\`);
    console.log(\`  TLS: \\\${r.ssl.protocol}\`);
    console.log(\`  Issues: \\\${r.issues.length === 0 ? "None" : r.issues.join(", ")}\`);
  }
});

このスクリプトは、証明書の有効期限、TLS プロトコルのバージョン、および不足しているセキュリティを報告します。 各ドメインのヘッダー。 毎週のセキュリティレビューに追加するか、 インシデント対応ダッシュボード。

重要なポイント

  • /v1/ssl-cert/certificate 発行者、有効期限を返します。 days_until_expiry、シリアル番号、指紋、および SAN リスト ドメインの TLS 証明書。
  • /v1/ssl HTTPS サポートをチェックし、TLS プロトコルのバージョンを報告します。 HSTS、CSP、X-Frame-Options、X-Content-Type-Options、および Referrer-Policy をスキャンします。 ヘッダー。
  • どちらのエンドポイントも匿名で 1 分あたり 5 リクエストで動作します。 渡す X-Api-Key より高い制限のヘッダー。
  • 30 日のしきい値を設定した毎週の GitHub Actions cron ジョブが更新の失敗をキャッチします 停電になる前に。
  • 両方のエンドポイントを組み合わせて、証明書の有効期限の監視とセキュリティ ヘッダーを実行します。 単一のスクリプトで監査を実行します。

FAQ

API 経由で SSL 証明書の有効期限を確認するにはどうすればよいですか?
ドメインを含む JSON 本文を使用して、POST リクエストを https://api.botoi.com/v1/ssl-cert/certificate に送信します。 応答には、証明書の valid_from、valid_to、および days_until_expiry フィールドが含まれます。 openssl CLI や手動のブラウザ検査は必要ありません。
/v1/ssl-cert/certificate と /v1/ssl の違いは何ですか?
/v1/ssl-cert/certificate エンドポイントは、証明書の詳細 (発行者、件名、シリアル番号、有効期限、有効期限までの日数) を返します。 /v1/ssl エンドポイントは、HTTPS がサポートされているかどうかを確認し、HSTS、CSP、X-Frame-Options、Referrer-Policy などのセキュリティ ヘッダーをスキャンします。 1 つ目は有効期限の監視に使用し、2 つ目はセキュリティ監査に使用します。
API キーなしで SSL 証明書を確認できますか?
はい。 匿名アクセスでは、IP ベースのレート制限により、1 分あたり 5 リクエスト、1 日あたり 100 リクエストが許可されます。 サインアップや API キーは必要ありません。 スループットを高めるには、有料プランが月額 9 ドルから始まります。
SSL 証明書の有効期限はどのくらいの頻度で確認する必要がありますか?
ほとんどのチームにとって、毎週は適切な基準です。 Let's Encrypt 証明書は 90 日ごとに更新され、有効期限が切れる 30 日前に自動更新されます。 30 日間の警告しきい値を設定した毎週のチェックにより、証明書の有効期限が切れる前に更新の失敗を修正するのに十分な時間が得られます。
これは自己署名証明書または内部証明書で機能しますか?
API はパブリック インターネット経由でドメインに接続するため、ポート 443 で提供される任意の証明書で動作します。自己署名証明書は証明書の詳細を返しますが、発行者フィールドには信頼された CA ではなく自己署名エンティティが表示されます。

botoiで開発を始めよう

150以上のAPIエンドポイント。検索、テキスト処理、画像生成、開発者ユーティリティに対応。無料プラン、クレジットカード不要。