web-dev-qa-db-fra.com

En-têtes CORS manquants en réponse de PHP/Apache2

Un projet Zend Expressive sur lequel travaille mon entreprise est prêt à être expédié, mais dans notre environnement de transfert, nous manquons d'en-têtes de réponse pour une demande de pré-vol auprès de la base de données CORS. Cela ne se produit pas dans notre environnement de développement. Nous utilisons CorsMiddleware dans notre pipeline, mais il ne semble pas que le middleware soit le coupable.

Le problème

Pendant l'exécution, le middleware détecte les demandes de pré-vol entrantes et répondra avec une réponse comme ceci:

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

Eh bien, cela ne fonctionne que sur nos serveurs de développement et les serveurs Web intégrés de php. La réponse est différente de notre serveur de transfert, même si la demande est exactement la même, à l'exception de l'hôte:

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

Ce que nous avons essayé

Enquête sur le middleware

Nous avons vérifié que CorsMiddleware fonctionne parfaitement et définit les en-têtes requis. Lorsque nous modifions le code de réponse de CorsMiddleware et le définissons sur 202 au lieu de 200, nous do obtenons maintenant les en-têtes que nous recherchons. Si vous remettez le code de réponse en 200, les en-têtes disparaissent à nouveau.

Définir les en-têtes manuellement

En utilisant l'exemple suivant:

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

Cela a le même comportement jusqu'à ce que nous modifions le code de réponse en 204 ou tout autre chose que 200.

Regarder le corps

Le corps de la réponse est vide et ne doit contenir aucun élément, mais lorsque nous ajoutons du contenu au corps de la réponse, les en-têtes apparaissent comme si rien n'était faux.

Donc, si j'ajoute du contenu corporel, les en-têtes sont présents. Pas de contenu corporel? Pas d'en-tête CORS. Est-ce une configuration dans Apache? Est-ce qu'il me manque une configuration en PHP? Est-ce que j'oublie quelque chose?

Plus de détails

Toutes les demandes ont été testées avec httpie, Postman, curl et le client http de PhpStorm.

Voici l'exemple httpie:

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

Voici l'exemple de curl:

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"

Configuration Cors dans pipeline.php (caractère générique uniquement pour les tests):

$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));
    },
]);

L'environnement de rassemblement:

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 sur la mise en scène:

<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 sur le développement:

<VirtualHost *:443>
        ServerName      php71.****.com
        ServerAdmin     dev@****.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>

À tout le monde qui pointe du doigt Cloudflare:

Essayez ce lien direct avec httpie. Ce lien n'utilise pas 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'

Vérifiez le code source dans votre navigateur: http://37.97.135.33/cors.php?source=1

11
halfpastfour.am

D'après tout ce que j'ai lu ici, y compris vos commentaires, il semble que votre serveur "de production" est derrière un PROXY plus exactement CloudFlare. Vous avez fourni des détails sur votre environnement de développement actif, mais rien sur l'environnement de production non opérationnel.

Votre configuration semble correcte, et si cela fonctionne sur une configuration de développement sans un PROXY, cela signifie que le PROXY est en train de modifier les en-têtes.

Une recherche rapide à ce sujet concernant CloudFlare a donné suffisamment d'indications sur le fait que CloudFlare peut être la cause de votre problème.

Je vous suggère fortement d'activer le "mode de développement" dans CloudFlare afin qu'il contourne le cache et que vous puissiez voir tout ce qui va/vient sur le serveur Origin.

L'article suivant devrait vous aider à comprendre et à résoudre votre problème:

https://support.cloudflare.com/hc/en-us/articles/203063414-Why-can-t-I-see-my-CORS-headers-

METTRE À JOUR:

Il semble que votre problème vienne d'Apache Mod Pagespeed. En le désactivant, vos en-têtes sont présents à tout moment.

On ne sait toujours pas pourquoi le mod enlève vos en-têtes, mais c'est pour une autre question et une autre fois.

2
Norbert Boros

Votre configuration indique clairement que les en-têtes sont générés. Ce n'est donc pas le code ni le middleware.

Je crois que les en-têtes sont supprimés par quelque chose - vérifiez le mod_headers et la configuration d’Apache au cas où il y aurait une directive unset non autorisée.

Une autre possibilité moins probable est que vous examiniez le serveur intermédiaire via un équilibreur de charge ou un proxy quelconque, qui réécrit les en-têtes et laisse le CORS sorti (pour vérifier cela, vous devrez peut-être intercepter le trafic sortant d'Apache).

J'ai fait les deux erreurs, moi-même.

1
LSerni

Assurez-vous d'avoir la bonne configuration dans Zend Expressive. Par exemple, le code ci-dessous permettra à CORS d'accéder à tout domaine appelant.

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