首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP cURL 獲取 HTTPS 請求完整指南:SSL 憑證驗證、常見錯誤與安全最佳實踐

     編輯:PHP 相關知識

使用 cURL 存取 HTTPS API 時,常遇到 SSL certificate problem: certificate verify failed。本文系統說明原因、解決方案、安全風險與生產環境最佳配置。

錯誤原因

cURL 預設驗證伺服器 SSL 憑證:

  • 信任錨點 (CA 憑證) 缺失或過期
  • 伺服器使用自簽憑證
  • 憑證鏈不完整(缺少中間 CA)
  • 主機名不匹配 (CN/SAN)
  • 系統 CA 憑證包過舊 (如 CentOS 6/7 預設 curl 版本舊)

快速解決(開發/測試環境)

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/endpoint');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// 【臨時繞過】僅限開發/測試使用!
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 不驗證憑證真偽
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);     // 不驗證主機名

$response = curl_exec($ch);
if ($err = curl_error($ch)) { die('cURL Error: ' . $err); }
curl_close($ch);

⚠️ 生產環境嚴禁使用,會暴露中間人攻擊 (MITM) 風險。

生產環境正確配置

方案一:指定 CA 憑證包(推薦)

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/endpoint');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// 指定最新 CA 憑證包 (cacert.pem)
curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . '/cacert.pem');
// 或使用系統路徑
// curl_setopt($ch, CURLOPT_CAINFO, '/etc/ssl/certs/ca-certificates.crt'); // Debian/Ubuntu
// curl_setopt($ch, CURLOPT_CAINFO, '/etc/pki/tls/certs/ca-bundle.crt');   // CentOS/RHEL

// 正常驗證
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // 2 = 驗證 CN/SAN 匹配

$response = curl_exec($ch);

方案二:更新系統 CA 憑證

# Ubuntu/Debian
apt-get update && apt-get install -y ca-certificates

# CentOS/RHEL
yum update -y ca-certificates

# Alpine
apk add --no-cache ca-certificates

方案三:下載最新 cacert.pem(跨平台通用)

# 定期更新腳本 (cron 每月)
wget -q -O /usr/local/share/cacert.pem https://curl.se/ca/cacert.pem
# PHP 配置
curl_setopt($ch, CURLOPT_CAINFO, '/usr/local/share/cacert.pem');

完整 HTTPS cURL 封裝函式

/**
 * 安全的 HTTPS cURL 請求
 * @param string $url
 * @param array $postData POST 欄位
 * @param array $headers 自定義 Header
 * @param int $timeout 秒
 * @return string|false 回應內容或 false
 */
function curlHttps(string $url, array $postData = [], array $headers = [], int $timeout = 30)
{
    $ch = curl_init();
    
    // SSL 安全配置
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
    // 可選:指定 CA 憑證包
    // curl_setopt($ch, CURLOPT_CAINFO, '/path/to/cacert.pem');
    
    // 基礎配置
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
    curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate'); // 支援壓縮
    
    // Header
    $defaultHeaders = [
        'User-Agent: PHP-cURL/' . phpversion(),
        'Accept: application/json, text/plain, */*',
    ];
    curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge($defaultHeaders, $headers));
    
    // POST 資料
    if (!empty($postData)) {
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
    }
    
    // 執行
    $response = curl_exec($ch);
    $errno = curl_errno($ch);
    $error = curl_error($ch);
    $info = curl_getinfo($ch);
    curl_close($ch);
    
    if ($errno) {
        error_log("cURL Error [$errno]: $error | URL: $url | HTTP: " . ($info['http_code'] ?? 'N/A'));
        return false;
    }
    
    // 可選:檢查 HTTP 狀態碼
    if ($info['http_code'] >= 400) {
        error_log("HTTP Error {$info['http_code']}: $url");
        // 視業務決定是否回傳 false
    }
    
    return $response;
}

// 使用範例
$apiUrl = 'https://api.payment.com/v1/charge';
$data = ['order_id' => '20240101001', 'amount' => 99900];
$headers = ['Authorization: Bearer ' . $accessToken];
$result = curlHttps($apiUrl, $data, $headers, 10);
if ($result === false) { /* 處理失敗 */ }
$response = json_decode($result, true);

常見 SSL 選項對比

選項值含義建議
CURLOPT_SSL_VERIFYPEERtrue驗證伺服器憑證真偽生產必開
CURLOPT_SSL_VERIFYPEERfalse跳過憑證驗證僅開發/測試
CURLOPT_SSL_VERIFYHOST2驗證憑證 CN/SAN 匹配主機名生產必開
CURLOPT_SSL_VERIFYHOST0不驗證主機名不建議
CURLOPT_CAINFO路徑指定 CA 憑證包檔案推薦指定
CURLOPT_CAPATH目錄指定 CA 憑證目錄替代方案
CURLOPT_SSLCERT路徑客戶端憑證 (mTLS)雙向認證時
CURLOPT_SSLKEY路徑客戶端私鑰雙向認證時
CURLOPT_SSLKEYPASSWD字串私鑰密碼私鑰加密時

疑難排解檢查清單

  1. curl -v https://api.example.com 命令列測試,觀察憑證鏈輸出
  2. 檢查 openssl s_client -connect api.example.com:443 -servername api.example.com
  3. 確認 PHP cURL 版本:php -i | grep "cURL version",建議 ≥ 7.60
  4. 確認 openssl 版本:php -i | grep "OpenSSL version"
  5. 若為自簽憑證:將自簽 CA 加入 cacert.pem 或使用 CURLOPT_CAINFO 指定
  6. 檢查防火牆/代理是否攔截 TLS 握手 (SSL Bump)

安全最佳實踐總結

  • ✅ 生產環境 **永遠啟用** CURLOPT_SSL_VERIFYPEER = true、CURLOPT_SSL_VERIFYHOST = 2
  • ✅ 部署並定期更新 cacert.pem(建議每月)
  • ✅ 使用 CURLOPT_CAINFO 顯式指定 CA 憑證包,避免依賴系統路徑差異
  • ✅ 記錄 cURL 錯誤碼、HTTP 狀態碼、耗時,納入監控告警
  • ✅ 敏感 API 採用 mTLS (雙向認證) + 簽名驗證
  • ✅ 設定合理 CURLOPT_TIMEOUT、CURLOPT_CONNECTTIMEOUT 防止阻塞

正確處理 HTTPS 憑證驗證是 API 整合安全的基石。**拒絕「關閉驗證」捷徑**,投入少量精力配置 CA 憑證包,即可獲得生產級的傳輸層安全保障。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved