file_get_contents (): operação SSL falhou com o código 1, falha ao ativar criptografia

188

Eu tenho tentado acessar este serviço REST em particular a partir de uma página PHP que criei em nosso servidor. Eu reduzi o problema até essas duas linhas. Então, minha página PHP fica assim:

<?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; ?>

A página morre na linha 2 com os seguintes erros:

  • Aviso: file_get_contents (): A operação SSL falhou com o código 1. Mensagens de erro do OpenSSL: erro: 14090086: rotinas SSL: SSL3_GET_SERVER_CERTIFICATE: falha na verificação do certificado em ... php na linha 2
    • Aviso: file_get_contents (): falha ao ativar criptografia em ... php na linha 2
    • Aviso: 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): falha ao abrir fluxo: operação falhou em ... php na linha 2

Estamos usando um servidor Gentoo. Recentemente, atualizamos para o PHP versão 5.6. Foi após a atualização quando esse problema apareceu.

Encontrei quando substituí o serviço REST por um endereço como https://www.google.com; minha página funciona muito bem.

Em uma tentativa anterior “verify_peer”=>false, configurei e passei isso como um argumento para file_get_contents, conforme descrito aqui: file_get_contents ignorando o confirm_peer => false? Mas como o escritor observou; não fez diferença.

Perguntei a um de nossos administradores de servidor se essas linhas em nosso arquivo php.ini existem:

  • extension = php_openssl.dll
  • allow_url_fopen = Ativado

Ele me disse que, como estamos no Gentoo, o openssl é compilado quando construímos; e não está definido no arquivo php.ini.

Também confirmei que allow_url_fopenestá funcionando. Devido à natureza especializada deste problema; Não estou encontrando muitas informações para obter ajuda. Algum de vocês se deparou com algo assim? Obrigado.

Joe
fonte
Se você usa o Kaspersky, verifique o seguinte: stackoverflow.com/a/54791481/3549317
cespon

Respostas:

344

Este foi um link extremamente útil para encontrar:

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

Um documento oficial descrevendo as alterações feitas para abrir o ssl no PHP 5.6 A partir daqui, aprendi mais um parâmetro que deveria ter definido como false: "confirm_peer_name" => false

Nota: Isso tem implicações de segurança muito significativas. Desabilitar a verificação potencialmente permite que um invasor MITM use um certificado inválido para interceptar as solicitações. Embora possa ser útil fazer isso no desenvolvimento local, outras abordagens devem ser usadas na produção.

Portanto, meu código de trabalho se parece com isso:

<?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
fonte
122
esta certificação breaks SSL e é uma falha de segurança
hypery2k
8
Como @ hypery2k apontou, você não deve apenas desativar a verificação. Veja minha resposta para uma solução alternativa que não anula o propósito de usar ssl.
elitechief21
4
Isso realmente não deveria ser feito. Em vez de contornar o problema, você deve corrigi-lo. Veja a melhor solução em @ elitechief21.
Jasper
5
Isso pode ser frustrante se você estiver testando de um ambiente local contra uma API. Neste caso, eu normalmente destruo a verificação.
HappyCoder # 30/15
12
Desde que eu sempre vir aqui quando eu tento fazer isso, aqui está o código para facilmente copiar e colar:file_get_contents($url, false, stream_context_create(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false))));
laurent
154

Você não deve simplesmente desativar a verificação. Em vez disso, você deve baixar um pacote de certificados, talvez o pacote curl funcione?

Então você só precisa colocá-lo no seu servidor web, dando ao usuário que executa o php permissão para ler o arquivo. Então este código deve funcionar para você:

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

Felizmente, o certificado raiz do site que você está tentando acessar está no pacote de ondulação. Caso contrário, isso ainda não funcionará até você obter o certificado raiz do site e colocá-lo no seu arquivo de certificado.

elitechief21
fonte
De onde vem o arquivo .crt? Não há nenhum link no site curl que você fornece, apenas .pem.
vee 23/05
1
O arquivo pem deve ser o mesmo. No momento em que foi publicado, eles tinham duas versões do pacote de certificados em seu site, um pem e um crt e eu apenas usei o arquivo crt no meu exemplo (naturalmente, esse seria o que eles removeriam). Para referência futura, os arquivos crt geralmente são renomeados para arquivos pem, que são renomeados para que o Windows reconheça o arquivo como um arquivo de certificado. Isso nem sempre será o caso, mas é neste caso. Eu vou atualizar meu exemplo para que ele usa o arquivo pem atualmente no site onda
elitechief21
4
Há também uma função útil stream_context_set_defaultque pode ser usado para que você não tem que passá-lo em file_get_contents toda
Ken Koch
Alguém já fez isso funcionar? Tentei com o pacote cacert.pem vinculado a esta resposta e também com o certificado Raiz da CA Comodo (que é a raiz da CA do certificado que desejo verificar), mas sempre falha.
Zmippie
1
De uma perspectiva de segurança: ao lado do uso da opção de contexto de fluxo de café, pode ser muito importante definir também as cifras permitidas na opção de contexto de fluxo de cifras e proibir as versões SSL conhecidas como vulneráveis. Também é recomendável definir a opção de contexto do fluxo disable_compression como true para mitigar o vetor de ataque CRIME.
Josef Glatz
36

Corrigi isso corrigindo o OpenSSL instalado na minha máquina e adicionando-o ao meu php.ini:

openssl.cafile=/usr/local/etc/openssl/cert.pem
andlin
fonte
@Akhi Você deve ser capaz de encontrar alguns tutoriais se você pesquisar no Google
andlin
2
Eu usei o mesmo método usando o PHP 7 no IIS, baixei o cert.pemarquivo e defini-lo php.inicomo este, e funcionou:openssl.cafile=D:\Tools\GnuWin32\bin\cacert.pem
David Refoua
1
Fiz o download do arquivo PEM em curl.haxx.se/docs/caextract.html - corrigi o problema para mim no Windows com uma URL específica do gstatic.com.
21818
O download do arquivo PEM de curl.haxx.se/docs/caextract.html não funcionou para mim no Centos 7. Criei um certificado de pacote configurável concatenando o certificado principal e o pkcs7, coloque-o no servidor e especifique o caminho openssl.cafile. +1 para resposta e direção corretas.
Arvind K.Fev
Ao criar um pacote não se esqueça de converter PKCS para arquivo pem antes concating-lo para certificação principal
Arvind K.
22

Você pode solucionar esse problema escrevendo uma função personalizada que usa curl, como em:

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;

}

Em seguida, basta usar em file_get_contents_curlvez de file_get_contentssempre que você estiver chamando um URL que começa com https.

Ben Shoval
fonte
Isso funcionou para mim. Grande trabalho e obrigado!
Nick Green
11

Trabalhando para mim, estou usando o PHP 5.6. A extensão openssl deve estar ativada e, ao ligar para o mapa do Google api confirm_peer make false Abaixo, o código está funcionando para mim.

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

Se a sua versão do PHP for 5, tente instalar o cURL digitando o seguinte comando no terminal:

sudo apt-get install php5-curl
ARUNBALAN NV
fonte
9
Isso não tem absolutamente nada a ver cURL.
Andreas
2
Instalar o php curl deve ser a resposta correta! Para o meu sistema Mac, estou usando port, e o comando é:sudo port install php70-curl
hailong 23/09/16
6

as etapas a seguir corrigirão esse problema,

  1. Faça o download do certificado da CA neste link: https://curl.haxx.se/ca/cacert.pem
  2. Encontre e abra o php.ini
  3. Procure curl.cainfoe cole o caminho absoluto no qual você baixou o certificado.curl.cainfo ="C:\wamp\htdocs\cert\cacert.pem"
  4. Reinicie o WAMP / XAMPP (servidor apache).
  5. Funciona!

espero que ajude !!

shakee93
fonte
isso é seguro? eu amo soluções fáceis, mas im faltando algumas informações aqui ..
swisswiss
para teste está ok
Geomorillo
5

Só queria acrescentar isso, já que encontrei o mesmo problema e nada que eu pudesse encontrar em qualquer lugar funcionaria (por exemplo, fazer o download do arquivo cacert.pem, definir cafile no php.ini etc.)

Se você estiver usando o NGINX e seu certificado SSL for fornecido com um "certificado intermediário", será necessário combinar o arquivo intermediário com o arquivo principal "mydomain.com.crt" e ele deverá funcionar. O Apache possui uma configuração específica para certificados intermediários, mas o NGINX não possui, portanto deve estar no mesmo arquivo que o seu certificado regular.

SlickTheNick
fonte
4

O motivo desse erro é que o PHP não possui uma lista de autoridades de certificação confiáveis.

O PHP 5.6 e posterior tentam carregar as CAs confiáveis ​​pelo sistema automaticamente. Problemas com isso podem ser corrigidos. Veja http://php.net/manual/en/migration56.openssl.php para mais informações.

O PHP 5.5 e versões anteriores são realmente difíceis de configurar corretamente, pois você precisa especificar manualmente o pacote CA em cada contexto de solicitação, algo que você não deseja espalhar pelo seu código. Então, decidi pelo meu código que, nas versões PHP <5.6, a verificação SSL simplesmente fica desativada:

$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
fonte
Isso me ajudou a encontrar o meu problema. Embora eu esteja no PHP 5.6, eu estava usando uma biblioteca cliente api desatualizada que especificou manualmente um arquivo CA antigo usando a opção de contexto cafile conforme o link acima. Remover isso da biblioteca cliente da API corrigia isso para mim. Presumivelmente PHP começaram a usar OpenSSLs confiável pacote
Joe Lipson
4

Teve o mesmo erro com o PHP 7 no XAMPP e OSX.

A resposta acima mencionada em https://stackoverflow.com/ é boa, mas não resolveu completamente o problema para mim. Eu tive que fornecer a cadeia de certificados completa para fazer o file_get_contents () funcionar novamente. Foi assim que eu fiz:

Obter certificado raiz / intermediário

Antes de tudo, tive que descobrir qual é a raiz e o certificado intermediário.

A maneira mais conveniente é talvez uma ferramenta de certificação on-line como o ssl-shopper

Lá encontrei três certificados, um certificado de servidor e dois certificados de cadeia (um é a raiz, o outro aparentemente o intermediário).

Tudo o que preciso fazer é pesquisar na internet os dois. No meu caso, esta é a raiz:

descodificação DV SSL SHA256 CA

E isso leva ao seu URL thawte.com . Então, eu apenas coloquei esse certificado em um arquivo de texto e fiz o mesmo para o intermediário. Feito.

Obter o certificado do host

A próxima coisa que eu precisava fazer era baixar meu certificado de servidor. No Linux ou OS X, isso pode ser feito com o openssl:

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

Agora junte todos eles

Agora basta mesclar todos eles em um arquivo. (Talvez seja bom apenas colocá-los em uma pasta, apenas os fundi em um arquivo). Você pode fazer assim:

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

diga ao PHP onde encontrar a cadeia

Existe esta útil função openssl_get_cert_locations () que lhe dirá onde o PHP está procurando por arquivos cert. E existe esse parâmetro, que informará ao file_get_contents () onde procurar arquivos cert. Talvez os dois lados funcionem. Eu preferi o parâmetro way. (Comparado com a solução mencionada acima).

Então este é agora o meu 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));

Isso é tudo. file_get_contents () está funcionando novamente. Sem CURL e espero que sem falhas de segurança.

nr
fonte
Qual é o arquivo chain.pem? É isso chain.crt?
Dr.X
Não é o chain.crt, é o certificado real. É a lista de certificados intermediários, também conhecidos como cadeia de certificados. Você não precisa necessariamente disso. Use um verificador de certificado SSL para descobrir se você precisa. Nesse caso, você pode procurar o nome do seu emissor de certificado + o termo "cadeia" ou "intermediário" para encontrar o arquivo correto.
nr
4

Depois de ser vítima desse problema no centOS após atualizar o php para php5.6, encontrei uma solução que funcionou para mim.

Obtenha o diretório correto para seus certificados serem colocados por padrão com este

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

Em seguida, use isso para obter o certificado e colocá-lo no local padrão encontrado no código acima

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

Tive o mesmo problema de SSL na minha máquina do desenvolvedor (php 7, xampp no ​​windows) com um certificado autoassinado tentando abrir um arquivo " https: // localhost / ...". Obviamente, o conjunto de certificados raiz (cacert.pem) não funcionou. Acabei de copiar manualmente o código do arquivo apache server.crt no arquivo cacert.pem baixado e fiz a entrada openssl.cafile = path / to / cacert.pem no php.ini

cabide
fonte
2

Outra coisa a tentar é reinstalar ca-certificatescomo detalhado aqui .

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

E outra coisa a tentar é permitir explicitamente o certificado de um site em questão, conforme descrito aqui (especialmente se o site for o seu próprio servidor e você já tiver o .pem ao seu alcance).

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

Eu estava enfrentando esse erro de SO exato depois de atualizar para o PHP 5.6 no CentOS 6, tentando acessar o próprio servidor que possui um certificado de segurança de baixo custo que talvez precise ser atualizado, mas em vez disso instalei um certificado letsencrypt e com as duas etapas acima. o truque. Não sei por que o segundo passo foi necessário.


Comandos úteis

Exibir versão openssl:

# openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

Veja as configurações atuais do 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
local
fonte
1

Em relação a erros semelhantes a

[11-May-2017 19:19:13 America / Chicago] PHP Aviso: file_get_contents (): A operação SSL falhou com o código 1. OpenSSL Mensagens de erro: erro: 14090086: rotinas SSL: ssl3_get_server_certificate: falha na verificação do certificado

Você verificou as permissões do certificado e dos diretórios referenciados pelo openssl?

Você consegue fazer isso

var_dump(openssl_get_cert_locations());

Para obter algo semelhante a este

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

Esse problema me frustrou por um tempo, até que percebi que minha pasta "certs" tinha 700 permissões, quando deveria ter 755 permissões. Lembre-se, essa não é a pasta para chaves, mas certificados. Eu recomendo ler este link com permissões ssl.

Uma vez eu fiz

chmod 755 certs

O problema foi corrigido, pelo menos para mim de qualquer maneira.

Aleks
fonte
Yay! CHMOD também foi problema meu ;-) Obrigado
RA.
0

Eu tive o mesmo problema em outra página segura ao usar wgetou file_get_contents. Muitas pesquisas (incluindo algumas das respostas sobre essa pergunta) resultaram em uma solução simples - instalação do Curl e PHP-Curl - Se eu entendi corretamente, o Curl tem a CA raiz da Comodo que resolveu o problema

Instale os complementos Curl e PHP-Curl e reinicie o Apache

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

Tudo agora trabalhando.

Aubs
fonte