file_get_contents (): Operacja SSL nie powiodła się z kodem 1, Nie udało się włączyć szyfrowania

188

Próbowałem uzyskać dostęp do tej konkretnej usługi REST ze strony PHP, którą utworzyłem na naszym serwerze. Zawęziłem problem do tych dwóch linii. Moja strona PHP wygląda tak:

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

Strona umiera w wierszu 2 z następującymi błędami:

  • Ostrzeżenie: file_get_contents (): Operacja SSL nie powiodła się z kodem 1. OpenSSL Komunikaty o błędach: błąd: 14090086: Procedury SSL: SSL3_GET_SERVER_CERTIFICATE: weryfikacja certyfikatu nie powiodła się ... php w linii 2
    • Ostrzeżenie: file_get_contents (): Nie można włączyć szyfrowania w ... php w linii 2
    • Ostrzeżenie: 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): nie można otworzyć strumienia: operacja nie powiodła się w ... php w linii 2

Używamy serwera Gentoo. Niedawno zaktualizowaliśmy do wersji PHP 5.6. Ten problem pojawił się po aktualizacji.

Znalazłem, kiedy zastępuję usługę REST adresem takim jak https://www.google.com; moja strona działa dobrze.

We wcześniejszej próbie ustawiłem “verify_peer”=>falsei przekazałem to jako argument do file_get_contents, jak opisano tutaj: file_get_contents ignorując Verify_peer => false? Ale jak zauważył pisarz; to nie miało znaczenia.

Zapytałem jednego z naszych administratorów serwerów, czy istnieją następujące wiersze w naszym pliku php.ini:

  • rozszerzenie = php_openssl.dll
  • allow_url_fopen = Wł

Powiedział mi, że skoro jesteśmy na Gentoo, openssl jest kompilowany podczas kompilacji; i nie jest ustawiony w pliku php.ini.

Potwierdziłem również, że allow_url_fopendziała. Ze względu na specjalistyczny charakter tego problemu; Nie znajduję wielu informacji o pomocy. Czy ktoś z was spotkał coś takiego? Dzięki.

Joe
źródło
Jeśli korzystasz z Kaspersky, zaznacz to: stackoverflow.com/a/54791481/3549317
cespon

Odpowiedzi:

344

Był to niezwykle pomocny link do znalezienia:

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

Oficjalny dokument opisujący zmiany wprowadzone w celu otwarcia ssl w PHP 5.6 Odtąd dowiedziałem się o jeszcze jednym parametrze, który powinienem ustawić na false: "

Uwaga: Ma to bardzo znaczący wpływ na bezpieczeństwo. Wyłączenie weryfikacji potencjalnie pozwala osobie atakującej MITM na użycie nieprawidłowego certyfikatu do podsłuchania żądań. Chociaż może być to przydatne w rozwoju lokalnym, w produkcji należy stosować inne podejścia.

Mój działający kod wygląda następująco:

<?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
źródło
122
łamie to certyfikat SSL i jest dziurą w zabezpieczeniach
hypery2k,
8
Jak wskazał @ hypery2k, nie należy po prostu wyłączać weryfikacji. Zobacz moją odpowiedź na alternatywne rozwiązanie, które nie pokona celu użycia ssl.
elitechief21,
4
Tak naprawdę nie należy tego robić. Zamiast omijać problem, należy go naprawić. Zobacz lepsze rozwiązanie z @ elitechief21.
Jasper
5
Może to być frustrujące, jeśli testujesz z lokalnego środowiska pod kątem interfejsu API. W takim przypadku zwykle nuke weryfikację.
HappyCoder
12
Ponieważ zawsze przychodzę tutaj, gdy próbuję to zrobić, oto kod do łatwego kopiowania i wklejania:file_get_contents($url, false, stream_context_create(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false))));
laurent
154

Nie należy po prostu wyłączać weryfikacji. Raczej powinieneś pobrać pakiet certyfikatów, może pakiet curl zrobi?

Następnie wystarczy umieścić go na serwerze WWW, dając użytkownikowi, który uruchamia php, uprawnienia do odczytu pliku. Następnie ten kod powinien działać dla Ciebie:

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

Mamy nadzieję, że certyfikat główny witryny, do której próbujesz uzyskać dostęp, znajduje się w pakiecie curl. Jeśli tak nie jest, nadal nie będzie działać, dopóki nie zdobędziesz certyfikatu głównego witryny i nie umieścisz go w pliku certyfikatu.

elitechief21
źródło
Skąd pochodzi plik .crt? Nie ma linku na stronie curl, którą podajesz, że ma tylko .pem.
vee
1
Plik pem powinien być taki sam. W chwili opublikowania tego dokumentu mieli na swojej stronie dwie wersje pakietu certyfikatów: pem i crt, a ja po prostu użyłem pliku crt w moim przykładzie (oczywiście to one usuną). Do wglądu w przyszłości pliki CRT są zwykle przemianowywane na pliki PEM, których nazwy są zmieniane, aby system Windows rozpoznał plik jako plik certyfikatu. Nie zawsze tak będzie, ale tak jest w tym przypadku. Zaktualizuję mój przykład, aby używał pliku pem obecnie na stronie curl
elitechief21
4
jest też przydatna funkcja, z stream_context_set_defaultktórej można korzystać, więc nie trzeba za każdym razem przekazywać jej do file_get_contents
Ken Koch
Czy ktoś kiedykolwiek sprawił, że to zadziałało? Próbowałem z pakietem cacert.pem połączonym z tą odpowiedzią, a także z certyfikatem głównym CA Comodo (który jest głównym certyfikatem CA dla certyfikatu, który chcę zweryfikować), ale zawsze się nie powiedzie.
zmippie
1
Z punktu widzenia bezpieczeństwa: obok użycia opcji kontekstu strumienia szyfrującego bardzo ważne może być również zdefiniowanie dozwolonych szyfrów w opcji kontekstu strumienia szyfrującego i zabronienie tych wersji SSL, które są znane jako podatne na atak. Zalecane jest również ustawienie opcji kontekstu strumienia „enable_compression” na wartość true, aby złagodzić wektor ataku CRIME.
Josef Glatz,
36

Naprawiłem to, upewniając się, że OpenSSL został zainstalowany na moim komputerze, a następnie dodając to do mojego php.ini:

openssl.cafile=/usr/local/etc/openssl/cert.pem
andlin
źródło
@Akhi Jeśli znajdziesz go w Google, powinieneś być w stanie znaleźć tutoriale
andlin
2
Użyłem tej samej metody przy użyciu PHP na IIS 7, pobrany cert.pemplik i ustawić php.initak, i to działało:openssl.cafile=D:\Tools\GnuWin32\bin\cacert.pem
David Refoua
1
Pobrałem plik PEM z curl.haxx.se/docs/caextract.html - naprawiłem problem w systemie Windows przy użyciu określonego adresu URL gstatic.com.
Jake
Pobieranie pliku PEM z curl.haxx.se/docs/caextract.html nie działało dla mnie na Centos 7. Utworzyłem certyfikat pakietu, łącząc główny certyfikat z pkcs7 i umieszczając go na serwerze, a następnie określając ścieżkę openssl.cafile. +1 za właściwą odpowiedź i kierunek.
Arvind K.
Tworząc pakiet, nie zapomnij przekonwertować pliku pkcs na plik pem przed zatwierdzeniem go do głównej certyfikacji
Arvind K.
22

Możesz obejść ten problem, pisząc niestandardową funkcję używającą curl, jak w:

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;

}

Następnie używaj file_get_contents_curlzamiast, file_get_contentsgdy dzwonisz do adresu URL zaczynającego się od https.

Ben Shoval
źródło
To zadziałało dla mnie. Świetna robota i dziękuję!
Nick Green
11

Pracując dla mnie, używam PHP 5.6. rozszerzenie openssl powinno być włączone i podczas wywoływania google map api Verify_peer make false Poniższy kod działa dla mnie.

<?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
źródło
10

Jeśli twoja wersja PHP to 5, spróbuj zainstalować cURL, wpisując następujące polecenie w terminalu:

sudo apt-get install php5-curl
ARUNBALAN NV
źródło
9
To nie ma absolutnie nic wspólnego cURL.
Andreas
2
Instalacja curl php powinna być poprawną odpowiedzią! W moim systemie Mac używam portu, a polecenie brzmi:sudo port install php70-curl
hailong
6

poniższe kroki naprawią ten problem,

  1. Pobierz certyfikat CA z tego linku: https://curl.haxx.se/ca/cacert.pem
  2. Znajdź i otwórz php.ini
  3. Wyszukaj curl.cainfoi wklej bezwzględną ścieżkę do miejsca, w którym pobrano certyfikat.curl.cainfo ="C:\wamp\htdocs\cert\cacert.pem"
  4. Uruchom ponownie WAMP / XAMPP (serwer apache).
  5. To działa!

mam nadzieję, że to pomaga !!

shakee93
źródło
czy to jest bezpieczne? uwielbiam łatwe rozwiązania, ale brakuje mi tutaj niektórych informacji ..
szwajcarski
do testowania jest w porządku
Geomorillo
5

Chciałem tylko to dodać, ponieważ napotkałem ten sam problem i nic, co nigdzie nie mogę znaleźć, nie zadziałałoby (np. Pobranie pliku cacert.pem, ustawienie cafile w php.ini itp.)

Jeśli używasz NGINX, a Twój certyfikat SSL jest dostarczany z „certyfikatem pośrednim”, musisz połączyć pośredni plik certyfikatu z głównym plikiem „mydomain.com.crt” i powinien on działać. Apache ma ustawienie specyficzne dla pośrednich certyfikatów, ale NGINX tego nie robi, więc musi znajdować się w tym samym pliku, co zwykły certyfikat.

SlickTheNick
źródło
4

Przyczyną tego błędu jest to, że PHP nie ma listy zaufanych urzędów certyfikacji.

PHP 5.6 i nowsze wersje próbują automatycznie ładować CA zaufane przez system. Problemy z tym można naprawić. Więcej informacji można znaleźć na stronie http://php.net/manual/en/migration56.openssl.php .

PHP 5.5 i wcześniejsze są naprawdę trudne do skonfigurowania poprawnie, ponieważ musisz ręcznie określić pakiet CA w każdym kontekście żądania, czego nie chcesz rozpraszać wokół swojego kodu. Dlatego zdecydowałem się na mój kod, że dla wersji PHP <5.6 weryfikacja SSL po prostu wyłącza się:

$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
źródło
To pomogło mi znaleźć mój problem. Mimo że korzystam z PHP 5.6, korzystałem z przestarzałej biblioteki klienta interfejsu API, która ręcznie określiła stary plik CA za pomocą opcji kontekstu cafile zgodnie z powyższym linkiem. Usunięcie tego z biblioteki klienta interfejsu API naprawiło to dla mnie. Prawdopodobnie PHP zaczął używać zaufanego pakietu OpenSSL
Joe Lipson
4

Miał ten sam błąd z PHP 7 na XAMPP i OSX.

Powyższa odpowiedź w https://stackoverflow.com/ jest dobra, ale nie rozwiązała całkowicie problemu dla mnie. Musiałem podać pełny łańcuch certyfikatów, aby file_get_contents () znów działał. Tak to zrobiłem:

Uzyskaj certyfikat główny / pośredni

Przede wszystkim musiałem dowiedzieć się, co to jest root i certyfikat pośredni.

Najwygodniejszym sposobem może być internetowe narzędzie certyfikujące, takie jak SSL-Shopper

Tam znalazłem trzy certyfikaty, jeden certyfikat serwera i dwa certyfikaty łańcuchowe (jeden jest rootem, drugi najwyraźniej pośrednikiem).

Wszystko, co muszę zrobić, to po prostu wyszukać w Internecie oba z nich. W moim przypadku jest to katalog główny:

thawte DV SSL SHA256 CA

I to prowadzi do jego url thawte.com . Więc po prostu umieściłem ten certyfikat w pliku tekstowym i zrobiłem to samo dla pośredniego. Gotowe.

Uzyskaj certyfikat hosta

Następną rzeczą, którą musiałem zrobić, jest pobranie certyfikatu serwera. W systemie Linux lub OS X można to zrobić za pomocą openssl:

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

Teraz połącz je wszystkie

Teraz po prostu połącz je wszystkie w jeden plik. (Może warto po prostu umieścić je w jednym folderze, po prostu połączyłem je w jeden plik). Możesz to zrobić w następujący sposób:

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

powiedz PHP gdzie znaleźć łańcuch

Jest ta przydatna funkcja openssl_get_cert_locations (), która powie ci, gdzie PHP szuka plików cert. I jest ten parametr, który powie file_get_contents (), gdzie szukać plików cert. Może oba sposoby będą działać. Wolałem sposób parametru. (W porównaniu do rozwiązania wspomnianego powyżej).

To jest teraz mój kod 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));

To wszystko. file_get_contents () znów działa. Bez CURL i, mam nadzieję, bez wad bezpieczeństwa.

nr
źródło
Co znajduje się plik chain.pem? Czy to chain.crtjest
Dr.X
To nie jest chain.crt, to dotyczy samego certyfikatu. Jest to lista certyfikatów pośrednich, czyli łańcucha certyfikatów. Nie musisz tego koniecznie. Skorzystaj z kontrolera certyfikatów SSL, aby dowiedzieć się, czy go potrzebujesz. Jeśli tak, możesz wyszukać nazwę wystawcy certyfikatu + termin „łańcuch” lub „pośredni”, aby znaleźć właściwy plik.
nr
4

Po padnięciu ofiarą tego problemu na centOS po aktualizacji php do php5.6 znalazłem rozwiązanie, które działało dla mnie.

Znajdź w tym katalogu odpowiedni katalog dla swoich certyfikatów

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

Następnie użyj tego, aby uzyskać certyfikat i umieścić go w domyślnej lokalizacji znalezionej na podstawie powyższego kodu

wget http://curl.haxx.se/ca/cacert.pem -O <default location>
Edward DiGirolamo
źródło
3

Miałem ten sam problem ssl na moim komputerze programisty (php 7, xampp w systemie Windows) z samopodpisanym certyfikatem próbującym otworzyć plik „ https: // localhost / ...”. Oczywiście zestaw certyfikatu głównego (cacert.pem) nie działał. Właśnie skopiowałem ręcznie kod z pliku Apache server.crt-file w pobranym pliku cacert.pem i zrobiłem wpis openssl.cafile = ścieżka / do / cacert.pem w php.ini

hangerer
źródło
2

Inną rzeczą do wypróbowania jest ponowna instalacja, ca-certificatesjak opisano tutaj .

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

Kolejną rzeczą, którą należy wypróbować, jest wyraźne zezwolenie na certyfikat dla jednej witryny, jak opisano tutaj (szczególnie, jeśli ta witryna jest własnym serwerem i masz już dostęp do pliku .pem).

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

Natrafiłem na ten właśnie błąd SO po aktualizacji do PHP 5.6 na CentOS 6, próbując uzyskać dostęp do samego serwera, który ma tani certyfikat bezpieczeństwa, który być może musiał zostać zaktualizowany, ale zamiast tego zainstalowałem certyfikat letsencrypt i po dwóch powyższych krokach zrobiłem to sztuczka. Nie wiem, dlaczego drugi krok był konieczny.


Przydatne polecenia

Wyświetl wersję openssl:

# openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

Wyświetl bieżące ustawienia 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
teren
źródło
1

Odnośnie błędów podobnych do

[11-maj-2017 19:19:13 Ameryka / Chicago] Ostrzeżenie PHP: file_get_contents (): Operacja SSL nie powiodła się z kodem 1. OpenSSL Komunikaty o błędach: błąd: 14090086: Procedury SSL: ssl3_get_server_certificate: weryfikacja certyfikatu nie powiodła się

Czy sprawdziłeś uprawnienia do certyfikatów i katalogów, do których odwołuje się openssl?

Możesz to zrobić

var_dump(openssl_get_cert_locations());

Aby uzyskać coś podobnego do tego

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

Ten problem mnie frustrował przez pewien czas, dopóki nie zdałem sobie sprawy, że mój folder „certs” ma 700 uprawnień, a powinien mieć 755 uprawnień. Pamiętaj, że to nie jest folder kluczy, ale certyfikatów. Polecam przeczytanie tego linku na temat uprawnień ssl.

Kiedyś to zrobiłem

chmod 755 certs

Problem został naprawiony, przynajmniej dla mnie.

Aleks
źródło
Tak! CHMOD był również moim problemem ;-) Dzięki
RA.
0

Miałem ten sam problem z kolejną bezpieczną stroną podczas używania wgetlub file_get_contents. Wiele badań (w tym niektóre odpowiedzi na to pytanie) zaowocowało prostym rozwiązaniem - instalacją Curl i PHP-Curl - Jeśli dobrze zrozumiałem, Curl ma główny urząd certyfikacji dla Comodo, który rozwiązał problem

Zainstaluj dodatek Curl i PHP-Curl, a następnie uruchom ponownie Apache

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

Wszystko teraz działa.

Aubs
źródło