Aller au contenu

Tous les billets

curl ne voit pas CORS

J’ai vérifié une mise en production avec curl. Elle répondait 200. Dans un navigateur, rien ne fonctionnait.

vérificationcorsméthode

J’avais déployé une application sur une nouvelle origine. Vérification d’usage :

curl -s -o /dev/null -w "%{http_code}" https://api.exemple.com/api/login
200

Deux cents. L’API répond, la route existe, le certificat est bon. J’ai validé et je suis passé à la suite.

Une heure plus tard : « je n’arrive pas à me connecter ».

Ce que curl répondait vraiment

CORS n’est pas une protection du serveur. C’est une règle appliquée par le navigateur, à partir d’un en-tête que le serveur renvoie. Aucun blocage ne se produit côté serveur : la réponse part, complète, avec son code 200. C’est le navigateur qui la reçoit, constate qu’aucun Access-Control-Allow-Origin ne l’autorise, et refuse de la remettre au JavaScript qui l’a demandée.

curl n’est pas un navigateur. Il n’applique aucune politique d’origine. Il ne pouvait donc pas voir le problème — et il ne mentait pas : il répondait honnêtement à une question que je ne lui posais pas.

Je croyais demander « est-ce que ça marche ? ». Je demandais « est-ce que le serveur répond ? ».

La correction

Ajouter l’origine dans la configuration CORS a pris deux minutes. Ce qui en valait la peine, c’est le test :

public function test_l_origine_du_coffre_est_autorisee(): void
{
    $reponse = $this->withHeaders(['Origin' => 'https://coffre.exemple.com'])
        ->options('/api/login');

    $reponse->assertHeader('Access-Control-Allow-Origin', 'https://coffre.exemple.com');
}

Et une deuxième règle, moins évidente : refuser toute origine contenant *. La bibliothèque que j’utilise promeut silencieusement une telle entrée en expression régulière. https://*.exemple.com n’autorise pas ce que vous croyez.

Ce que j’en retiens

Chaque outil de vérification a un angle mort, et l’angle mort n’est jamais annoncé. curl ignore CORS. Les tests unitaires ignorent la politique de sécurité de contenu et l’hydratation. toBeVisible() ignore qu’un élément déborde de l’écran. Un 401 simulé masque une garde cliente qu’on croyait tester.

La question utile n’est pas « est-ce que mon test passe ». C’est : qu’est-ce que cet outil est structurellement incapable de voir ?