Ich habe ein Stück Code (nennen wir es Code A), das in einem Framework arbeitet, und ich möchte es in einem anderen Framework zum Laufen bringen. Der funktionierende Code macht eine erfolgreiche POST-Anfrage mit CURL wie folgt (Anfrage mit aktiviertem CURLOPT_VERBOSE):
* Connected to android.clients.google.com (216.58.209.238) port 443 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /Applications/MAMP/Library/OpenSSL/cert.pem
CApath: none
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=US; ST=California; L=Mountain View; O=Google Inc; CN=*.google.com
* start date: Jan 18 19:17:59 2017 GMT
* expire date: Apr 12 18:51:00 2017 GMT
* subjectAltName: host "android.clients.google.com" matched cert's "android.clients.google.com"
* issuer: C=US; O=Google Inc; CN=Google Internet Authority G2
* SSL certificate verify ok.
> POST /auth HTTP/1.1
Host: android.clients.google.com
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 97
Das http-Framework hier (yii2/httpclient, um genau zu sein) hat zu viele Abhängigkeiten, um es in das andere Projekt zu bringen, also versuche ich, es auf niedriger Ebene wie folgt neu zu erstellen (nennen wir es Code B):
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'post-data-here');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://android.clients.google.com/auth');
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/x-www-form-urlencoded; charset=UTF-8"]); // just because I'm desperate
curl_setopt($ch, CURLOPT_VERBOSE, true);
$content = curl_exec($ch);
Ich erwarte, dass das das gleiche Ergebnis hat, aber das ist, was ich bekomme:
* Connected to android.clients.google.com (216.58.209.238) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.google.com
* Server certificate: Google Internet Authority G2
* Server certificate: GeoTrust Global CA
> POST /auth HTTP/1.1
Host: android.clients.google.com
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 97
* upload completely sent off: 97 out of 97 bytes
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: Mon, 01 Jan 1990 00:00:00 GMT
< Date: Fri, 27 Jan 2017 12:07:12 GMT
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Server: GSE
< Alt-Svc: clear
< Accept-Ranges: none
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
<HTML>
<HEAD>
<TITLE>HTTP Version Not Supported</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>HTTP Version Not Supported</H1>
<H2>Error 505</H2>
</BODY>
</HTML>
Und statt einer gültigen Antwort bekomme ich “Error 505: HTTP Version Not Supported”. Der einzige Unterschied, den ich sehe, ist, dass der Arbeitscode versucht, “ALPN, http/1.1 anzubieten”, während der letztere Code dies nicht tut. Und der Zertifikatsteil danach, aber er wird nie in Code A erwähnt, also bin ich mir nicht sicher, was er tut, um ihn bereitzustellen.
Beide Codeversionen laufen auf demselben Server, dieselbe Version von PHP (5.6) und CURL (7.51.0). Der Unterschied im ausführlichen Protokoll beginnt, BEVOR Daten gesendet werden, also denke ich, dass es nicht darum geht, dass Daten oder Header falsch eingestellt sind.
Was ich bisher versucht habe (mit wenig oder keiner Wirkung):
- curl_setopt($ch, CURLOPT_SSL_ENABLE_ALPN, was auch immer) – funktioniert nicht, weil CURLOPT_SSL_ENABLE_ALPN überhaupt nicht definiert ist (obwohl es in dieser Version von CURL sein muss, nicht sicher, was falsch ist)
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, was auch immer)
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, was auch immer)
- curl_setopt($ch, CURLOPT_SSLVERSION, was auch immer)
- einige andere verzweifelte dumme Sachen, für die ich mich sogar schäme, sie hier zu zeigen
Ich habe versucht, den Arbeitscode so tief wie möglich zu lernen, aber es scheint, dass er nichts über den einfachen HTTP-POST ausführt. Ich habe jeden curl_setopt verfolgt, den er macht, und es scheint, dass es nur diese curl_setopt gibt, die ich in meinem Code verwendet habe, nichts Besonderes. Trotzdem funktioniert es und mein Code nicht.
Ich habe versucht, dasselbe über die Befehlszeile zu machen:
$ curl https://android.clients.google.com/auth -v --http1.1 -X POST --no-alpn --no-npn --data "copypasted-post-data-from-code-B-and-yes-its-urlencoded"
Habe das richtige Ergebnis:
* Trying 216.58.209.238...
* TCP_NODELAY set
* Connected to android.clients.google.com (216.58.209.238) port 443 (#0)
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /Applications/MAMP/Library/OpenSSL/cert.pem
CApath: none
...
> POST /auth HTTP/1.1
> Host: android.clients.google.com
> User-Agent: curl/7.51.0
> Accept: */*
> Content-Length: 97
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 97 out of 97 bytes
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: Mon, 01 Jan 1990 00:00:00 GMT
< Date: Fri, 27 Jan 2017 13:22:27 GMT
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Server: GSE
< Alt-Svc: clear
< Accept-Ranges: none
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
<
SID=BAD_COOKIE
LSID=BAD_COOKIE
Auth=here-s-the-data-i-need
Wie ich im vorherigen Abschnitt sehen kann, wird ein SSL-Zertifikat bereitgestellt, aber Sie haben kein Zertifikat bereitgestellt.
– MA SIDDIQUI
27. Januar 2017 um 12:55 Uhr
Nun ja, das auch, aber ich gebe diesen Zertifikatspfad auch nicht in Code A an. Ich bin mir nicht sicher, was es tut, um es bereitzustellen, und wie ich das wiederhole. Meine Vermutung ist CURLOPT_CAINFO, CURLOPT_CAPATH (hat keine Auswirkung und wird nirgendwo im Arbeitscode verwendet)
– Andrej
27. Januar 2017 um 13:05 Uhr
Das SSL-Zertifikat ist eine Art Signatur, um eine sichere Verbindung über das TCP-Protokoll herzustellen. Dies ist jedoch nicht obligatorisch, bis der Dienstanbieter eine unsichere Verbindung ablehnt. Ich frage nur, ob Sie ein Problem mit der Antwort haben, nicht mit den Antwortheadern
– MA SIDDIQUI
27. Januar 2017 um 13:08 Uhr
Die Antwort, die ich mit Code B bekomme, ist
HTTP-Version nicht unterstützt
Fehler 505
das ist das Problem 🙂 Code A liefert die gewünschte Antwort, keinerlei Fehler– Andrej
27. Januar 2017 um 13:11 Uhr
definiere es einfach manuell dann `
if(!defined('CURLOPT_SSL_ENABLE_ALPN')){define('CURLOPT_SSL_ENABLE_ALPN',226);}
und stellen Sie sicher, dass curl_setopt(_array?) bool(true) zurückgibt– Hansenrik
28. Januar 2017 um 13:17 Uhr