PHP 5.6.x における OpenSSL 関連の変更

ストリームラッパーが、SSL/TLS を使っている場合のピア証明書とホスト名の検証にデフォルトで対応

暗号化されたすべてのクライアントストリームで、ピア検証がデフォルトで有効になりました。 デフォルトでは、OpenSSL のデフォルト CA バンドルを使ってピア証明書を検証します。 たいていの場合は、正しい SSL 証明書を持つサーバーと通信するならこれを変更する必要はありません。 OpenSSL が、よく知られた CA バンドルを使うように設定されているからです。

デフォルトの CA バンドルを上書きすることもできます。 openssl.cafile あるいは openssl.capath を設定すればグローバルに変更でき、コンテキストオプション cafile あるいは capath を使えばリクエスト単位で変更できます。

一般的にはおすすめできませんが、 コンテキストオプション verify_peerfalse にしてリクエストでのピア証明書の検証を無効化することもできます。 また同じく、ピア名の検証も、コンテキストオプション verify_peer_namefalse にすれば無効化できます。

証明書のフィンガープリント

証明書のフィンガープリントの抽出と、その検証に対応しました。 新たに追加された openssl_x509_fingerprint() 関数が、 X.509 証明書からフィンガープリントを抽出します。また、 SSL ストリームコンテキスト に二つのオプションが追加されました。 capture_peer_cert はピアの X.509 証明書を取り込み、 peer_fingerprint はピアの証明書が指定したフィンガープリントにマッチすることを確かめます。

デフォルトの暗号の更新

PHP がデフォルトで使う暗号が更新され、 » Mozilla cipher recommendations に基づいた、よりセキュアなリストになりました。 リストにない例外は、anonymous Diffie-Hellman ciphers と RC4 です。

新しい定数 OPENSSL_DEFAULT_STREAM_CIPHERS で、この一覧にアクセスできます。 また、(過去のバージョンの PHP のように) 上書きもできます。上書きをするには、コンテキストオプション ciphers を設定します。

デフォルトでの圧縮の無効化

SSL/TLS の圧縮が、デフォルトで無効になりました。これは、CRIME 攻撃への対応です。 PHP 5.4.13 で新たにコンテキストオプション disable_compression が加わり、圧縮を無効にできるようになりました。そのデフォルト値が true (つまり圧縮が無効になっている) に変わったのです。

サーバーの暗号の優先が可能に

SSL コンテキストオプション honor_cipher_order が追加されました。これによって、暗号化されたストリームサーバーでの BEAST 脆弱性を除去するために、サーバーの暗号をクライアントよりも優先させられるようになります。

ネゴシエート済みのプロトコルと暗号へのアクセス

暗号化ストリームでネゴシエート済みのプロトコルと暗号へのアクセスが、 stream_get_meta_data() あるいは stream_context_get_options() でできるようになりました。 これは、SSL コンテキストオプション capture_session_metatrue になっている場合に有効です。

<?php
$ctx
= stream_context_create(['ssl' => [
'capture_session_meta' => TRUE
]]);

$html = file_get_contents('https://google.com/', FALSE, $ctx);
$meta = stream_context_get_options($ctx)['ssl']['session_meta'];
var_dump($meta);
?>

上の例の出力は以下となります。

array(4) {
  ["protocol"]=>
  string(5) "TLSv1"
  ["cipher_name"]=>
  string(20) "ECDHE-RSA-AES128-SHA"
  ["cipher_bits"]=>
  int(128)
  ["cipher_version"]=>
  string(11) "TLSv1/SSLv3"
}

暗号化ストリームサーバーでの前方秘匿性に関する新たなオプション

暗号化クライアントストリームはすでに前方秘匿性に対応しています。 というのも、一般に前方秘匿性は、サーバーが制御するものだからです。 PHP の暗号化サーバーストリームが使っている証明書は前方秘匿性に対応しており、 何もしなくても有効になっています。 しかし、前方秘匿性に関してより決め細やかな制御をしたり、 互換性に関する問題に対応したりするために、 新たな SSL コンテキストオプションがいくつか追加されました。

ecdh_curve

このオプションで、ECDH 暗号で使う曲線を選択できるようになります。 省略した場合は prime256v1 を使います。

dh_param

Diffie-Hellman 鍵交換用のパラメータを含むファイルへのパス。 このファイルは、たとえば以下のようなコマンドで生成したものです。

openssl dhparam -out /path/to/my/certs/dh-2048.pem 2048
single_dh_use

true にすると、Diffie-Hellman パラメータを使うときに、 新たな鍵ペアを作成します。これにより、前方秘匿性が向上します。

single_ecdh_use

true にすると、ECDH 暗号スイートでのネゴシエート時に 新たな鍵ペアを作成します。これにより、前方秘匿性が向上します。

SSL/TLS のバージョンの選択

SSL や TLS の、特定のバージョンを指定できるようになりました。SSL コンテキストオプション crypto_method を利用するか、 あるいはストリームラッパーの作成時に (stream_socket_client() あるいは stream_socket_server() を呼んで) トランスポートを指定します。

SSL コンテキストオプション crypto_method には、 stream_socket_enable_crypto() 関数の crypto_type と同様に、 許可するプロトコルをビットマスクで指定します。

プロトコルのバージョンと、対応するオプション
プロトコル クライアントのフラグ サーバーのフラグ トランスポート
任意のバージョンの TLS あるいは SSL STREAM_CRYPTO_METHOD_ANY_CLIENT STREAM_CRYPTO_METHOD_ANY_SERVER ssl://
任意のバージョンの TLS STREAM_CRYPTO_METHOD_TLS_CLIENT STREAM_CRYPTO_METHOD_TLS_SERVER tls://
TLS 1.0 STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT STREAM_CRYPTO_METHOD_TLSv1_0_SERVER tlsv1.0://
TLS 1.1 STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT STREAM_CRYPTO_METHOD_TLSv1_1_SERVER tlsv1.1://
TLS 1.2 STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT STREAM_CRYPTO_METHOD_TLSv1_2_SERVER tlsv1.2://
SSL 3 STREAM_CRYPTO_METHOD_SSLv3_CLIENT STREAM_CRYPTO_METHOD_SSLv3_SERVER sslv3://
<?php

// file_get_contents() を使うには TLS 1.0 以降が必要です
$ctx = stream_context_create([
'ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_TLS_CLIENT,
],
]);
$html = file_get_contents('https://google.com/', false, $ctx);

// TLS 1.1 あるいは 1.2 が必要です
$ctx = stream_context_create([
'ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT |
STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
],
]);
$html = file_get_contents('https://google.com/', false, $ctx);

// 接続時に tlsv1.2:// ストリームソケットトランスポートを利用します
$sock = stream_socket_client('tlsv1.2://google.com:443/');

?>

openssl_get_cert_locations() の追加

openssl_get_cert_locations() 関数が追加されました。 この関数は、PHP が CA バンドルを探す際のデフォルトの場所を返します。

<?php
var_dump
(openssl_get_cert_locations());
?>

上の例の出力は以下となります。

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

SPKI のサポート

署名された公開鍵およびチャレンジ (SPKAC) の生成や抽出、そして検証のサポートが追加されました。 新たに追加された関数 openssl_spki_new()openssl_spki_verify()openssl_spki_export_challenge() そして openssl_spki_export() で、 SPKAC が HTML5 の KeyGen 要素から生成した PEM 公開鍵とチャレンジの作成、検証、エクスポートを行います。

openssl_spki_new

秘密鍵とチャレンジ文字列、そしてハッシュアルゴリズムを指定して、 新しい SPKAC を生成します。

<?php
$pkey
= openssl_pkey_new();
openssl_pkey_export($pkey, 'secret passphrase');

$spkac = openssl_spki_new($pkey, 'challenge string');
?>

上の例の出力は以下となります。

SPKAC=MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkYjViMzYxMTktNjY5YS00ZDljLWEyYzctMGZjNGFhMjVlMmE2MA0GCSqGSIb3DQEBAwUAA4GBAF7hu0ifzmjonhAak2FhhBRsKFDzXdKIkrWxVNe8e0bZzMrWOxFM/rqBgeH3/gtOUDRS5Fnzyq425UsTYbjfiKzxGeCYCQJb1KJ2V5Ij/mIJHZr53WYEXHQTNMGR8RPm7IxwVXVSHIgAfXsXZ9IXNbFbcaLRiSTr9/N4U+MXUWL7
openssl_spki_verify

指定した SPKAC を検証します。

<?php
$pkey
= openssl_pkey_new();
openssl_pkey_export($pkey, 'secret passphrase');

$spkac = openssl_spki_new($pkey, 'challenge string');
var_dump(openssl_spki_verify($spkac));
?>
openssl_spki_export_challenge

指定した SPKAC から、関連付けられたチャレンジをエクスポートします。

<?php
$pkey
= openssl_pkey_new();
openssl_pkey_export($pkey, 'secret passphrase');

$spkac = openssl_spki_new($pkey, 'challenge string');
$challenge = openssl_spki_export_challenge($spkac);
echo
$challenge;
?>

上の例の出力は以下となります。

challenge string
openssl_spki_export

PEM フォーマットされた RSA 公開鍵を、SPKAC からエクスポートします。

<?php
$pkey
= openssl_pkey_new();
openssl_pkey_export($pkey, 'secret passphrase');

$spkac = openssl_spki_new($pkey, 'challenge string');
echo
openssl_spki_export($spkac);
?>

上の例の出力は以下となります。

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcvQh9SKOPv4DwI8LwSaFx02h7
l9QCiDs6sF2GfsSTEUG61SnjQ/v4uJiLKBgbVOagj9rkSCwtTez23ATPeGaBj2Zg
ipv+tv5IXyqUP8ropXJQ5ELtbXPUN/gvw7cO5EbPHr/7eMhbpw8Gl+AfWxW5hLW8
MGw/+AwwjHBOwong/QIDAQAB
-----END PUBLIC KEY-----
add a note add a note

User Contributed Notes 1 note

up
14
Victor
9 years ago
To get back to the "old behavior" with self-signed ("snakeoil") certificates to possibly incorrect name, you need to set both options.

<?php
            $streamContext
= stream_context_create([
               
'ssl' => [
                   
'verify_peer'      => false,
                   
'verify_peer_name' => false
               
]
            ]);
           
$contents = file_get_contents('https://url', false, $streamContext);
?>
To Top