file_get_contents (): la operación SSL falló con el código 1, no se pudo habilitar el cifrado

188

He estado intentando acceder a este servicio REST en particular desde una página PHP que he creado en nuestro servidor. Reduje el problema a estas dos líneas. Entonces mi página PHP se ve así:

<?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 página muere en la línea 2 con los siguientes errores:

  • Advertencia: file_get_contents (): la operación SSL falló con el código 1. OpenSSL Mensajes de error: error: 14090086: rutinas SSL: SSL3_GET_SERVER_CERTIFICATE: la verificación del certificado falló en ... php en la línea 2
    • Advertencia: file_get_contents (): no se pudo habilitar el cifrado en ... php en la línea 2
    • Advertencia: 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): no se pudo abrir la secuencia: la operación falló en ... php en la línea 2

Estamos usando un servidor Gentoo. Recientemente actualizamos a PHP versión 5.6. Fue después de la actualización cuando apareció este problema.

Encontré cuando reemplazo el servicio REST con una dirección como https://www.google.com; mi página funciona bien

En un intento anterior, configuré “verify_peer”=>falsey pasé eso como un argumento a file_get_contents, como se describe aquí: file_get_contents ignorando execute_peer => false? Pero como señaló el escritor; No hizo ninguna diferencia.

Le pregunté a uno de nuestros administradores de servidores si existen estas líneas en nuestro archivo php.ini:

  • extension = php_openssl.dll
  • allow_url_fopen = Activado

Me dijo que, dado que estamos en Gentoo, openssl se compila cuando construimos; y no está configurado en el archivo php.ini.

También confirmó que allow_url_fopenestá funcionando. Debido a la naturaleza especializada de este problema; No encuentro mucha información para obtener ayuda. ¿Alguno de ustedes se ha encontrado con algo como esto? Gracias.

Joe
fuente
Si usa Kaspersky, verifique esto: stackoverflow.com/a/54791481/3549317
cespon

Respuestas:

344

Este fue un enlace enormemente útil para encontrar:

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

Un documento oficial que describe los cambios realizados para abrir ssl en PHP 5.6. Desde aquí aprendí un parámetro más que debería haber establecido en false: "verificar_nombre_peer" => falso

Nota: Esto tiene implicaciones de seguridad muy significativas. Deshabilitar la verificación potencialmente permite que un atacante MITM use un certificado no válido para espiar las solicitudes. Si bien puede ser útil hacer esto en el desarrollo local, se deben usar otros enfoques en la producción.

Entonces mi código de trabajo se ve así:

<?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
fuente
122
esto rompe la certificación SSL y es un agujero de seguridad
hypery2k
8
Como señaló @ hypery2k, no solo debe deshabilitar la verificación. Vea mi respuesta para una solución alternativa que no anule el propósito de usar SSL.
elitechief21
44
Esto realmente no debería hacerse. En lugar de esquivar el problema, debería solucionarlo. Vea la mejor solución de @ elitechief21.
Jasper
55
Esto puede ser frustrante si está probando desde un entorno local contra una API. En este caso, normalmente hago la verificación nuclear
HappyCoder
12
Como siempre vengo aquí cuando trato de hacer esto, aquí está el código para copiar y pegar fácilmente:file_get_contents($url, false, stream_context_create(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false))));
Laurent
154

No deberías simplemente desactivar la verificación. En su lugar, debe descargar un paquete de certificados, ¿tal vez lo hará el paquete curl ?

Luego solo tiene que ponerlo en su servidor web, dando al usuario que ejecuta php permiso para leer el archivo. Entonces este código debería funcionar para usted:

$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));

Con suerte, el certificado raíz del sitio al que está intentando acceder está en el paquete curl. Si no es así, esto todavía no funcionará hasta que obtenga el certificado raíz del sitio y lo coloque en su archivo de certificado.

elitechief21
fuente
¿De dónde viene el archivo .crt? No hay ningún enlace en el sitio web curl que le proporciones, solo tiene .pem.
vee
1
El archivo pem debe ser el mismo. En el momento en que esto se publicó, tenían dos versiones del paquete de certificados en su sitio, una pem y una crt y yo solo usé el archivo crt para mi ejemplo (naturalmente, esa es la que eliminarían). Para referencia futura, los archivos crt generalmente se renombran como archivos pem, que se renombran para que Windows reconozca el archivo como un archivo de certificado. Este no siempre será el caso, pero lo es en este caso. Actualizaré mi ejemplo para que use el archivo pem actualmente en el sitio curl
elitechief21
44
también hay una función útil stream_context_set_defaultque puede usarse para que no tenga que pasarla a file_get_contents cada vez
Ken Koch
¿Alguien ha conseguido que esto funcione? Intenté con el paquete cacert.pem vinculado en esta respuesta, y también con el certificado de raíz de CA Comodo (que es la raíz de CA para el certificado que quiero haber verificado), pero siempre fallará.
zmippie
1
Desde una perspectiva de seguridad: junto al uso de la opción de contexto de flujo de cafile, podría ser muy importante definir también los cifrados permitidos en la opción de contexto de flujo de cifrado y prohibir las versiones SSL que se conocen como vulnerables. También se recomienda configurar la opción de contexto de transmisión disable_compression en true para mitigar el vector de ataque CRIME.
Josef Glatz
36

Solucioné esto asegurándome de que OpenSSL estaba instalado en mi máquina y luego agregué esto a mi php.ini:

openssl.cafile=/usr/local/etc/openssl/cert.pem
andlin
fuente
@ Akhi Deberías poder encontrar algunos tutoriales si lo buscas en Google,
2016
2
Utilicé el mismo método usando PHP 7 en IIS, descargué el cert.pemarchivo y configuré el php.inisiguiente, y funcionó:openssl.cafile=D:\Tools\GnuWin32\bin\cacert.pem
David Refoua
1
Descargué el archivo PEM de curl.haxx.se/docs/caextract.html ; solucioné el problema para mí en Windows con una URL particular de gstatic.com.
Jake
La descarga del archivo PEM de curl.haxx.se/docs/caextract.html no funcionó para mí en Centos 7. Creé un certificado de paquete concatenando el certificado principal y pkcs7 y lo puse en el servidor y luego especifiqué la ruta openssl.cafile. +1 por respuesta y dirección correctas.
Arvind K.
Al crear un paquete, no olvide convertir pkcs a un archivo pem antes de concatarlo a la certificación principal
Arvind K.
22

Puede solucionar este problema escribiendo una función personalizada que use curl, como en:

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;

}

Luego, solo use en file_get_contents_curllugar de file_get_contentscuando llame a una url que comienza con https.

Ben Shoval
fuente
Esto funcionó para mí. gran trabajo y gracias!
Nick Green
11

Trabajando para mí, estoy usando PHP 5.6. La extensión openssl debe estar habilitada y al llamar a google map api verificar_peer hacer falso El siguiente código funciona para mí.

<?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
fuente
10

Si su versión de PHP es 5, intente instalar cURL escribiendo el siguiente comando en el terminal:

sudo apt-get install php5-curl
ARUNBALAN NV
fuente
9
Esto no tiene absolutamente nada que ver con cURL.
Andreas
2
¡Instalar php curl debería ser la respuesta correcta! Para mi sistema Mac, estoy usando port, y el comando es:sudo port install php70-curl
hailong
6

los siguientes pasos solucionarán este problema,

  1. Descargue el certificado de CA desde este enlace: https://curl.haxx.se/ca/cacert.pem
  2. Encuentra y abre php.ini
  3. Busque curl.cainfoy pegue la ruta absoluta donde descargó el Certificado.curl.cainfo ="C:\wamp\htdocs\cert\cacert.pem"
  4. Reinicie WAMP / XAMPP (servidor apache).
  5. ¡Funciona!

Espero que ayude !!

shakee93
fuente
¿Es esto seguro? amo la solución fácil, pero im falta alguna información aquí ..
swisswiss
para las pruebas está bien
Geomorillo
5

Solo quería agregar a esto ya que me encontré con el mismo problema y nada de lo que pude encontrar en ningún lugar funcionaría (por ejemplo, descargar el archivo cacert.pem, configurar cafile en php.ini, etc.)

Si está utilizando NGINX y su certificado SSL viene con un "certificado intermedio", debe combinar el archivo de certificado intermedio con su archivo principal "mydomain.com.crt" y debería funcionar. Apache tiene una configuración específica para los certificados intermedios, pero NGINX no, por lo que debe estar dentro del mismo archivo que su certificado regular.

SlickTheNick
fuente
4

La razón de este error es que PHP no tiene una lista de autoridades de certificación confiables.

PHP 5.6 y posteriores intentan cargar las CA confiables por el sistema automáticamente. Los problemas con eso se pueden solucionar. Ver http://php.net/manual/en/migration56.openssl.php para más información.

PHP 5.5 y versiones anteriores son realmente difíciles de configurar correctamente, ya que debe especificar manualmente el paquete CA en cada contexto de solicitud, algo que no desea espolvorear alrededor de su código. Así que decidí por mi código que para las versiones de PHP <5.6, la verificación SSL simplemente se deshabilita:

$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
fuente
Esto me ayudó a encontrar mi problema. Aunque estoy en PHP 5.6, estaba usando una biblioteca de cliente api desactualizada que especificaba manualmente un archivo CA antiguo usando la opción de contexto cafile según su enlace anterior. Quitar eso de la biblioteca del cliente api lo arregló para mí. Presumiblemente PHP comenzó a usar el paquete de confianza OpenSSLs
Joe Lipson
4

Tenía el mismo error con PHP 7 en XAMPP y OSX.

La respuesta mencionada anteriormente en https://stackoverflow.com/ es buena, pero no resolvió completamente el problema para mí. Tuve que proporcionar la cadena de certificados completa para que file_get_contents () volviera a funcionar. Así lo hice:

Obtener certificado raíz / intermedio

En primer lugar, tuve que averiguar cuál es la raíz y el certificado intermedio.

La forma más conveniente es tal vez una herramienta de certificación en línea como ssl-shopper

Allí encontré tres certificados, un certificado de servidor y dos certificados de cadena (uno es la raíz y el otro aparentemente el intermedio).

Todo lo que necesito hacer es buscar en Internet para ambos. En mi caso, esta es la raíz:

thawte DV SSL SHA256 CA

Y lleva a su url thawte.com . Así que simplemente puse este certificado en un archivo de texto e hice lo mismo para el intermedio. Hecho.

Obtenga el certificado de host

Lo siguiente que tuve que hacer es descargar mi certificado de servidor. En Linux u OS X se puede hacer con openssl:

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

Ahora reúnelos a todos

Ahora solo combine todos en un solo archivo. (Tal vez sea bueno simplemente ponerlos en una carpeta, solo los fusioné en un archivo). Puedes hacerlo así:

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

dile a PHP dónde encontrar la cadena

Existe esta práctica función openssl_get_cert_locations () que le dirá dónde PHP está buscando archivos cert. Y existe este parámetro, que le dirá a file_get_contents () dónde buscar archivos cert. Tal vez las dos formas funcionen. Preferí la forma del parámetro. (En comparación con la solución mencionada anteriormente).

Entonces este es mi código 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));

Eso es todo. file_get_contents () está funcionando nuevamente. Sin CURL y con suerte sin fallas de seguridad.

nr
fuente
¿Cuál es el archivo chain.pem? Es esto chain.crt?
Dr.X
No es el chain.crt, eso es para el certificado real. Es la lista de certificados intermedios, también conocida como cadena de certificados. No necesariamente lo necesitas. Use un verificador de certificados SSL para averiguar si lo necesita. Si es así, puede buscar el nombre de su emisor de certificados + el término "cadena" o "intermedio" para encontrar el archivo correcto.
nr
4

Después de ser víctima de este problema en centOS después de actualizar php a php5.6, encontré una solución que funcionó para mí.

Obtenga el directorio correcto para que sus certificados se coloquen de forma predeterminada con esto

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

Luego, use esto para obtener el certificado y colocarlo en la ubicación predeterminada que se encuentra en el código anterior

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

Tenía el mismo problema SSL en mi máquina de desarrollador (php 7, xampp en Windows) con un certificado autofirmado que intentaba encontrar un archivo " https: // localhost / ...". Obviamente, el ensamblaje del certificado raíz (cacert.pem) no funcionó. Acabo de copiar manualmente el código del archivo apache server.crt en el archivo cacert.pem descargado e hice la entrada openssl.cafile = path / to / cacert.pem en php.ini

ahorcado
fuente
2

Otra cosa para intentar es reinstalar ca-certificatescomo se detalla aquí .

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

Y otra cosa que debe intentar es permitir explícitamente el certificado de un sitio en cuestión como se describe aquí (especialmente si ese sitio es su propio servidor y ya tiene el .pem al alcance).

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

Me encontré con este error SO exacto después de actualizar a PHP 5.6 en CentOS 6 tratando de acceder al servidor en sí, que tiene un certificado de seguridad barato que tal vez necesitaba ser actualizado, pero en su lugar instalé un certificado de letencrypt y con estos dos pasos anteriores lo hice el truco. No sé por qué fue necesario el segundo paso.


Comandos útiles

Ver la versión de openssl:

# openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

Ver la configuración actual 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
sitio
fuente
1

En cuanto a errores similares a

[11-may-2017 19:19:13 America / Chicago] Advertencia de PHP: file_get_contents (): Error de operación SSL con el código 1. Mensajes de error de OpenSSL: error: 14090086: Rutinas SSL: ssl3_get_server_certificate: verificación de certificado fallida

¿Ha verificado los permisos de los certificados y directorios a los que hace referencia openssl?

Puedes hacerlo

var_dump(openssl_get_cert_locations());

Para obtener algo similar a esto

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) ""
}

Este problema me frustró por un tiempo, hasta que me di cuenta de que mi carpeta "certs" tenía 700 permisos, cuando debería haber tenido 755 permisos. Recuerde, esta no es la carpeta para las claves sino los certificados. Recomiendo leer este enlace en los permisos de SSL.

Una vez que lo hice

chmod 755 certs

El problema se solucionó, al menos para mí de todos modos.

Aleks
fuente
¡Hurra! CHMOD también fue mi problema ;-) Gracias
RA.
0

Tuve el mismo problema para otra página segura al usar wgetofile_get_contents . Una gran cantidad de investigación (incluidas algunas de las respuestas a esta pregunta) resultó en una solución simple: instalar Curl y PHP-Curl. Si he entendido correctamente, Curl tiene la CA raíz de Comodo que resolvió el problema

Instale el complemento Curl y PHP-Curl, luego reinicie Apache

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

Todo ahora trabajando.

Aubs
fuente