PHP 怎么PHP mTLS 绑定

wen PHP项目 1

本文目录导读:

PHP 怎么PHP mTLS 绑定

  1. PHP 作为客户端(发起请求)
  2. PHP 作为服务端(接收请求并验证客户端)
  3. 完整的客户端证书生成脚本
  4. PHP 框架集成示例(Laravel)
  5. mTLS 测试脚本
  6. 注意事项

在 PHP 中实现 mTLS(双向 TLS)绑定,主要涉及客户端证书验证服务端证书验证两个方面,以下是完整的实现方案:

PHP 作为客户端(发起请求)

cURL 实现 mTLS

<?php
$ch = curl_init('https://example.com/api');
// 设置 CA 证书(验证服务端)
curl_setopt($ch, CURLOPT_CAINFO, '/path/to/ca-cert.pem');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
// 客户端证书和私钥
curl_setopt($ch, CURLOPT_SSLCERT, '/path/to/client-cert.pem');
curl_setopt($ch, CURLOPT_SSLKEY, '/path/to/client-key.pem');
curl_setopt($ch, CURLOPT_SSLKEYPASSWD, 'your_key_password'); // 如果私钥有密码
// 如果使用 PKCS#12 格式
// curl_setopt($ch, CURLOPT_SSLCERT, '/path/to/client.p12');
// curl_setopt($ch, CURLOPT_SSLCERTPASSWD, 'p12_password');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
    echo 'Error: ' . curl_error($ch);
}
curl_close($ch);

PHP Stream Context 实现

<?php
$context = stream_context_create([
    'ssl' => [
        'cafile' => '/path/to/ca-cert.pem',
        'verify_peer' => true,
        'verify_peer_name' => true,
        'local_cert' => '/path/to/client-cert.pem',
        'local_pk' => '/path/to/client-key.pem',
        'passphrase' => 'your_key_password',
        'allow_self_signed' => false
    ]
]);
$result = file_get_contents('https://example.com/api', false, $context);

PHP 作为服务端(接收请求并验证客户端)

Apache 配置示例

<VirtualHost *:443>
    ServerName example.com
    # HTTPS 配置
    SSLEngine on
    SSLCertificateFile /path/to/server-cert.pem
    SSLCertificateKeyFile /path/to/server-key.pem
    # 客户端证书验证
    SSLVerifyClient require
    SSLVerifyDepth 10
    SSLCACertificateFile /path/to/client-ca-cert.pem
    # 可选:将客户端证书信息传递给 PHP
    SSLOptions +StdEnvVars
    DocumentRoot /var/www/html
</VirtualHost>

Nginx 配置

server {
    listen 443 ssl;
    server_name example.com;
    ssl_certificate /path/to/server-cert.pem;
    ssl_certificate_key /path/to/server-key.pem;
    ssl_client_certificate /path/to/client-ca-cert.pem;
    ssl_verify_client on;
    ssl_verify_depth 10;
    location / {
        proxy_pass http://php-fpm:9000;
        proxy_set_header X-Client-Cert $ssl_client_raw_cert;
        proxy_set_header X-Client-Verify $ssl_client_verify;
        proxy_set_header X-Client-DN $ssl_client_s_dn;
    }
}

PHP 获取客户端证书信息

<?php
// 获取客户端证书信息(Apache 环境)
if (isset($_SERVER['SSL_CLIENT_VERIFY'])) {
    $verify = $_SERVER['SSL_CLIENT_VERIFY']; // SUCCESS, FAILED, NONE
    if ($verify === 'SUCCESS') {
        // 证书信息
        $certDN = $_SERVER['SSL_CLIENT_S_DN'];
        $certIssuer = $_SERVER['SSL_CLIENT_I_DN'];
        $certSerial = $_SERVER['SSL_CLIENT_M_SERIAL'];
        $certExpiry = $_SERVER['SSL_CLIENT_V_END'];
        // 从证书 DN 中提取信息
        $parts = explode(',', $certDN);
        foreach ($parts as $part) {
            list($key, $value) = explode('=', $part, 2);
            $info[strtolower(trim($key))] = trim($value);
        }
        $commonName = $info['cn'] ?? '';
        $email = $info['emailaddress'] ?? '';
        // 可以基于证书信息做业务逻辑
        if (!isAuthorized($commonName, $email)) {
            http_response_code(403);
            exit('Unauthorized');
        }
    }
}
// Nginx 环境
if (isset($_SERVER['HTTP_X_CLIENT_VERIFY'])) {
    if ($_SERVER['HTTP_X_CLIENT_VERIFY'] === 'SUCCESS') {
        $clientDN = $_SERVER['HTTP_X_CLIENT_DN'] ?? '';
        // 处理证书信息
    }
}
// 更复杂的证书解析(从原始证书)
if (isset($_SERVER['HTTP_X_CLIENT_CERT'])) {
    $certPem = "-----BEGIN CERTIFICATE-----\n" 
                . chunk_split(base64_decode($_SERVER['HTTP_X_CLIENT_CERT']), 64, "\n")
                . "-----END CERTIFICATE-----";
    $certData = openssl_x509_parse($certPem);
    if ($certData) {
        $subject = $certData['subject'];
        $commonName = $subject['CN'] ?? '';
        $serialNumber = $certData['serialNumber'] ?? '';
        $validFrom = $certData['validFrom_time_t'] ?? 0;
        $validTo = $certData['validTo_time_t'] ?? 0;
    }
}
// 自定义授权逻辑
function isAuthorized($cn, $email) {
    // 检查证书是否在授权列表中
    $authorizedCerts = [
        'John Doe' => 'john@example.com',
        'Jane Smith' => 'jane@example.com'
    ];
    return isset($authorizedCerts[$cn]) && $authorizedCerts[$cn] === $email;
}
?>

完整的客户端证书生成脚本

#!/bin/bash
# 生成自签名 CA
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt \
    -subj "/C=US/ST=State/L=City/O=Organization/CN=My CA"
# 生成客户端证书
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr \
    -subj "/C=US/ST=State/L=City/O=Organization/CN=John Doe/emailAddress=john@example.com"
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
    -out client.crt -days 365 -sha256
# 生成 PKCS#12 格式(包含证书和私钥)
openssl pkcs12 -export -out client.p12 -inkey client.key -in client.crt -certfile ca.crt

PHP 框架集成示例(Laravel)

// app/Http/Middleware/VerifyClientCertificate.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class VerifyClientCertificate
{
    public function handle(Request $request, Closure $next)
    {
        if (!$this->hasValidClientCertificate($request)) {
            return response('Unauthorized', 403);
        }
        // 将证书信息存入请求属性
        $request->attributes->add([
            'client_cert' => $this->getClientCertificateData($request)
        ]);
        return $next($request);
    }
    protected function hasValidClientCertificate($request)
    {
        // Nginx/Apache 传递的验证结果
        return $request->server('SSL_CLIENT_VERIFY') === 'SUCCESS' ||
               $request->header('X-Client-Verify') === 'SUCCESS';
    }
    protected function getClientCertificateData($request)
    {
        return [
            'subject' => $request->server('SSL_CLIENT_S_DN', $request->header('X-Client-DN', '')),
            'issuer' => $request->server('SSL_CLIENT_I_DN', ''),
            'serial' => $request->server('SSL_CLIENT_M_SERIAL', ''),
        ];
    }
}

mTLS 测试脚本

<?php
// test-mtls.php - 简单的 mTLS 测试
class MTLSClient {
    private $certFile;
    private $keyFile;
    private $caFile;
    public function __construct($cert, $key, $ca) {
        $this->certFile = $cert;
        $this->keyFile = $key;
        $this->caFile = $ca;
    }
    public function request($url, $method = 'GET', $data = null) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_CAINFO => $this->caFile,
            CURLOPT_SSLCERT => $this->certFile,
            CURLOPT_SSLKEY => $this->keyFile,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CUSTOMREQUEST => $method,
            CURLOPT_TIMEOUT => 30
        ]);
        if ($data) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        }
        $response = curl_exec($ch);
        $error = curl_error($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        return [
            'http_code' => $httpCode,
            'response' => $response,
            'error' => $error
        ];
    }
}
// 使用示例
$client = new MTLSClient(
    '/path/to/client.crt',
    '/path/to/client.key',
    '/path/to/ca.crt'
);
$result = $client->request('https://api.example.com/secure-endpoint', 'GET');
var_dump($result);
?>

注意事项

  1. 证书安全:私钥文件权限设为 600,确保只有 web 服务器用户可读
  2. 证书轮换:实施证书过期监控和自动更新机制
  3. 错误处理:适当处理各种 SSL/TLS 错误情况
  4. 性能优化:开启 SSL 会话缓存以提高性能
  5. 日志记录:记录证书验证失败和被拒绝的请求

通过这些配置和代码,可以全面实现 PHP 应用的 mTLS 绑定。

抱歉,评论功能暂时关闭!