wake-up-neo.com

Fehlende CORS-Header als Antwort von PHP/Apache2

Ein Zend Expressive-Projekt, an dem mein Unternehmen arbeitet, kann bereits ausgeliefert werden. In unserer Staging-Umgebung scheinen jedoch die Antwortheader für eine CORS-Vorabanfrage zu fehlen. Dies geschieht nicht in unserer Entwicklungsumgebung. Wir verwenden CorsMiddleware in unserer Pipeline, aber es sieht nicht so aus, als ob Middleware der Täter ist.

Das Problem

Zur Laufzeit erkennt die Middleware eingehende Anfragen vor dem Flug und antwortet mit einer Antwort wie folgt:

HTTP/1.1 200 OK
Date: Mon, 20 Aug 2018 15:09:03 GMT
Server: Apache
X-Powered-By: PHP/7.1.19
Access-Control-Allow-Origin: https://example.com
Vary: Origin
Access-Control-Allow-Headers: content-type
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

Nun, das funktioniert nur auf unseren Entwicklungsservern und den integrierten Webservern von php. Die Antwort unterscheidet sich von unserem Staging-Server, auch wenn die Anforderung abgesehen vom Host genau dieselbe ist:

HTTP/1.1 200 OK
Date: Mon, 20 Aug 2018 15:11:29 GMT
Server: Apache
Keep-Alive: timeout=5, max=100
Cache-Control: max-age=0, no-cache
Content-Length: 0
Content-Type: text/html; charset=UTF-8

Was wir versucht haben

Untersuchung der Middleware

Wir haben festgestellt, dass CorsMiddleware einwandfrei läuft und die erforderlichen Header tatsächlich setzt. Wenn wir den Antwortcode von CorsMiddleware ändern und ihn auf 202 anstelle von 200 setzen, erhalten wir do die Header, nach denen wir suchen. Wenn Sie den Antwortcode wieder in 200 ändern, werden die Kopfzeilen wieder ausgeblendet.

Die Header manuell einstellen

Verwenden Sie das folgende Beispiel:

header('Access-Control-Allow-Origin: https://example.com');
header('Access-Control-Allow-Headers: content-type');
header('Vary: Origin');
exit(0);

Dies hat das gleiche Verhalten, bis wir den Antwortcode in 204 oder in etwas anderes als 200 ändern.

Blick auf den Körper

Der Antworttext ist leer und sollte nichts enthalten, aber wenn wir dem Antworttext Inhalt hinzufügen, werden die Header so angezeigt, als sei nichts falsch.

Wenn ich also Körperinhalt hinzufüge, sind die Header vorhanden. Kein Körperinhalt? Keine CORS-Header. Ist das eine Einstellung in Apache? Fehlt mir etwas Konfiguration in PHP? Vergesse ich etwas?

Weitere Details

Alle Anfragen wurden mit httpie, Postman, curl und dem http-Client von PhpStorm getestet.

Hier ist das httpie-Beispiel:

http -v OPTIONS https://staging.****.com \
    'access-control-request-method:POST' \
    'Origin:https://example.com' \
    'access-control-request-headers:content-type'

Hier ist das Curl-Beispiel:

curl "https://staging.****.com" \
--request OPTIONS \
--include \
--header "access-control-request-method: POST" \
--header "Origin: https://example.com" \
--header "access-control-request-headers: content-type"

Cors Konfiguration in pipeline.php (Platzhalter nur zum Testen):

$app->pipe(new CorsMiddleware([
    "Origin"         => [
        "*",
    ],
    "headers.allow"  => ['Content-Type'],
    "headers.expose" => [],
    "credentials"    => false,
    "cache"          => 0,

    // Get list of allowed methods from matched route or provide empty array.
    'methods' => function (ServerRequestInterface $request) {
        $result = $request->getAttribute(RouteResult::class);
        /** @var \Zend\Expressive\Router\Route $route */
        $route = $result->getMatchedRoute();

        return $route ? $route->getAllowedMethods() : [];
    },

    // Respond with a json response containing the error message when the CORS check fails.
    'error'   => function (
        ServerRequest $request,
        Response $response,
        $arguments
    ) {
        $data['status']  = 'error';
        $data['message'] = $arguments['message'];

        return $response->withHeader('Content-Type', 'application/json')
                        ->getBody()->write(json_encode($data));
    },
]);

Die Inszenierungsumgebung:

OS: Debian 9.5 server
Webserver: Apache/2.4.25 (Debian) (built: 2018-06-02T08:01:13)
PHP: PHP 7.1.20-1+0~20180725103315.2+stretch~1.gbpd5b650 (cli) (built: Jul 25 2018 10:33:20) ( NTS )

Apache2 vhost zum Staging:

<IfModule mod_ssl.c>
<VirtualHost ****:443>
        ServerName staging.****.com
        DocumentRoot /var/www/com.****.staging/public

        ErrorLog /var/log/Apache2/com.****.staging.error.log
        CustomLog /var/log/Apache2/com.****.staging.access.log combined
        <Directory /var/www/com.****.staging>
                Options +SymLinksIfOwnerMatch
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>
SSLCertificateFile /etc/letsencrypt/live/staging.****.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/staging.****.com/privkey.pem
Include /etc/letsencrypt/options-ssl-Apache.conf
</VirtualHost>
</IfModule>

Apache2 vhost zur Entwicklung:

<VirtualHost *:443>
        ServerName      php71.****.com
        ServerAdmin     [email protected]****.com
        DocumentRoot    /var/www/

        <Directory /var/www/>
                Options Indexes FollowSymlinks
                AllowOverride All
                Require all granted
        </Directory>

        ErrorLog        ${Apache_LOG_DIR}/error.ssl.log
        CustomLog       ${Apache_LOG_DIR}/access.ssl.log combined

        SSLEngine On
        SSLCertificateFile /etc/ssl/certs/****.crt
        SSLCertificateKeyFile /etc/ssl/certs/****.key
</VirtualHost>

An alle, die mit dem Finger auf Cloudflare zeigen:

Versuchen Sie diese direkte Verbindung mit httpie. Dieser Link verwendet kein Cloudflare:

http -v OPTIONS http://37.97.135.33/cors.php \
    'access-control-request-method:POST' \
    'Origin:https://example.com' \
    'access-control-request-headers:content-type'

Überprüfen Sie den Quellcode in Ihrem Browser: http://37.97.135.33/cors.php?source=1

11
halfpastfour.am

Nach allem, was ich hier lese, einschließlich Ihrer Kommentare, scheint es, als sei Ihr "Produktions" -Server hinter einem PROXY-Prozessor, genauer gesagt aus CloudFlare. Sie haben Details zu Ihrem Arbeitsumfeld angegeben, aber nichts zur nicht-funktionierenden Produktionsumgebung.

Ihr Setup scheint korrekt zu sein, und wenn es bei einem Entwicklungssetup ohne PROXY funktioniert, bedeutet dies, dass der PROXY die Header ändert.

Eine schnelle Suche in Bezug auf CloudFlare hat genügend Hinweise darauf gegeben, dass CloudFlare die Ursache Ihres Problems sein kann.

Ich empfehle Ihnen dringend, den "Entwicklungsmodus" in CloudFlare zu aktivieren, damit der Cache umgangen wird und Sie alles auf dem Origin-Server sehen können.

Der folgende Artikel soll Ihnen helfen, Ihr Problem zu verstehen und zu beheben:

https://support.cloudflare.com/hc/de-de/articles/203063414-Warum-kann-t-I-see-my-CORS-headers-

UPDATE:

Es scheint, dass Ihr Problem von Apache Mod Pagespeed stammt. Wenn Sie es deaktivieren, sind die Kopfzeilen immer präsent.

Es ist immer noch unklar, warum der Mod die Kopfzeilen entfernt, aber das ist eine andere Frage und Zeit.

2
Norbert Boros

Ihre Konfiguration macht deutlich, dass die Header generiert werden, also nicht der Code oder der Fehler der Middleware.

Ich glaube, dass die Header durch etwas entfernt werden durch etwas - den Apache mod_headers und die Konfiguration überprüfen, falls es eine unberechtigte unset-Direktive gibt.

Eine andere, weniger wahrscheinliche Möglichkeit besteht darin, dass Sie den Staging-Server über einen Load Balancer oder einen Proxy in irgendeiner Form betrachten, der die Header neu schreibt und den CORS ausblendet (um dies zu überprüfen, müssen Sie möglicherweise den ausgehenden Datenverkehr von Apache abfangen).

Ich habe selbst beide Fehler gemacht.

1
LSerni

Bitte stellen Sie sicher, dass Sie die richtige Konfiguration in Zend Expressive haben. Der folgende Code ermöglicht beispielsweise den Zugriff von CORS auf eine aufrufende Domäne

use Psr\Http\Message\ServerRequestInterface;
use Tuupola\Middleware\CorsMiddleware;
use Zend\Expressive\Router\RouteResult;

$app->pipe(new CorsMiddleware([
    "Origin" => ["*"],
    "methods" => ["GET", "POST", "PUT", "PATCH", "DELETE"]
}
]));
0
Rinsad Ahmed