【问题标题】:Why does tlstest.paypal.com work from browser but not from my PHP code (useful for Paypal IPN)?为什么 tlstest.paypal.com 可以在浏览器中运行,但不能从我的 PHP 代码中运行(对 Paypal IPN 有用)?
【发布时间】:2018-11-28 23:44:27
【问题描述】:

2018 年 6 月 30 日之后,Paypal won't accept non-TLS 1.2 + HTTP 1.1 requests 不再。
他们创建了 URL https://tlstest.paypal.com/ 来测试连接是否正常。如果我们在浏览器中打开这个 URL,就会成功:

PayPal_Connection_OK

问题:使用以下代码从 PHP 连接时为什么会失败?(我根本没有得到任何响应,浏览器仍处于等待“状态”like this,所以它没有甚至到达echo $errno; echo $errstr;)

<?php
$req = '';    // usually I use $req = 'cmd=_notify-validate'; for IPN
$header .= "POST / HTTP/1.1\r\n";
$header .= "Host: tlstest.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen('tls://tlstest.paypal.com', 443, $errno, $errstr, 30);

if (!$fp) {
    echo $errno;
    echo $errstr;
} else {
    fputs($fp, $header);
    while (!feof($fp))
    {
        $res = fgets($fp, 1024);
        echo $res;
    }
    fclose($fp);
}
?>

注意:

【问题讨论】:

  • 您可能应该在if 的第一个分支中添加一些调试内容,该分支当前为空。 fsockopen 可能不使用 TLS1.2,它可能取决于许多事情,例如您未披露的 PHP 版本,以及 openssl 的安装方式。我认为另一个问题的主要建议也适用于这里:fsockopen 作为 API 太低,请使用真正的 HTTP 客户端,如curl
  • why does it fail 什么失败了?您实际上还没有解释错误是什么。是否设置了 errnoerrstr?它们包含什么?
  • 基于此 - 我假设实际发生的情况是页面加载将挂起 30 秒( fsock 超时 ),然后返回连接超时错误。您是否能够打开与任何其他主机的连接?在任何港口?你在VPS上运行吗?或者在一些共享的网络主机上?防火墙规则/访问限制策略可能会阻止来自主机的出站请求。将 30 减少到 5 并确认页面挂起 5 秒后再报错 -
  • 您至少需要 openssl 1.0.1 来支持 TLS 1.2,打印 OPENSSL_VERSION_NUMBER 它应该至少为 0x1000100。你的版本可能太旧了,PHP 5.5 已经 2 年不支持了...
  • 你为什么不想使用 PHP 中的curl 库?在同一主机上,您是否尝试过命令行wgetcurl 甚至openssl s_client 来确定问题出在您的代码/库还是网络问题?或者您是否尝试将您的代码用于另一台主机?请注意,实际上您可能需要 SNI,您检查了吗?

标签: php ssl paypal paypal-ipn tls1.2


【解决方案1】:

通过将 tls:// 更改为 ssl:// 对我来说完全没有意义,但这也是为什么使用 fsockopen 是一个太低级的库而不能与它进行 HTTP 交换的原因(你应该使用适当的 HTTP 客户端库),同时在 TLS 方面配置不够。

$fp = fsockopen('tls://tlstest.paypal.com', 443, $errno, $errstr, 30); 我明白了:

HTTP/1.1 426 Unknown
Server: AkamaiGHost
Mime-Version: 1.0
Content-Type: text/html
Content-Length: 267
Expires: Fri, 22 Jun 2018 19:49:46 GMT
Date: Fri, 22 Jun 2018 19:49:46 GMT
Connection: keep-alive
Upgrade: TLS/1.2

<HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD><BODY>
<H1>Access Denied</H1>

You don't have permission to access "http&#58;&#47;&#47;tlstest&#46;paypal&#46;com&#47;" on this server.<P>
Reference&#32;&#35;18&#46;8024a17&#46;1529696986&#46;1fc51318
</BODY>
</HTML>

但是$fp = fsockopen('ssl://tlstest.paypal.com', 443, $errno, $errstr, 30); 我明白了:

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Date: Fri, 22 Jun 2018 20:05:35 GMT
Connection: keep-alive

然后就挂了,可能是因为是keep-alive连接,而且缓冲区小于1024,所以你没有得到下面的body内容。 这可能是“PayPal_Connection_OK”,因为它与Content-Length 中显示的长度完全匹配。 这再次表明您应该使用 HTTP 客户端库,而不是尝试(严重)在 fsockopen 之上重新实现 HTTP。

【讨论】:

    【解决方案2】:

    为了完整起见,这是一个工作代码(完全归功于 PatrickMevzek 的回答):

    <?php
    $req = '';
    $header = "POST / HTTP/1.1\r\n";
    $header .= "Host: tlstest.paypal.com\r\n";
    $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
    $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
    $fp = fsockopen('ssl://tlstest.paypal.com', 443, $errno, $errstr, 3);
    
    if (!$fp) {
        echo $errno;
        echo $errstr;
    } else {
        fputs($fp, $header);
        while (!feof($fp))
        {
            $res = fgets($fp, 21);   // 21 because length of "PayPal_Connection_OK"
            echo $res;
        }
        fclose($fp);
    }
    ?>
    

    这是来自服务器的答案:

    # php -f test.php
    HTTP/1.1 200 OK
    Content-Type: text/html
    Content-Length: 20
    Date: Fri, 22 Jun 2018 20:19:56 GMT
    Connection: keep-alive
    
    PayPal_Connection_OK
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-06
      • 2016-12-13
      相关资源
      最近更新 更多