file_get_contents (): l'opération SSL a échoué avec le code 1, impossible d'activer la crypto

198

J'ai essayé d'accéder à ce service REST particulier à partir d'une page PHP que j'ai créée sur notre serveur. J'ai réduit le problème à ces deux lignes. Donc ma page PHP ressemble à ceci:

<?php
$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json");

echo $response; ?>

La page meurt sur la ligne 2 avec les erreurs suivantes:

  • Avertissement: file_get_contents (): l'opération SSL a échoué avec le code 1. Messages d'erreur OpenSSL: erreur: 14090086: Routines SSL: SSL3_GET_SERVER_CERTIFICATE: la vérification du certificat a échoué dans ... php sur la ligne 2
    • Attention: file_get_contents (): Impossible d'activer la crypto dans ... php sur la ligne 2
    • Attention: file_get_contents ( https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json): échec de l'ouverture du flux: échec de l'opération dans ... php sur la ligne 2

Nous utilisons un serveur Gentoo. Nous avons récemment mis à niveau vers la version 5.6 de PHP. C'est après la mise à jour que ce problème est apparu.

J'ai trouvé quand je remplace le service REST par une adresse comme https://www.google.com; ma page fonctionne très bien.

Dans une tentative précédente, j'ai défini “verify_peer”=>falseet transmis cela en argument à file_get_contents, comme décrit ici: file_get_contents ignorant verify_peer => false? Mais comme l'écrivain l'a noté; cela n'a fait aucune différence.

J'ai demandé à l'un de nos administrateurs de serveur si ces lignes dans notre fichier php.ini existent:

  • extension = php_openssl.dll
  • allow_url_fopen = Activé

Il m'a dit que puisque nous sommes sur Gentoo, openssl est compilé lorsque nous construisons; et il n'est pas défini dans le fichier php.ini.

J'ai également confirmé que cela allow_url_fopenfonctionnait. En raison de la nature spécialisée de ce problème; Je ne trouve pas beaucoup d'informations pour m'aider. L'un de vous a-t-il rencontré quelque chose comme ça? Merci.

Joe
la source
Si vous utilisez Kaspersky, vérifiez ceci: stackoverflow.com/a/54791481/3549317
cespon
J'ai constaté que je n'obtenais cette erreur que lors du grattage à partir du même site Web (pas localement), c'est-à-dire: le grattage d'un site Web différent sur lequel le script est exécuté fonctionnait parfaitement.
WilliamK le

Réponses:

358

C'était un lien extrêmement utile à trouver:

http://php.net/manual/en/migration56.openssl.php

Un document officiel décrivant les modifications apportées à l'ouverture de ssl en PHP 5.6 D'ici, j'ai appris un autre paramètre que j'aurais dû mettre à false: "verify_peer_name" => false

Remarque: cela a des implications de sécurité très importantes. La désactivation de la vérification permet potentiellement à un attaquant MITM d'utiliser un certificat non valide pour écouter les requêtes. S'il peut être utile de le faire dans le développement local, d'autres approches devraient être utilisées dans la production.

Donc, mon code de travail ressemble à ceci:

<?php
$arrContextOptions=array(
    "ssl"=>array(
        "verify_peer"=>false,
        "verify_peer_name"=>false,
    ),
);  

$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json", false, stream_context_create($arrContextOptions));

echo $response; ?>
Joe
la source
131
cela brise la certification SSL et est une faille de sécurité
hypery2k
8
Comme @ hypery2k l'a souligné, vous ne devriez pas simplement désactiver la vérification. Voir ma réponse pour une solution alternative qui ne va pas à l'encontre de l'objectif d'utiliser ssl.
elitechief21
4
Cela ne devrait vraiment pas être fait. Plutôt que de contourner le problème, vous devriez le résoudre. Découvrez la meilleure solution sur @ elitechief21.
Jasper
6
Cela peut être frustrant si vous testez à partir d'un environnement local avec une API. Dans ce cas, je détruis normalement la vérification.
HappyCoder
14
Puisque je viens toujours ici quand j'essaye de faire ça, voici le code pour copier et coller facilement:file_get_contents($url, false, stream_context_create(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false))));
laurent
158

Vous ne devriez pas simplement désactiver la vérification. Vous devriez plutôt télécharger un bundle de certificats, peut-être que le bundle curl fera l'affaire?

Ensuite, il vous suffit de le mettre sur votre serveur Web, en donnant à l'utilisateur qui exécute php l'autorisation de lire le fichier. Ensuite, ce code devrait fonctionner pour vous:

$arrContextOptions=array(
    "ssl"=>array(
        "cafile" => "/path/to/bundle/cacert.pem",
        "verify_peer"=> true,
        "verify_peer_name"=> true,
    ),
);

$response = file_get_contents("https://maps.co.weber.ut.us/arcgis/rest/services/SDE_composite_locator/GeocodeServer/findAddressCandidates?Street=&SingleLine=3042+N+1050+W&outFields=*&outSR=102100&searchExtent=&f=json", false, stream_context_create($arrContextOptions));

Espérons que le certificat racine du site auquel vous essayez d'accéder se trouve dans le bundle curl. Si ce n'est pas le cas, cela ne fonctionnera toujours pas tant que vous n'aurez pas obtenu le certificat racine du site et l'avez mis dans votre fichier de certificat.

elitechief21
la source
D'où vient le fichier .crt? Il n'y a pas de lien dans le site Web curl que vous fournissez, il n'a que .pem.
vee
1
Le fichier pem doit être le même. Au moment où cela a été publié, ils avaient deux versions du paquet de certificats sur leur site, un pem et un crt et je viens d'utiliser le fichier crt pour mon exemple (naturellement, c'est celui qu'ils supprimeraient). Pour référence ultérieure, les fichiers crt sont généralement des fichiers pem renommés, qui sont renommés afin que Windows reconnaisse le fichier en tant que fichier de certificat. Ce ne sera pas toujours le cas, mais c'est dans ce cas. Je vais mettre à jour mon exemple pour qu'il utilise le fichier pem actuellement sur le site curl
elitechief21
6
il y a aussi une fonction utile stream_context_set_defaultqui peut être utilisée pour que vous n'ayez pas à la passer dans file_get_contents à chaque fois
Ken Koch
1
Est-ce que quelqu'un a déjà réussi à faire fonctionner cela? J'ai essayé avec le bundle cacert.pem lié à cette réponse, ainsi qu'avec le certificat Comodo CA Root (qui est la racine CA pour le certificat que je souhaite faire vérifier), mais cela échouera toujours.
zmippie
1
Du point de vue de la sécurité: à côté de l'utilisation de l'option de contexte de flux cafile, il pourrait être très important de définir également les chiffrements autorisés dans l'option de contexte de flux de chiffrements et d'interdire les versions SSL qui sont connues comme vulnérables. Il est également recommandé de définir l'option de contexte de flux disable_compression sur true pour atténuer le vecteur d'attaque CRIME.
Josef Glatz
37

J'ai corrigé cela en m'assurant que OpenSSL était installé sur ma machine, puis en l'ajoutant à mon php.ini:

openssl.cafile=/usr/local/etc/openssl/cert.pem
Andlin
la source
@Akhi Vous devriez pouvoir trouver des tutoriels si vous le recherchez sur Google
andlin
2
J'ai utilisé la même méthode en utilisant PHP 7 sur IIS, téléchargé le cert.pemfichier et défini php.inicomme ceci, et cela a fonctionné:openssl.cafile=D:\Tools\GnuWin32\bin\cacert.pem
David Refoua
1
J'ai téléchargé le fichier PEM à partir de curl.haxx.se/docs/caextract.html - j'ai corrigé le problème pour moi sous Windows avec une URL gstatic.com particulière.
Jake
Le téléchargement du fichier PEM à partir de curl.haxx.se/docs/caextract.html n'a pas fonctionné pour moi sur Centos 7. J'ai créé un certificat de bundle en concaténant le certificat principal et pkcs7 et le placez sur le serveur puis spécifiez le chemin openssl.cafile. +1 pour la bonne réponse et la bonne direction.
Arvind K.
Lors de la création d'un bundle, n'oubliez pas de convertir les pkcs en fichier pem avant de le concéder à la certification principale
Arvind K.
23

Vous pouvez contourner ce problème en écrivant une fonction personnalisée qui utilise curl, comme dans:

function file_get_contents_curl( $url ) {

  $ch = curl_init();

  curl_setopt( $ch, CURLOPT_AUTOREFERER, TRUE );
  curl_setopt( $ch, CURLOPT_HEADER, 0 );
  curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
  curl_setopt( $ch, CURLOPT_URL, $url );
  curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, TRUE );

  $data = curl_exec( $ch );
  curl_close( $ch );

  return $data;

}

Ensuite, utilisez simplement file_get_contents_curlau lieu de file_get_contentschaque fois que vous appelez une URL commençant par https.

Ben Shoval
la source
15

Pour moi, j'utilise PHP 5.6. L'extension openssl doit être activée et lors de l'appel de google map api verify_peer make false Le code ci-dessous fonctionne pour moi.

<?php
$arrContextOptions=array(
    "ssl"=>array(
         "verify_peer"=>false,
         "verify_peer_name"=>false,
    ),
);  
$url = "https://maps.googleapis.com/maps/api/geocode/json?latlng="
      . $latitude
      . ","
      . $longitude
      . "&sensor=false&key="
      . Yii::$app->params['GOOGLE_API_KEY'];

$data = file_get_contents($url, false, stream_context_create($arrContextOptions));

echo $data;
?>
Dipti
la source
10

Si votre version PHP est 5, essayez d'installer cURL en tapant la commande suivante dans le terminal:

sudo apt-get install php5-curl
ARUNBALAN NV
la source
9
Cela n'a absolument rien à voir avec cURL.
Andreas
2
L'installation de php curl devrait être la bonne réponse! Pour mon système Mac, j'utilise le port, et la commande est:sudo port install php70-curl
hailong
6

les étapes suivantes résoudront ce problème,

  1. Téléchargez le certificat CA à partir de ce lien: https://curl.haxx.se/ca/cacert.pem
  2. Trouvez et ouvrez php.ini
  3. Recherchez curl.cainfoet collez le chemin absolu où vous avez téléchargé le certificat.curl.cainfo ="C:\wamp\htdocs\cert\cacert.pem"
  4. Redémarrez WAMP / XAMPP (serveur Apache).
  5. Ça marche!

J'espère que ça t'as aidé !!

shakee93
la source
est-ce sûr? J'adore les solutions faciles mais il
me
pour le test est ok
Geomorillo
5

Je voulais juste ajouter à cela car j'ai rencontré le même problème et rien de ce que je pourrais trouver n'importe où ne fonctionnerait (par exemple, télécharger le fichier cacert.pem, configurer cafile dans php.ini, etc.)

Si vous utilisez NGINX et que votre certificat SSL est livré avec un "certificat intermédiaire", vous devez combiner le fichier de certificat intermédiaire avec votre fichier principal "mydomain.com.crt" et cela devrait fonctionner. Apache a un paramètre spécifique pour les certificats intermédiaires, mais NGINX ne l'a pas donc il doit être dans le même fichier que votre certificat régulier.

SlickTheNick
la source
4

La raison de cette erreur est que PHP n'a pas de liste d'autorités de certification de confiance.

PHP 5.6 et les versions ultérieures essaient de charger automatiquement les autorités de certification approuvées par le système. Les problèmes peuvent être résolus. Voir http://php.net/manual/en/migration56.openssl.php pour plus d'informations.

PHP 5.5 et les versions antérieures sont vraiment difficiles à configurer correctement car vous devez spécifier manuellement le bundle CA dans chaque contexte de requête, une chose que vous ne voulez pas saupoudrer autour de votre code. J'ai donc décidé pour mon code que pour les versions PHP <5.6, la vérification SSL est simplement désactivée:

$req = new HTTP_Request2($url);
if (version_compare(PHP_VERSION, '5.6.0', '<')) {
    //correct ssl validation on php 5.5 is a pain, so disable
    $req->setConfig('ssl_verify_host', false);
    $req->setConfig('ssl_verify_peer', false);
}
cweiske
la source
Cela m'a aidé à trouver mon problème. Même si je suis sur PHP 5.6, j'utilisais une bibliothèque cliente api obsolète qui spécifiait manuellement un ancien fichier CA en utilisant l'option de contexte cafile selon votre lien ci-dessus. La suppression de cela de la bibliothèque cliente de l'API l'a corrigé pour moi. Vraisemblablement PHP a commencé à utiliser le bundle de confiance OpenSSL
Joe Lipson
4

Eu la même erreur avec PHP 7 sur XAMPP et OSX.

La réponse mentionnée ci-dessus dans https://stackoverflow.com/ est bonne, mais elle n'a pas complètement résolu le problème pour moi. J'ai dû fournir la chaîne de certificats complète pour que file_get_contents () fonctionne à nouveau. Voilà comment je l'ai fait:

Obtenir le certificat racine / intermédiaire

Tout d'abord, je devais comprendre quelle était la racine et le certificat intermédiaire.

Le moyen le plus pratique est peut-être un outil de certification en ligne comme le ssl-shopper

Là, j'ai trouvé trois certificats, un certificat de serveur et deux certificats de chaîne (l'un est la racine, l'autre apparemment l'intermédiaire).

Tout ce que j'ai à faire est de rechercher les deux sur Internet. Dans mon cas, c'est la racine:

thawte DV SSL SHA256 CA

Et cela mène à son url thawte.com . J'ai donc simplement mis ce certificat dans un fichier texte et j'ai fait de même pour l'intermédiaire. Terminé.

Obtenez le certificat d'hôte

La prochaine chose que je devais faire est de télécharger mon certificat de serveur. Sur Linux ou OS X, cela peut être fait avec openssl:

openssl s_client -showcerts -connect whatsyoururl.de:443 </dev/null 2>/dev/null|openssl x509 -outform PEM > /tmp/whatsyoururl.de.cert

Maintenant rassemblez-les tous

Maintenant, fusionnez-les tous dans un seul fichier. (Peut-être qu'il est bon de les mettre dans un dossier, je les ai juste fusionnés en un seul fichier) Vous pouvez le faire comme ceci:

cat /tmp/thawteRoot.crt > /tmp/chain.crt
cat /tmp/thawteIntermediate.crt >> /tmp/chain.crt
cat /tmp/tmp/whatsyoururl.de.cert >> /tmp/chain.crt

dire à PHP où trouver la chaîne

Il y a cette fonction pratique openssl_get_cert_locations () qui vous dira où PHP recherche les fichiers cert. Et il y a ce paramètre, qui dira à file_get_contents () où chercher les fichiers cert. Peut-être que les deux méthodes fonctionneront. J'ai préféré la méthode des paramètres. (Par rapport à la solution mentionnée ci-dessus).

Donc c'est maintenant mon code PHP

$arrContextOptions=array(
    "ssl"=>array(
        "cafile" => "/Applications/XAMPP/xamppfiles/share/openssl/certs/chain.pem",
        "verify_peer"=> true,
        "verify_peer_name"=> true,
    ),
);

$response = file_get_contents($myHttpsURL, 0, stream_context_create($arrContextOptions));

C'est tout. file_get_contents () fonctionne à nouveau. Sans CURL et espérons-le sans failles de sécurité.

nr
la source
Quel est le fichier chain.pem? C'est ça chain.crt?
Dr.X
Ce n'est pas le chain.crt, c'est pour le certificat réel. C'est la liste des certificats intermédiaires, aka chaîne de certificats. Vous n'en avez pas nécessairement besoin. Utilisez un vérificateur de certificats SSL pour savoir si vous en avez besoin. Si tel est le cas, vous pouvez rechercher le nom de votre émetteur de certificat + le terme «chaîne» ou «intermédiaire» pour trouver le fichier correct.
nr
4

Après avoir été victime de ce problème sur centOS après la mise à jour de php vers php5.6, j'ai trouvé une solution qui fonctionnait pour moi.

Obtenez le répertoire correct pour vos certificats à placer par défaut avec ceci

php -r 'print_r(openssl_get_cert_locations()["default_cert_file"]);'

Ensuite, utilisez ceci pour obtenir le certificat et le mettre dans l'emplacement par défaut trouvé à partir du code ci-dessus

wget http://curl.haxx.se/ca/cacert.pem -O <default location>
Edward DiGirolamo
la source
3

J'ai eu le même problème ssl sur ma machine de développement (php 7, xampp sous Windows) avec un certificat auto-signé essayant d'ouvrir un fichier " https: // localhost / ...". De toute évidence, l'assembly du certificat racine (cacert.pem) ne fonctionnait pas. Je viens de copier manuellement le code du fichier apache server.crt-File dans le cacert.pem téléchargé et j'ai fait l'entrée openssl.cafile = path / to / cacert.pem dans php.ini

cintre
la source
2

Une autre chose à essayer est de réinstaller ca-certificatescomme détaillé ici .

# yum reinstall ca-certificates
...
# update-ca-trust force-enable 
# update-ca-trust extract

Et une autre chose à essayer est d'autoriser explicitement le certificat du site en question comme décrit ici (surtout si le site est votre propre serveur et que vous avez déjà le .pem à portée de main).

# cp /your/site.pem /etc/pki/ca-trust/source/anchors/
# update-ca-trust extract

Je tombais sur cette erreur SO exacte après la mise à niveau vers PHP 5.6 sur CentOS 6 en essayant d'accéder au serveur lui-même qui a un certificat de sécurité à bas prix qu'il fallait peut-être mettre à jour, mais au lieu de cela, j'ai installé un certificat letsencrypt et avec ces deux étapes ci-dessus, il l'a fait l'astuce. Je ne sais pas pourquoi la deuxième étape était nécessaire.


Commandes utiles

Voir la version openssl:

# openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

Afficher les paramètres actuels de PHP cli ssl:

# php -i | grep ssl
openssl
Openssl default config => /etc/pki/tls/openssl.cnf
openssl.cafile => no value => no value
openssl.capath => no value => no value
site
la source
1

Concernant les erreurs similaires à

[11-mai-2017 19:19:13 Amérique / Chicago] Avertissement PHP: file_get_contents (): l'opération SSL a échoué avec le code 1. Messages d'erreur OpenSSL: erreur: 14090086: Routines SSL: ssl3_get_server_certificate: échec de la vérification du certificat

Avez-vous vérifié les autorisations du certificat et des répertoires référencés par openssl?

Tu peux le faire

var_dump(openssl_get_cert_locations());

Pour obtenir quelque chose de similaire

array(8) {
  ["default_cert_file"]=>
  string(21) "/usr/lib/ssl/cert.pem"
  ["default_cert_file_env"]=>
  string(13) "SSL_CERT_FILE"
  ["default_cert_dir"]=>
  string(18) "/usr/lib/ssl/certs"
  ["default_cert_dir_env"]=>
  string(12) "SSL_CERT_DIR"
  ["default_private_dir"]=>
  string(20) "/usr/lib/ssl/private"
  ["default_default_cert_area"]=>
  string(12) "/usr/lib/ssl"
  ["ini_cafile"]=>
  string(0) ""
  ["ini_capath"]=>
  string(0) ""
}

Ce problème m'a frustré pendant un certain temps, jusqu'à ce que je réalise que mon dossier «certs» avait 700 autorisations, alors qu'il aurait dû en avoir 755. N'oubliez pas que ce n'est pas le dossier des clés mais des certificats. Je recommande de lire ce lien sur les autorisations SSL.

Une fois que j'ai fait

chmod 755 certs

Le problème a été résolu, du moins pour moi en tout cas.

Aleks
la source
Yay! CHMOD était aussi mon problème ;-) Merci
RA.
0

J'ai eu le même problème pour une autre page sécurisée lors de l'utilisation de wgetou file_get_contents. De nombreuses recherches (y compris certaines des réponses à cette question) ont abouti à une solution simple - installer Curl et PHP-Curl - Si j'ai bien compris, Curl a la racine CA pour Comodo qui a résolu le problème

Installez Curl et l'addon PHP-Curl, puis redémarrez Apache

sudo apt-get install curl
sudo apt-get install php-curl
sudo /etc/init.d/apache2 reload

Tout fonctionne maintenant.

Aubs
la source