PHP業界の重鎮 廣川類氏のコラム第三回 次のPHPはどうなる? バージョン7.2でここが変わる


PHP 7.2の新機能: クラスメソッドの型宣言の省略

11月9日にリリース候補版(Release Candidate, RC)であるPHP 7.2.0 RC6が公開されました。特に大きな問題が見つからない限り、このRC6版が最後のRCとなり、予定通り11月30日にPHP 7.2.0が正式公開される予定です。以下、前号に引き続きPHP 7.2の変更点を紹介します。

PHP 7以降、スカラー型の型宣言(タイプヒンティング)が可能となっています。既存の共通ライブラリを型宣言を行うように更新した場合、どうなるでしょうか?例えば、以下のような簡単な例を考えてみましょう。

まず、共有ライブラリ(lib.php)にクラスBaseが定義されているとします。関数booに型宣言を追加したと想定します。

<?php // 共有ライブラリ

class Base {

public function boo(string $s) {}

}

従来からこのライブラリを使ってクラスFooを定義していたとします。

<?php

require_once ‘lib.php’;

class Foo extends Base {

public function boo($s) {}

}

このスクリプトfoo.phpを7.1までのバージョンのPHPで実行すると、以下のような警告が発生します。

Warning: Declaration of Foo::boo($s) should be compatible with Base::boo(string $s) in foo.php on line 3

これは、派生クラスFooにおいて基底クラスBaseのメソッドbooで定義される型宣言が省略されているためです。PHP 7.2以降では、このように型宣言を省略した場合でも警告が発生しないようになります。一般に共有ライブラリを更新した際に、これを使用するコードで定義された派生クラスを全て更新するのは手間がかかります。上記の動作となったことで、スクリプト内のクラスに段階的に型宣言を追加し、更新することが可能となります。

PHP 7.2の新機能: password_hash関数へのArgon2導入

Webアプリケーションでは、ユーザアクセス認証用などに使用されるパスワードを保存することが必要となるケースが度々あります。この際、パスワードをそのまま保存しておくのは危険ですので、パスワードのハッシュ値を保存しておきます。認証を行う際には、ユーザに入力されたパスワードのハッシュ値を計算して、保存されたハッシュ値と照合することで認証の可否を決定します。過去のWebアプリケーションでは、ハッシュ値を計算するハッシュ関数としてmd5()やsha1()関数が使用されていましたが、昨今の技術の進歩により容易に解読されてしまうリスクがあります。また、タイミング攻撃と呼ばれる攻撃により、パスワード文字列を絞り込まれるリスクもあります。

PHP 5.5以降、PHPではパスワード用ハッシュ値を安全に生成する関数password_hash()関数が定義されました。照合を行う際には、password_verify()関数を使用します。password_hash()関数では、パスワード保存に適した最新のハッシュ関数が使用されることとなっており、技術の進歩にも適用できます。PHP 7.1までは、Bcrypt(Blowfish)がハッシュ値生成アルゴリズムとして使用されています。また、password_verify()関数は、タイミング攻撃に対応し、安全にパスワード認証を行うことができます。

PHP 7.2では、ハッシュ値生成アルゴリズムとしてPassword Hashing Competition 2015の勝者であるArgon2が使用できるようになりました。パスワード用ハッシュ値生成関数では、ハッシュ生成にかかる計算コストをパラメータとして指定できます。これは、総当たり攻撃のような攻撃をやりにくくするためのものです。Webアプリケーション側の負荷も上がりますが、保護すべき情報の重要度に応じて適切に設定することで、相対的に攻撃側の負荷を高めることができます。Bcryptでは、コストを指定するパラメータは1種類のみでしたが、Argon2では3種類のパラメータを指定でき、より柔軟にコストを指定することができます。以下にサンプルコードを示します。Argon2を使用する場合、パスワード生成アルゴリズムとして第二引数にPHP定数PASSWORD_ARGON2Iを指定します。また、オプション引数として第三引数に配列でコストパラメータを指定します。コストパラメータは、メモリコスト、時間コスト、並列度(スレッド)の3種類が指定できます。ここでは、メモリコストに2^17(2の17乗)、時間コストに4、並列度に4を指定しています。これらの値は、保護すべき情報の重要度とサーバ負荷を考慮して、適切に設定します。

 

<?php

$opt= [‘memory_cost’=>1<<17,’time_cost’=>4,’threads’=>2];

$hash = password_hash(‘password’,PASSWORD_ARGON2I,$opt);

 

生成されるハッシュ文字列は、例えば、以下のようになります。ハッシュ文字列自体にアルゴリズムやオプションなどのパラメータが埋め込まれているため、パスワード認証を行う際には、オプションの引数を必要とせず、ハッシュ文字列のみで認証を行うことができます。

 

PHP 7.2の新機能:Sodiumライブラリの標準化

PHP 7.2では、従来標準エクステンションレポジトリPECLで提供されていた暗号ライブラリLibsodiumの機能がPHPコアに標準的に組み込まれました。Libsodiumは、クロスプラットフォーム、複数のプログラミング言語で使用可能な暗号ライブラリで、暗号/復号、署名、パスワード用ハッシュ生成などの機能を有しています。多くのプログラミング言語では、標準の暗号関数としてOpenSSLをサポートしていますが、近年の暗号技術の進歩に対応していないため、強度が低い暗号の誤った使い方を誘発し、サイドチャネル攻撃などを受けやすくなっています。Libsodiumは標準的な設定で安全性が高い暗号APIを提供しており、サイドチャネル攻撃に対応し、かつ、最近のCPUの命令に対応して高速に動作するのが特徴です。

暗号による情報伝送の例として、認証付き暗号(AEAD)の仕様例を示します。認証付き暗号は、データの秘匿性に加えて、伝送経路におけるデータ改ざん等の検知に対応し、完全性・認証性を有する暗号の仕組みです。ここでは、認証付き暗号のアルゴリズムとして、高速かつ強力な対称暗号であるChaCha20-Poly1305を使用します。暗号化方式としてのChaCha20-Poly1305はRFC7539で規格化され、また、TLSの暗号化方式としてRFC7905で仕様化されています。

 

まず、以下の関数であらかじめ共通鍵の生成を行い、鍵交換等により暗号文を送信する側と受信する側で共有します。

$key = sodium_crypto_aead_chacha20poly1305_keygen();

 

次に暗号化の処理を行います。暗号化対象のメッセージ文$msgとオプションで付加データを用意します。乱数として$nonceを生成した後、sodium_crypto_aead_chacha20poly1305_encrypt()関数で暗号化を行います。

 

$msg = “this is secret message.”; // 暗号化対象のメッセージの例

$ad = “public message.”;        // 付加データ(オプション)

$nonce = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES);

$ciphertext = sodium_crypto_aead_chacha20poly1305_encrypt($msg, $ad, $nonce, $key);

復号は、sodium_crypto_aead_chacha20poly1305_decrypt()関数を使用します。

 

echo sodium_crypto_aead_chacha20poly1305_decrypt($ciphertext, $ad, $nonce, $key);

 

libsodiumによる暗号化機能の強化は、PHP 7.2の目玉です。本バージョンでLibsodiumをコアに組み込むことにより、「PHPは近代的な暗号機能を標準サポートする最初のプログラミング言語になる」と言われています。暗号化と聞くととっつきにくい印象を受けますが、暗号化APIが標準的にサポートされることで、PHPはより使いやすく実用的な言語になっていくと思われます。


Post Author: 廣川類

廣川類

1996年からPHPを使い始め、マニュアルの翻訳や国際化などの活動に携わる。
PHPユーザ会の設立メンバーの一人。著書に「PHP徹底攻略」(ソフトバンクパブリッシング刊)などがある。