web-dev-qa-db-fra.com

Échec des accusés de réception IPN de PayPal avec les routines SSL: SSL3_READ_BYTES: échec de la négociation de l'alerte sslv3

Sans aucun changement de notre part et peut-être lié à POODLE/SSL3, notre appel d'API Paypal à PPIPNMessage :: validate échoue maintenant.

SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure

Le paiement et la réception de l'IPN sont très bien (et nous n'avons jamais pris en charge SSL3 entrant), il échoue simplement lors de la reconnaissance de l'IPN (curieusement Paypal n'essaye pas à nouveau, même si nous avons échoué)

L'exécution de curl à partir de la même ligne de commande du serveur réussit

$ 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: */*

J'ai noté que ssllabs.com affiche 1 IP sur 4 prenant toujours en charge SSL3 sur ce point de terminaison.

21
ianhk

C'est le même problème que Erreur 0x1408F10B: "SSL3_GET_RECORD: numéro de version incorrect" avec le SDK Paypal

La version de l'API Paypal que nous utilisons les codes durs CURLOPT_SSLVERSION à 3.

Notre correctif est de l'insérer avant tout appel Paypal.

PPHttpConfig::$DEFAULT_CURL_OPTS[CURLOPT_SSLVERSION] = 4;
32
ianhk

J'ai eu le même problème ... Modifiez simplement la ligne suivante sur le ipnlistener.php

de:

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.");
        }
    }
}
?>
15
Pedro Lobito

Paypal a désactivé SSLv3 en réponse à la vulnérabilité de "POODLE". Lisez à ce sujet ici: Réponse Paypal

Si vous utilisez ipnlistener.php, forcez TLS 1.2 comme protocole SSL:

curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); 

(Mise à jour: TLS V1.2 requis pour 2017)

Remarque: Paypal met à jour les protocoles utilisés pour sécuriser toutes les connexions externes établies à leurs systèmes. Transport Layer Security version 1.2 (TLS 1.2) et Hypertext Transfer Protocol version 1.1 (HTTP/1.1) deviendront obligatoires pour la communication avec Paypal en 2018. Voir leur Security Roadmap

13
rareclass

SSLv3 n'est plus disponible pour 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

Vous devez changer votre CURLOPT_SSLVERSION en TLSv1:

curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);

(Cette constante "CURL_SSLVERSION_TLSv1" n'est pas disponible pour les anciennes versions PHP, donc une autre façon consiste à supprimer simplement l'application CURLOPT_SSLVERSION.)

2
heuri

Pour moi Aucune de ces choses mentionnées dans les autres réponses n'a fonctionné, mais j'ai pu comprendre le correctif et c'est dans le même sens, mais il m'a fallu un certain temps pour savoir quel fichier le sdk que j'utilisais mettait sa configuration curl comme là était pas de fichier d'écoute ipn et que "PPHttpConfig" me donnerait une erreur fatale

j'ai donc trouvé que c'est le fichier d'écoute qu'ils utilisent maintenant:

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

J'ai trouvé à l'intérieur

CURLOPT_SSLVERSION => 1

Je l'ai changé en:

CURLOPT_SSLVERSION => 4

et cela m'a arrangé.

1
Kit Ramos

Je suis d'accord. Perdu heures en essayant de le comprendre. Sur mon écouteur IPN, j'ai dû supprimer "force ssl v3". À partir de ce moment, mon IPN a recommencé à fonctionner.

Faites juste une boucle -v https://Paypal.com

Il montre: connexion SSL utilisant TLS_RSA_WITH_AES_256_CBC_SHA

0

J'ai la même erreur lors de la vérification de l'IPN avec Paypal. Voici les solutions du problème

J'ai travaillé avec PHP 5.3 et PHP 5.3 n'est plus compatible avec SSL version 3. J'ai mis à niveau avec PHP version avec 5.4 et ajouté sous la ligne de code. Cela fonctionne pour moi.

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');
0
Nikunj K.