タグ: 暗号

PHP用のCookieセッションセーブハンドラー

JWTが凄い使われ方をしているようなので、可能な限りマシなCookieベースのセッションセーブハンドラーを書きました。メリットは

  • サーバー側のリソース(DBやファイルなど)を使わないので簡単にスケールする

ことにあります。

しかし、Cookieの保存は大きく分けて3つの問題があります。

  1. ネットワークが不安定だとデータが失われる場合がある(ネットワーク接続の問題)
  2. ロックがないのでデータが失われる場合がある(サーバー側の問題)
  3. 更新のタイミングによりデータが失われる場合がある(クライアント側の問題)

※ この問題はこのCookieを利用したセーブハンドラの問題ではなく、クッキー等のクライアント側にセッションデータを持たせるセッション管理の仕組み全般に存在します。

このハンドラーはデータ改ざんを検出するようになっており、Cookieの問題により改ざん攻撃と誤検出する可能性があります。

1、2は一般に広く認知されている問題でしょう。この場合はほぼ攻撃と誤検出されないと思われます。攻撃ではないのに攻撃と誤検出するのはほぼ3のケースだと考えられます。壊れ方によっては3ケースでも攻撃とは検出せず、仕様として単純にクッキーをリセットする場合もあります。タイミングによるデータ損失が100%攻撃と誤検出されるのではありません。

調べたのはしばらく前になりますが、タイミングによって失われるケースも完全に無視してよい程度ではありませんでした。これはブラウザのクッキー保存の実装コードの問題と考えられます。初期のバージョンはこの問題に対する緩和対策をしたコードにしていました。今は対タンパー性を上げる為にほぼ完全にバリデーションするコードになっています。(詳しくはコメント欄を参照)

どちらも一長一短で何とも言えないです。更新タイミングによるデータ損失が、どのブラウザで起きるのか、どのような使い方をすると起きるのか、どの程度あるのか、分らないので攻撃と誤検出するケースがある場合には教えていただけると助かります。※

※ 現コードはHMACで使用するクッキーをバリデーションしているので、更新タイミングによるデータ損失があると攻撃と誤検出します。きっちり正しく動作するコード ≠ きっちり厳格にバリデーションするコード、となる数少ないタイプのコードになります。ブラウザがRDBMSのトランザクションのように保存すれば済む話ですが。

もっと読む

PHPのserialize()/unserialize()を安全に利用する方法

serialize()でシリアライズしたデータを外部に送信/保存1し、それをunserialize()すると危険です。

アンシリアライズは複雑なメモリ操作が必要で、PHPに限らず、何度もメモリ破壊攻撃の脆弱性が見つかっています。このため、外部入力データのアンシリアライズは行うべきではありません。現在のPHPプロジェクトでは、RubyやPythonと同じく、アンシリアライズのメモリ問題を”脆弱性として取り扱わないことになっています。

しかし、外部データでもunserialize()を安全に利用する方法はあります。

もっと読む

暗号学的ハッシュを安全に使うには?

2017年2月にGoogleがSHA1ハッシュの衝突に成功した、とアナウンスしました。1

暗号学的に安全なハッシュ関数な場合、SHA2-256を使っていると思います。SHA3が利用可能になのでSHA3を利用している場合も多いと思います。SHA2もSHA3も暗号学的ハッシュ関数です。ざっくりとこれらのハッシュ関数を安全に使う方法を紹介します。

もっと読む

HKDF, HMACなどのハッシュ関数を使う場合に知っておくべきFS/PFS

PHPにHKDF関数、hash_hkdf()が追加されましたが、そのシグニチャは褒められるモノではありません。

出鱈目なシグニチャのhash_hkdf関数を安全に使う方法

hash_hkdf()が脆弱なAPI仕様になってしまった主な原因は、開発者がハッシュ関数を利用して鍵を導出する場合に知っておくべきFS/PFSの概念を知らなかったことにあります。(秘密鍵のセキュリティ維持にSaltが必須であるとの理解が足りなかったことも原因)

FS/PFSはハッシュ関数を利用した安全な鍵導出に必須の知識です。簡単な概念なので直ぐに理解できると思います。

もっと読む

文字列(ハッシュ)の安全な比較方法 – hash_equals

映画などでPINコードを一桁づつ解析してドアを開錠する、といったシーンがあると思います。こんなのは”映画の世界だけ”と思っている方も多いと思います。しかし、タイミング攻撃を利用すると”実際にこれと全く同じ方法”で鍵となる情報を解析できます。

タイミング攻撃とはサイドチャネル攻撃の一種で、鍵情報を比較的簡単に解析する方法です。PHP 5.6からはタイミング攻撃に脆弱でない比較方法を提供しています。

結論から書くと、秘密の文字列を比較する場合

if ($secret_str !== $user_input) {
  // ユーザー提供の秘密情報不一致
  die('不正なアクセスです');
}

といった通常の文字列は比較は危険です。

if (!hash_equals($secret_st, $user_input)) {
  die('不正なアクセスです');
}

上記のようにhash_equals関数を利用しなければなりません。

もっと読む

HMACを利用した安全なAPIキーの送受信

Webアプリケーションの機能をサービスとして提供する場合、ランダムな値の秘密のAPIキーを鍵とすることが多いです。

// 何らかのAPIを呼び出す
http://example.com/api/v2/get_something?api_key=qwertyuiop

シンプルな方法で使いやすいですが、鍵となるAPIキーをそのまま使っているので鍵が漏洩する可能性があります。HMACやHKDFを使うと鍵となるAPIキーを直接使わないでAPIへのアクセスを認証できます。

もっと読む

hash_hmac()の使い方

HMACの応用的な使い方をここ数本のブログで書いてきましたが、HMACの基本的な使い方を紹介していませんでした。リクエストパラメーターを安全に検証/バリデーションする方法を例に紹介します。unserialize()を安全に利用する利用例にもなります。

参考:

HMACハッシュの使い方のまとめ

もっと読む

ハッシュ(HMAC)を使って弱い鍵を強い鍵に変える方法

既存の鍵から別の鍵を導出する方式としてはHKDF(RFC 5869)があります。AES用に弱い鍵から強い鍵を作るにはHKDFでなくてもHMACで十分です。実際、HKDFはHMACを組み合わせて鍵を導出しているだけなので、ここで紹介するHMACのみの鍵導出と同等です。

※ PHP 7.2からHKDFを実装したhash_hkdf()を使えます。hash_hkdf()が利用できる場合はシンプルにhash_hkdf()を使うと良いです。

もっと読む