file_get_contents () : 코드 1로 SSL 작업 실패, 암호화 사용 실패


188

서버에서 만든 PHP 페이지에서이 특정 REST 서비스에 액세스하려고했습니다. 문제를이 두 줄로 좁혔습니다. 그래서 내 PHP 페이지는 다음과 같습니다

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

페이지가 2 행에서 다음 오류와 함께 종료됩니다.

  • 경고 : file_get_contents () : SSL 작업 코드 1 실패
    • 경고 : file_get_contents () : 2 행의 ... php에서 암호화를 활성화하지 못했습니다.
    • 경고 : 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) : 스트림을 열지 못했습니다 : 2 행의 ... php에서 작업이 실패했습니다.

우리는 젠투 서버를 사용하고 있습니다. 최근에 PHP 버전 5.6으로 업그레이드했습니다. 이 문제가 나타 났을 때 업그레이드 이후였습니다.

REST 서비스를 다음과 같은 주소로 바꿀 때 찾았습니다 https://www.google.com. 내 페이지가 잘 작동합니다.

이전 시도에서 나는을 설정 “verify_peer”=>false하고 여기에 설명 된대로 file_get_contents에 인수로 전달했습니다. verify_peer => false? 그러나 작가가 지적한 것처럼; 아무런 차이가 없었습니다.

php.ini 파일에 다음 줄이 있는지 서버 관리자에게 물었습니다.

  • extension = php_openssl.dll
  • allow_url_fopen = 켜기

그는 Gentoo를 사용하고 있기 때문에 우리가 빌드 할 때 openssl이 컴파일된다고 말했습니다. php.ini 파일에 설정되어 있지 않습니다.

또한 allow_url_fopen작동하고 있음을 확인했습니다 . 이 문제의 특수한 특성으로 인해; 도움이 필요한 정보를 많이 찾지 못했습니다. 이런 사람이 있습니까? 감사.


Kaspersky를 사용하는 경우 이것을 확인하십시오 : stackoverflow.com/a/54791481/3549317
cespon

답변:


344

이것은 다음을 찾는 데 매우 유용한 링크였습니다.

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

PHP 5.6에서 ssl을 여는 변경 사항을 설명하는 공식 문서 여기에서 내가 false로 설정해야하는 하나 이상의 매개 변수에 대해 배웠습니다. "verify_peer_name"=> false

참고 : 이것은 보안에 매우 중요한 영향을 미칩니다. 확인을 비활성화하면 MITM 공격자 가 잘못된 인증서를 사용하여 요청을 도청 할 수 있습니다. 로컬 개발에서이 작업을 수행하는 것이 유용 할 수 있지만 프로덕션에서는 다른 접근 방식을 사용해야합니다.

작업 코드는 다음과 같습니다.

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

122
이 휴식의 SSL 인증 및 보안 구멍입니다
hypery2k

8
@ hypery2k가 지적했듯이 확인을 비활성화해서는 안됩니다. ssl 사용 목적을 해치지 않는 대체 솔루션에 대한 내 대답을 참조하십시오.
elitechief21

4
이 작업은 실제로 수행해서는 안됩니다. 문제를 회피하는 대신 실제로 수정해야합니다. @ elitechief21의 더 나은 솔루션을 참조하십시오.
재스퍼

5
API에 대해 로컬 환경에서 테스트하는 경우 실망 할 수 있습니다. 이 경우 일반적으로 확인을 수행합니다.
HappyCoder

12
이 작업을 수행 할 때 항상 여기에 오기 때문에 쉽게 복사하여 붙여 넣을 수있는 코드는 다음과 같습니다.file_get_contents($url, false, stream_context_create(array('ssl' => array('verify_peer' => false, 'verify_peer_name' => false))));
laurent

154

확인 만 끄면 안됩니다. 오히려 번들이 할 인증서 번들을 다운로드 해야합니까?

그런 다음 웹 서버에 저장하면 PHP를 실행하는 사용자에게 파일을 읽을 수있는 권한을 부여 할 수 있습니다. 그런 다음이 코드가 효과가 있습니다.

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

다행히도 액세스하려는 사이트의 루트 인증서가 curl 번들에 있습니다. 그렇지 않은 경우 사이트의 루트 인증서를 가져 와서 인증서 파일에 넣을 때까지 여전히 작동하지 않습니다.


.crt 파일은 어디에 있습니까? curl 웹 사이트에는 .pem 만 제공하는 링크가 없습니다.
vee

1
pem 파일은 같아야합니다. 이것이 게시되었을 때, 그들은 사이트에 인증서 번들의 두 가지 버전, 즉 pem과 crt를 가지고 있었고 방금 내 예제를 위해 crt 파일을 사용했습니다. 나중에 참조 할 수 있도록 crt 파일은 일반적으로 pem 파일로 이름이 바뀌며 Windows는 파일을 인증서 파일로 인식하도록 이름이 바뀝니다. 항상 그런 것은 아니지만,이 경우입니다. 필자는 현재 컬 사이트에있는 pem 파일을 사용하도록 예제를 업데이트합니다
elitechief21

4
유용한 함수가 stream_context_set_default있어서 매번 file_get_contents에 전달할 필요가 없습니다
Ken Koch

누구든지 이것을 작동 시켰습니까? 이 답변에 링크 된 cacert.pem 번들과 Comodo CA Root 인증서 (확인하려는 인증서의 CA 루트)로 시도했지만 항상 실패합니다.
zmippie

1
보안 관점에서 : cafile 스트림 컨텍스트 옵션 사용 옆에서 암호 스트림 컨텍스트 옵션에 허용 된 암호를 정의하고 취약한 것으로 알려진 SSL 버전을 금지하는 것이 매우 중요합니다. CRIME 공격 경로를 완화하려면 스트림 컨텍스트 옵션 disable_compression을 true로 설정하는 것이 좋습니다.
Josef Glatz

36

OpenSSL이 내 컴퓨터에 설치되어 있는지 확인한 다음 이것을 php.ini에 추가하여 문제를 해결했습니다.

openssl.cafile=/usr/local/etc/openssl/cert.pem

@Akhi 당신이 구글이라면 몇 가지 튜토리얼을 찾을 수있을 것입니다
andlin

2
IIS에서 PHP 7을 사용하는 동일한 방법을 사용하여 cert.pem파일을 다운로드 하고 다음과 php.ini같이 설정 했습니다.openssl.cafile=D:\Tools\GnuWin32\bin\cacert.pem
David Refoua

1
curl.haxx.se/docs/caextract.html 에서 PEM 파일을 다운로드했습니다 . 특정 gstatic.com URL이있는 Windows에서 문제가 해결되었습니다.
Jake

curl.haxx.se/docs/caextract.html 에서 PEM 파일을 다운로드하는 것이 Centos 7에서 작동하지 않았습니다. 주 인증서와 pkcs7을 연결하여 서버에 배치 한 다음 openssl.cafile 경로를 지정하여 번들 인증서를 작성했습니다. 정답과 방향은 +1입니다.
Arvind K.

번들을 생성 할 때 pkcs를 기본 인증으로 연결하기 전에 pkcs를 pem 파일로 변환하는 것을 잊지 마십시오
Arvind K.

22

다음과 같이 curl을 사용하는 사용자 정의 함수를 작성하여이 문제를 해결할 수 있습니다.

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;

}

그런 다음 https로 시작하는 URL을 호출 할 때마다 file_get_contents_curl대신 사용 file_get_contents하십시오.


이것은 나를 위해 일했습니다. 잘 했어 고마워!
Nick Green

11

나를 위해 PHP 5.6을 사용하고 있습니다. openssl 확장 기능을 활성화하고 Google지도를 호출하는 동안 api verify_peer make false 아래 코드가 작동합니다.

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

10

PHP 버전이 5 인 경우 터미널에 다음 명령을 입력하여 cURL을 설치하십시오.

sudo apt-get install php5-curl

9
이것은 절대적으로 아무 관련이 없습니다 cURL.
Andreas

2
PHP curl을 설치하는 것이 정답입니다! Mac 시스템의 경우 포트를 사용하고 있으며 명령은 다음과 같습니다.sudo port install php70-curl
hailong


6

아래 단계를 수행하면이 문제가 해결됩니다.

  1. 이 링크에서 CA 인증서를 다운로드하십시오. https://curl.haxx.se/ca/cacert.pem
  2. php.ini를 찾아서 엽니 다
  3. 를 찾아 curl.cainfo와 붙여 절대 경로 인증서를 다운로드 할 수 있습니다.curl.cainfo ="C:\wamp\htdocs\cert\cacert.pem"
  4. WAMP / XAMPP (apache 서버)를 다시 시작하십시오.
  5. 효과가있다!

희망이 도움이됩니다 !!


이게 안전합니까? 나는 쉬운 해결책을 좋아하지만 여기에 몇 가지 정보가 없습니다 ..
swisswiss

테스트를 위해 괜찮습니다
Geomorillo

5

동일한 문제가 발생하여 어디에서나 찾을 수있는 것이 없기 때문에 여기에 추가하고 싶었습니다 (예 : cacert.pem 파일 다운로드, php.ini에서 cafile 설정 등)

NGINX를 사용 중이고 SSL 인증서에 "중간 인증서"가 제공되는 경우 중간 인증서 파일을 기본 "mydomain.com.crt"파일과 결합해야합니다. Apache에는 중간 인증서에 특정한 설정이 있지만 NGINX는 그렇지 않으므로 일반 인증서와 동일한 파일 내에 있어야합니다.


4

이 오류의 이유는 PHP에 신뢰할 수있는 인증 기관 목록이 없기 때문입니다.

PHP 5.6 이상은 시스템이 신뢰하는 CA를 자동으로로드하려고합니다. 그 문제를 해결할 수 있습니다. 자세한 내용은 http://php.net/manual/en/migration56.openssl.php 를 참조하십시오.

이전 PHP 5.5은 정말 당신이 수동으로 각 요청의 컨텍스트에서 CA 번들, 당신은 당신의 코드를 주위에 뿌려하고 싶지 않은 일을 지정해야 제대로 때문에 설치 하드. 따라서 PHP 버전 <5.6의 경우 SSL 확인이 단순히 비활성화되도록 내 코드를 결정했습니다.

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

이것은 내 문제를 찾는 데 도움이되었습니다. PHP 5.6을 사용하고 있지만 위의 링크에 따라 cafile 컨텍스트 옵션을 사용하여 이전 CA 파일을 수동으로 지정한 오래된 API 클라이언트 라이브러리를 사용하고있었습니다. API 클라이언트 라이브러리에서 제거하면 문제가 해결되었습니다. 아마도 PHP는 OpenSSLs 신뢰할 수있는 번들을 사용하기 시작했습니다
Joe Lipson

4

XAMPP 및 OSX에서 PHP 7과 동일한 오류가 발생했습니다.

https : //.com/stackoverflow.com/에서 위에서 언급 한 답변 은 좋지만 문제를 완전히 해결하지 못했습니다. file_get_contents ()가 다시 작동하도록 완전한 인증서 체인을 제공해야했습니다. 그것이 내가 한 방법입니다.

루트 / 중간 인증서 받기

우선 루트 중간 인증서가 무엇인지 파악해야했습니다 .

가장 편리한 방법은 ssl-shopper 와 같은 온라인 인증서 도구입니다.

거기에서 세 가지 인증서, 하나는 서버 인증서와 두 개의 체인 인증서를 발견했습니다 (하나는 루트이고 다른 하나는 중간 인증서입니다).

내가해야 할 일은 인터넷을 검색하여 둘 다 검색하는 것입니다. 제 경우에는 이것이 루트입니다.

thawte DV SSL SHA256 CA

그리고 그것은 그의 URL thawte.com으로 연결됩니다 . 그래서 나는이 인증서를 텍스트 파일에 넣고 중간에 대해서도 똑같이했습니다. 끝난.

호스트 인증서 받기

다음으로 서버 인증서를 다운로드해야합니다. Linux 또는 OS X에서는 openssl을 사용하여 수행 할 수 있습니다.

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

이제 모두 함께 가져와

이제 모든 파일을 하나의 파일로 병합하십시오. (아마도 그것들을 하나의 폴더에 넣는 것이 좋을 것입니다. 나는 그것들을 하나의 파일로 병합했습니다). 다음과 같이 할 수 있습니다 :

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

PHP에게 체인을 찾을 수있는 곳을 알려주세요

이 편리한 함수 openssl_get_cert_locations ()가 있는데, PHP가 인증서 파일을 찾는 위치를 알려줍니다. 그리고이 매개 변수는 file_get_contents ()에게 인증서 파일을 찾을 위치를 알려줍니다. 두 가지 방법 모두 효과가있을 것입니다. 나는 매개 변수 방식을 선호했습니다. (위에서 언급 한 솔루션과 비교).

이제 이것은 내 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));

그게 다야. file_get_contents ()가 다시 작동합니다. CURL이없고 보안 결함이없는 경우.


파일이 chain.pem뭐야? 이것 chain.crt입니까?
Dr.X

실제 인증서에 대한 것은 chain.crt가 아닙니다. 중간 인증서 인 인증서 체인 일 경우 목록입니다. 반드시 필요한 것은 아닙니다. SSL 인증서 검사기를 사용하여 필요한지 확인하십시오. 그렇다면 인증서 발급자 이름 + "체인"또는 "중간"이라는 용어를 검색하여 올바른 파일을 찾을 수 있습니다.
nr

4

PHP를 php5.6으로 업데이트 한 후 centOS 에서이 문제의 희생양을 얻은 후에 나에게 맞는 해결책을 찾았습니다.

이것을 사용하여 인증서가 기본적으로 배치되도록 올바른 디렉토리를 얻으십시오.

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

그런 다음 이것을 사용하여 인증서를 가져 와서 위 코드에서 찾은 기본 위치에 넣으십시오.

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

3

" https : // localhost / ..."파일 을 열려고하는 자체 서명 된 인증서가있는 내 개발자 컴퓨터 (Windows의 PHP 7, xampp)에서 동일한 SSL 문제가 있었습니다. 분명히 루트 인증서 어셈블리 (cacert.pem)가 작동하지 않았습니다. 방금 다운로드 한 cacert.pem의 apache server.crt-File에서 코드를 수동으로 복사하고 php.ini의 openssl.cafile = path / to / cacert.pem 항목을 수행했습니다.


2

시도해야 할 또 다른 사항은 여기에ca-certificates 설명 된대로 다시 설치 하는 입니다.

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

또 다른 시도는 여기에 설명 된대로 문제의 사이트 인증서를 명시 적으로 허용하는 것입니다 (특히 사이트가 자신의 서버이고 이미 .pem에 도달 한 경우).

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

CentOS 6에서 PHP 5.6으로 업그레이드 한 후이 업데이트가 필요할 수있는 cheapsslsecurity 인증서가있는 서버 자체에 액세스하려고 시도한 후이 정확한 SO 오류가 발생했지만 대신 letencrypt 인증서를 설치하고 위의 두 단계를 수행했습니다. 트릭. 왜 두 번째 단계가 필요한지 모르겠습니다.


유용한 명령

openssl 버전보기 :

# openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013

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

1

비슷한 오류에 대해

[2017 년 5 월 11 일 19:19:13 America / Chicago] PHP 경고 : file_get_contents () : SSL 작업이 코드 1에서 실패했습니다. OpenSSL 오류 메시지 : error : 14090086 : SSL 루틴 : ssl3_get_server_certificate : certificate 확인 실패

openssl이 참조하는 인증서 및 디렉토리의 권한을 점검 했습니까?

당신은 이것을 할 수 있습니다

var_dump(openssl_get_cert_locations());

이와 비슷한 것을 얻으려면

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

이 문제는 "certs"폴더에 700 개의 권한이 있고 755 개의 권한이 있어야한다는 것을 깨달을 때까지 한동안 실망했습니다. 이 폴더는 키 폴더가 아니라 인증서입니다. SSL 권한에서이 링크를 읽는 것이 좋습니다 .

내가 한 번

chmod 755 certs

적어도 어쨌든 문제는 해결되었습니다.


예이! CHMOD도 제 문제였습니다 ;-) 감사합니다
RA.

0

사용할 때 다른 보안 페이지에 대한 동일한 문제가 있었다 wget거나file_get_contents . Curl과 PHP-Curl을 설치하는 많은 연구 (이 질문에 대한 답변 중 일부 포함)는 간단한 해결책으로 나타났습니다.

Curl 및 PHP-Curl 애드온을 설치 한 다음 Apache를 다시 시작하십시오.

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

모두 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.