[PHP]cURLでのSSL証明書検証

ローカル開発環境でSSL証明書の検証エラーを回避するためにCURLOPT_SSL_VERIFYPEERを無効にしますが、そのまま本番環境へ反映するとは重大なセキュリティリスクがあります。

🔐 推奨される安全な解決策

1. CA証明書の正しい設定方法

// 安全なcURL設定例
$ch = curl_init();

// URL設定
curl_setopt($ch, CURLOPT_URL, "https://example.com");

// SSL検証を有効化(必須)
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // ホスト名の厳密な検証

// CA証明書のパスを指定
curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . '/cacert.pem');

// 実行
$response = curl_exec($ch);
curl_close($ch);

2. CA証明書バンドルの取得方法

最新のCA証明書を取得するには:

# cacert.pem をダウンロード
curl -sS https://curl.se/ca/cacert.pem -o cacert.pem
# または
wget https://curl.se/ca/cacert.pem

📊 環境別の適切な設定

環境SSL検証設定CA証明書推奨度
本番環境VERIFYPEER=true
VERIFYHOST=2
システムまたはcacert.pem⭐⭐⭐⭐⭐ 必須
開発環境(有効証明書)VERIFYPEER=true
VERIFYHOST=2
同上⭐⭐⭐⭐⭐
開発環境(自己署名)VERIFYPEER=true自己署名証明書をCAに追加⭐⭐⭐⭐
一時的なテストのみVERIFYPEER=false
VERIFYHOST=0
不要⭐(非推奨)

⚠️ SSL検証を無効にする危険性

あなたのコードのようにSSL検証を完全に無効にすると:

  1. 中間者攻撃(MITM)の危険:通信を傍受・改ざんされる可能性
  2. データ漏洩:認証情報、APIキー、個人情報が盗まれるリスク
  3. コンプライアンス違反:GDPR、PCI DSSなどの規格に違反

🔧 開発環境での実用的な対応

自己署名証明書を使っている場合

// 自己署名証明書をCAバンドルに統合
$caBundle = file_get_contents('cacert.pem');
$localCert = file_get_contents('localhost.pem');
file_put_contents('combined.pem', $caBundle . "\n" . $localCert);

curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . '/combined.pem');

環境による条件分岐(最後の手段)

// 本当に必要な場合のみ、環境で分岐
if ($_SERVER['SERVER_NAME'] === 'localhost' || $_SERVER['SERVER_ADDR'] === '127.0.0.1') {
    // ローカル環境でのみ検証を緩和
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);

    // ログに警告を記録
    error_log('警告: SSL検証が無効化されています(ローカル環境)');
} else {
    // 本番・ステージング環境では厳格に検証
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
}

📝 安全なcURLラッパー関数の例

function secureCurlRequest(string $url, array $options = []): string {
    $ch = curl_init($url);

    // デフォルト設定(安全重視)
    $defaults = [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_CONNECTTIMEOUT => 10,
    ];

    // CA証明書のパスを自動検出
    $possiblePaths = [
        __DIR__ . '/cacert.pem',
        '/etc/ssl/certs/ca-certificates.crt',
        '/usr/local/ssl/certs/ca-bundle.crt',
    ];

    foreach ($possiblePaths as $path) {
        if (file_exists($path)) {
            $defaults[CURLOPT_CAINFO] = $path;
            break;
        }
    }

    // オプションをマージ
    curl_setopt_array($ch, $options + $defaults);

    $response = curl_exec($ch);

    if ($response === false) {
        $error = curl_error($ch);
        // エラーハンドリング
        throw new RuntimeException("cURLエラー: " . $error);
    }

    curl_close($ch);
    return $response;
}

💡 エラーメッセージの読み方

SSLエラーが発生した場合、以下のようなメッセージが表示されます:

  • SSL certificate problem: self signed certificate
    → 自己署名証明書を使用しています。CA証明書に追加する必要があります。
  • SSL certificate problem: unable to get local issuer certificate
    → CA証明書のパスが正しく設定されていません。
  • SSL certificate problem: certificate has expired
    → 証明書の有効期限が切れています。

🚀 本番環境へのデプロイ前チェックリスト

  1. ✅ SSL検証が有効化されているか確認
  2. ✅ 有効なCA証明書パスが設定されているか確認
  3. VERIFYHOST2に設定されているか確認
  4. ✅ すべてのテスト環境でもSSL検証を有効にしてテスト
  5. ✅ エラーログに警告がないか確認

まとめ

一時的な開発環境の利便性のためにSSL検証を無効にすることは避け、適切なCA証明書の設定を行うことが重要です。これにより、開発中から本番環境と同じセキュリティ条件で動作を確認できます。

コメント

タイトルとURLをコピーしました