使用 cURL 存取 HTTPS API 時,常遇到 SSL certificate problem: certificate verify failed。本文系統說明原因、解決方案、安全風險與生產環境最佳配置。
cURL 預設驗證伺服器 SSL 憑證:
$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) 風險。
$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);
# 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
# 定期更新腳本 (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 請求
* @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);
| 選項 | 值 | 含義 | 建議 |
|---|---|---|---|
| CURLOPT_SSL_VERIFYPEER | true | 驗證伺服器憑證真偽 | 生產必開 |
| CURLOPT_SSL_VERIFYPEER | false | 跳過憑證驗證 | 僅開發/測試 |
| CURLOPT_SSL_VERIFYHOST | 2 | 驗證憑證 CN/SAN 匹配主機名 | 生產必開 |
| CURLOPT_SSL_VERIFYHOST | 0 | 不驗證主機名 | 不建議 |
| CURLOPT_CAINFO | 路徑 | 指定 CA 憑證包檔案 | 推薦指定 |
| CURLOPT_CAPATH | 目錄 | 指定 CA 憑證目錄 | 替代方案 |
| CURLOPT_SSLCERT | 路徑 | 客戶端憑證 (mTLS) | 雙向認證時 |
| CURLOPT_SSLKEY | 路徑 | 客戶端私鑰 | 雙向認證時 |
| CURLOPT_SSLKEYPASSWD | 字串 | 私鑰密碼 | 私鑰加密時 |
curl -v https://api.example.com 命令列測試,觀察憑證鏈輸出openssl s_client -connect api.example.com:443 -servername api.example.comphp -i | grep "cURL version",建議 ≥ 7.60openssl 版本:php -i | grep "OpenSSL version"CURLOPT_CAINFO 指定CURLOPT_SSL_VERIFYPEER = true、CURLOPT_SSL_VERIFYHOST = 2cacert.pem(建議每月)CURLOPT_CAINFO 顯式指定 CA 憑證包,避免依賴系統路徑差異CURLOPT_TIMEOUT、CURLOPT_CONNECTTIMEOUT 防止阻塞正確處理 HTTPS 憑證驗證是 API 整合安全的基石。**拒絕「關閉驗證」捷徑**,投入少量精力配置 CA 憑證包,即可獲得生產級的傳輸層安全保障。