【问题标题】:PayPal IPN acknowledgements failing with SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failurePayPal IPN 确认因 SSL 例程而失败:SSL3_READ_BYTES:sslv3 警报握手失败
【发布时间】:2014-12-10 08:47:30
【问题描述】:

我们方面没有任何变化,可能与 POODLE/SSL3 有关,我们对 PPIPNMessage::validate 的 PayPal API 调用现在失败了。

SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure

IPN 的结帐和接收都很好(我们从不支持 SSL3 传入),只是在确认 IPN 时失败(奇怪的是 PayPal 不会再试一次,即使我们失败了)

从同一服务器命令行运行 curl 成功

$ curl -iv https://ipnpb.paypal.com/cgi-bin/webscr
* About to connect() to ipnpb.paypal.com port 443 (#0)
*   Trying 173.0.88.8... connected
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using AES256-SHA
* Server certificate:
*    subject: 1.3.6.1.4.1.311.60.2.1.3=US; 1.3.6.1.4.1.311.60.2.1.2=Delaware; businessCategory=Private Organization; serialNumber=3014267; C=US; postalCode=95131-2021; ST=California; L=San Jose; street=2211 N 1st St; O=PayPal, Inc.; OU=PayPal Production; CN=ipnpb.paypa
*    start date: 2013-06-28 00:00:00 GMT
*    expire date: 2015-08-02 23:59:59 GMT
*    subjectAltName: ipnpb.paypal.com matched
*    issuer: C=US; O=VeriSign, Inc.; OU=VeriSign Trust Network; OU=Terms of use at https://www.verisign.com/rpa (c)06; CN=VeriSign Class 3 Extended Validation SSL CA
*    SSL certificate verify ok.
> GET /cgi-bin/webscr HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: ipnpb.paypal.com
> Accept: */*

我确实注意到 ssllabs.com 显示 4 个 IP 中的 1 个仍支持此端点上的 SSL3。

【问题讨论】:

    标签: ssl paypal paypal-ipn


    【解决方案1】:

    PayPal 禁用 SSLv3 以响应“POODLE”漏洞。在这里阅读:PayPal Response

    如果您使用 ipnlistener.php,请强制使用 TLS 1.2 作为 SSL 协议:

    curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); 
    

    (更新:2017 年需要 TLS V1.2)

    注意: PayPal 正在升级用于保护与其系统建立的所有外部连接的协议。传输层安全版本 1.2 (TLS 1.2) 和超文本传输​​协议版本 1.1 (HTTP/1.1) 将成为 2018 年与 PayPal 通信的强制要求。请参阅他们的Security Roadmap

    【讨论】:

      【解决方案2】:

      我在使用 PayPal 检查 IPN 时遇到同样的错误。这是问题的解决方案

      我使用的是 PHP 5.3,而 PHP 5.3 不再支持 SSL 版本 3。我已经升级到 PHP 版本 5.4 并添加了下面的代码行。这对我有用。

      curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
      curl_setopt($ch, CURLOPT_POST, 1);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
      curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
      #curl_setopt($ch, CURLOPT_SSLVERSION, 4);
      curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); 
      curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'TLSv1');
      

      【讨论】:

        【解决方案3】:

        在调用函数 PPHttpPost() 时在您的 paypal 类中使用此设置

        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 
        curl_setopt($ch, CURLOPT_SSLVERSION, 6); //6 is for TLSV1.2
        

        【讨论】:

        • 这不是生产环境中的推荐设置。这些设置应为 TRUE,以确保中间没有第三方伪造您要连接的主机。
        【解决方案4】:

        对我来说,其他答案中提到的这些事情都没有奏效,但是,我能够找出修复方法并且它是相同的,但我花了一段时间才找出我正在使用的 sdk 放置它的卷曲的文件config 因为没有 ipn 侦听器文件,并且“PPHttpConfig”会给我一个致命错误

        所以我发现这是他们现在使用的监听器文件:

        PayPal-PHP-SDK/paypal/rest-api-sdk-php/lib/PayPal/Core/PayPalHttpConfig.php

        我在里面找到了

        CURLOPT_SSLVERSION => 1

        我改成:

        CURLOPT_SSLVERSION => 4

        这为我解决了问题。

        【讨论】:

        • 感谢 @Kit Ramos 的查找,以下是截至 2016 年 4 月的所有可能值: CURL_SSLVERSION_DEFAULT (0) CURL_SSLVERSION_TLSv1 (1) CURL_SSLVERSION_SSLv2 (2) CURL_SSLVERSION_SSLv3 (3) CURL_SSLVERSION_TLSv1_0 (4) CURL_SSLVERSION_TLSv1 5) CURL_SSLVERSION_TLSv1_2 (6)
        【解决方案5】:

        这和Error 0x1408F10B: "SSL3_GET_RECORD:wrong version number" with PayPal SDK一样的问题

        我们使用硬编码 CURLOPT_SSLVERSION 到 3 的 PayPal API 版本。

        我们的解决方法是在任何 PayPal 调用之前插入它。

        PPHttpConfig::$DEFAULT_CURL_OPTS[CURLOPT_SSLVERSION] = 4;
        

        【讨论】:

        • 是的,我们的 IPN 正在接受验证。我看到 SSLv3 已在一夜之间从沙盒端点和大多数实时 IPN 回复端点中删除。我不得不认为它很快就会从主要的 api-3t.paypal.com 端点中删除——此时所有对 PayPal 的 SSLv3 调用都将失败。
        • 是的,感谢 Paypal 的提前通知。实时站点出现故障并浪费了数小时来寻找解决方案。非常不开心。修复对我有用。
        • 嗯,我使用标准 cURL,当我设置 curl_setopt($ch, CURLOPT_SSLVERSION, 4);它仍然无法正常工作。有什么想法吗?
        • 嗯,PHP 文档说值 1 表示 TLSv1.0。它没有定义 4 - php.net/manual/en/function.curl-setopt.php 的含义
        • 456 值适用于 cURL 7.34+,因此某些较旧的 PHP 版本无法使用这些值。我使用了值1,它在我的半过时版本的 PHP 上运行良好。 :)
        【解决方案6】:

        SSLv3 不再适用于 www.paypal.com:

        # sslscan www.paypal.com|grep Accepted
        Accepted  TLSv1  256 bits  AES256-SHA
        Accepted  TLSv1  128 bits  AES128-SHA
        Accepted  TLSv1  168 bits  DES-CBC3-SHA
        Accepted  TLSv1  128 bits  RC4-SHA
        Accepted  TLSv1  128 bits  RC4-MD5
        

        您应该将 CURLOPT_SSLVERSION 更改为 TLSv1

        curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
        

        (此“CURL_SSLVERSION_TLSv1”-constant 不适用于较旧的 PHP 版本,因此另一种方法是简单地删除 CURLOPT_SSLVERSION 强制执行。)

        【讨论】:

          【解决方案7】:

          我遇到了同样的问题...只需更改 ipnlistener.php

          上的以下行

          来自:

          curl_setopt($ch, CURLOPT_SSLVERSION, 3);
          

          到:

          curl_setopt($ch, CURLOPT_SSLVERSION, 4);
          

          ipnlistener.php

          <?php
          /**
          * PayPal IPN Listener
          *
          * A class to listen for and handle Instant Payment Notifications (IPN) from
          * the PayPal server.
          *
          * https://github.com/Quixotix/PHP-PayPal-IPN
          *
          * @package PHP-PayPal-IPN
          * @author Micah Carrick
          * @copyright (c) 2012 - Micah Carrick
          * @version 2.1.0
          */
          class IpnListener {
          
              /**
          * If true, the recommended cURL PHP library is used to send the post back
          * to PayPal. If flase then fsockopen() is used. Default true.
          *
          * @var boolean
          */
              public $use_curl = true;
          
              /**
          * If true, explicitly sets cURL to use SSL version 3. Use this if cURL
          * is compiled with GnuTLS SSL.
          *
          * @var boolean
          */
              public $force_ssl_v3 = true;
          
              /**
          * If true, cURL will use the CURLOPT_FOLLOWLOCATION to follow any
          * "Location: ..." headers in the response.
          *
          * @var boolean
          */
              public $follow_location = false;
          
              /**
          * If true, an SSL secure connection (port 443) is used for the post back
          * as recommended by PayPal. If false, a standard HTTP (port 80) connection
          * is used. Default true.
          *
          * @var boolean
          */
              public $use_ssl = true;
          
              /**
          * If true, the paypal sandbox URI www.sandbox.paypal.com is used for the
          * post back. If false, the live URI www.paypal.com is used. Default false.
          *
          * @var boolean
          */
              public $use_sandbox = false;
          
              /**
          * The amount of time, in seconds, to wait for the PayPal server to respond
          * before timing out. Default 30 seconds.
          *
          * @var int
          */
              public $timeout = 30;
          
              private $post_data = array();
              private $post_uri = '';
              private $response_status = '';
              private $response = '';
          
              const PAYPAL_HOST = 'www.paypal.com';
              const SANDBOX_HOST = 'www.sandbox.paypal.com';
          
              /**
          * Post Back Using cURL
          *
          * Sends the post back to PayPal using the cURL library. Called by
          * the processIpn() method if the use_curl property is true. Throws an
          * exception if the post fails. Populates the response, response_status,
          * and post_uri properties on success.
          *
          * @param string The post data as a URL encoded string
          */
              protected function curlPost($encoded_data) {
          
                  if ($this->use_ssl) {
                      $uri = 'https://'.$this->getPaypalHost().'/cgi-bin/webscr';
                      $this->post_uri = $uri;
                  } else {
                      $uri = 'http://'.$this->getPaypalHost().'/cgi-bin/webscr';
                      $this->post_uri = $uri;
                  }
          
                  $ch = curl_init();
          
                          curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
                          curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
                          curl_setopt($ch, CURLOPT_CAINFO,
                           dirname(__FILE__)."/cert/api_cert_chain.crt");
                  curl_setopt($ch, CURLOPT_URL, $uri);
                  curl_setopt($ch, CURLOPT_POST, true);
                  curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded_data);
                  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $this->follow_location);
                  curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
                  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                  curl_setopt($ch, CURLOPT_HEADER, true);
                  curl_setopt($ch, CURLOPT_SSLVERSION, 4);
          
                  if ($this->force_ssl_v3) {
                      curl_setopt($ch, CURLOPT_SSLVERSION, 4); //Modified from 3 to 4
                  }
          
                  $this->response = curl_exec($ch);
                  $this->response_status = strval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
          
                  if ($this->response === false || $this->response_status == '0') {
                      $errno = curl_errno($ch);
                      $errstr = curl_error($ch);
                      throw new Exception("cURL error: [$errno] $errstr");
                  }
              }
          
              /**
          * Post Back Using fsockopen()
          *
          * Sends the post back to PayPal using the fsockopen() function. Called by
          * the processIpn() method if the use_curl property is false. Throws an
          * exception if the post fails. Populates the response, response_status,
          * and post_uri properties on success.
          *
          * @param string The post data as a URL encoded string
          */
              protected function fsockPost($encoded_data) {
          
                  if ($this->use_ssl) {
                      $uri = 'ssl://'.$this->getPaypalHost();
                      $port = '443';
                      $this->post_uri = $uri.'/cgi-bin/webscr';
                  } else {
                      $uri = $this->getPaypalHost(); // no "http://" in call to fsockopen()
                      $port = '80';
                      $this->post_uri = 'http://'.$uri.'/cgi-bin/webscr';
                  }
          
                  $fp = fsockopen($uri, $port, $errno, $errstr, $this->timeout);
          
                  if (!$fp) {
                      // fsockopen error
                      throw new Exception("fsockopen error: [$errno] $errstr");
                  }
          
                  $header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
                  $header .= "Host: ".$this->getPaypalHost()."\r\n";
                  $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
                  $header .= "Content-Length: ".strlen($encoded_data)."\r\n";
                  $header .= "Connection: Close\r\n\r\n";
          
                  fputs($fp, $header.$encoded_data."\r\n\r\n");
          
                  while(!feof($fp)) {
                      if (empty($this->response)) {
                          // extract HTTP status from first line
                          $this->response .= $status = fgets($fp, 1024);
                          $this->response_status = trim(substr($status, 9, 4));
                      } else {
                          $this->response .= fgets($fp, 1024);
                      }
                  }
          
                  fclose($fp);
              }
          
              private function getPaypalHost() {
                  if ($this->use_sandbox) return self::SANDBOX_HOST;
                  else return self::PAYPAL_HOST;
              }
          
              /**
          * Get POST URI
          *
          * Returns the URI that was used to send the post back to PayPal. This can
          * be useful for troubleshooting connection problems. The default URI
          * would be "ssl://www.sandbox.paypal.com:443/cgi-bin/webscr"
          *
          * @return string
          */
              public function getPostUri() {
                  return $this->post_uri;
              }
          
              /**
          * Get Response
          *
          * Returns the entire response from PayPal as a string including all the
          * HTTP headers.
          *
          * @return string
          */
              public function getResponse() {
                  return $this->response;
              }
          
              /**
          * Get Response Status
          *
          * Returns the HTTP response status code from PayPal. This should be "200"
          * if the post back was successful.
          *
          * @return string
          */
              public function getResponseStatus() {
                  return $this->response_status;
              }
          
              /**
          * Get Text Report
          *
          * Returns a report of the IPN transaction in plain text format. This is
          * useful in emails to order processors and system administrators. Override
          * this method in your own class to customize the report.
          *
          * @return string
          */
              public function getTextReport() {
          
                  $r = '';
          
                  // date and POST url
                  for ($i=0; $i<80; $i++) { $r .= '-'; }
                  $r .= "\n[".date('m/d/Y g:i A').'] - '.$this->getPostUri();
                  if ($this->use_curl) $r .= " (curl)\n";
                  else $r .= " (fsockopen)\n";
          
                  // HTTP Response
                  for ($i=0; $i<80; $i++) { $r .= '-'; }
                  $r .= "\n{$this->getResponse()}\n";
          
                  // POST vars
                  for ($i=0; $i<80; $i++) { $r .= '-'; }
                  $r .= "\n";
          
                  foreach ($this->post_data as $key => $value) {
                      $r .= str_pad($key, 25)."$value\n";
                  }
                  $r .= "\n\n";
          
                  return $r;
              }
          
              /**
          * Process IPN
          *
          * Handles the IPN post back to PayPal and parsing the response. Call this
          * method from your IPN listener script. Returns true if the response came
          * back as "VERIFIED", false if the response came back "INVALID", and
          * throws an exception if there is an error.
          *
          * @param array
          *
          * @return boolean
          */
              public function processIpn($post_data=null) {
          
                  $encoded_data = 'cmd=_notify-validate';
          
                  if ($post_data === null) {
                      // use raw POST data
                      if (!empty($_POST)) {
                          $this->post_data = $_POST;
                          $encoded_data .= '&'.file_get_contents('php://input');
                      } else {
                          throw new Exception("No POST data found.");
                      }
                  } else {
                      // use provided data array
                      $this->post_data = $post_data;
          
                      foreach ($this->post_data as $key => $value) {
                          $encoded_data .= "&$key=".urlencode($value);
                      }
                  }
          
                  if ($this->use_curl) $this->curlPost($encoded_data);
                  else $this->fsockPost($encoded_data);
          
                  if (strpos($this->response_status, '200') === false) {
                      throw new Exception("Invalid response status: ".$this->response_status);
                  }
          
                  if (strpos($this->response, "VERIFIED") !== false) {
                      return true;
                  } elseif (strpos($this->response, "INVALID") !== false) {
                      return false;
                  } else {
                      throw new Exception("Unexpected response from PayPal.");
                  }
              }
          
              /**
          * Require Post Method
          *
          * Throws an exception and sets a HTTP 405 response header if the request
          * method was not POST.
          */
              public function requirePostMethod() {
                  // require POST requests
                  if ($_SERVER['REQUEST_METHOD'] && $_SERVER['REQUEST_METHOD'] != 'POST') {
                      header('Allow: POST', true, 405);
                      throw new Exception("Invalid HTTP request method.");
                  }
              }
          }
          ?>
          

          【讨论】:

          • 我们使用的是同一个库,但它在我的网站上不起作用。我现在什至没有收到 IPN 请求。有什么想法吗?
          • 强制 cURL 使用 4 是解决问题的简单而正确的方法!像魅力一样工作!
          【解决方案8】:

          我同意这一点。 花了 小时 试图弄明白。 在我的 IPN 监听器上,我不得不删除“force ssl v3”。 从那一刻起,我的 IPN 又开始工作了。

          只要做一个 curl -v https://paypal.com

          它显示: 使用 TLS_RSA_WITH_AES_256_CBC_SHA 的 SSL 连接

          【讨论】:

            猜你喜欢
            • 2017-01-01
            • 2016-05-04
            • 2016-07-05
            • 2016-04-27
            • 2015-08-29
            • 2016-04-29
            • 2016-04-29
            • 2016-07-01
            相关资源
            最近更新 更多