PostgreSQL で暗号化 – pgcrypto を使ってみた

こんにちは。サイオステクノロジー OSS サポート担当 W です。

今回は PostgreSQL の Contrib モジュールとして利用できる pgcrypto を紹介します。マイナンバー法では不必要に個人番号が参照できてはならないとされています。暗号化は鍵の管理や復号処理の権限確認をしっかり行わないとあまり意味がなかったりするので、注意が必要ですが pgcrypto は個人番号の様に高い機密性が必要とされるデータを取り扱う場合に役立ちます。

本記事の執筆には Fedora 22 とそのパッケージの PostgreSQL (postgresql-9.4.4-1.fc22.x86_64) を利用しています。

pgcrypto のインストール

pgcrypto のインストールと使い方は lets.postgresql.jp で詳しく解説 (https://lets.postgresql.jp/documents/technical/contrib/pgcrypto/) されています。詳しくはそちらを参照ください。

RPM 系の Linux であれば PostgreSQL の contrib パッケージをインストールすると pgcrypto もインストールされます。

 # yum install postgresql-contrib

でインストール可能です。
Contrib モジュールの機能を有効にするには関数などを登録しなければなりません。

/usr/share/pgsql/extension/pgcrypto--1.1.sql

などのファイルに関数の登録設定が記載されています。古いシステムの場合

psql -f /usr/share/pgsql/extension/pgcrypto.sql [dbname]

などとしてデータベースに登録します。Fedora 22 などの新しいシステムの場合はデータベースにスーパーユーザとして接続し

CREATE EXTENSION pgcrypto;

を実行します。これで pgcrypt が利用可能になります。

pgcrypto がサポートする関数一覧

==汎用ハッシュ関数==

digest(data text, type text) returns bytea
digest(data bytea, type text) returns bytea

type (md5、sha1、sha224、sha256、sha384、sha512) で指定された data のハッシュ値を bytea (バイナリ) として返す。

==HMAC 関数==

hmac(data text, key text, type text) returns bytea
hmac(data bytea, key text, type text) returns bytea

key をキーとする data の MAC (Message Authentication Code) ハッシュを返す。type は digest 関数と同じ。この関数はメッセージの改ざんが無いか確認する為に用います。ハッシュ値を計算する際に key も一緒にハッシュ化するので、data が同じでも key を知っている場合しか同じハッシュ値を生成できません。

==パスワード関数==

crypt(password text, salt text) returns text
gen_salt(type text [, iter_count integer ]) returns text

crypt は標準 crypt 関数を利用したパスワードのハッシュ値を計算します。gen_salt はランダムな salt を生成します。salt には同じパスワードが利用されている場合でもハッシュ値を単純比較しただけでは判らなくする効果があります。

標準 crypt 関数はパスワードハッシュのプレフィックスでハッシュ関数を判別します。以下に md5 と bf (Blowfish) の例を記載します。利用できるハッシュ関数に xdes, des もありますが、パスワード長が 8文字なので利用すべきではありません。

postgres@[local]# select gen_salt('md5');
┌─────────────┐
│  gen_salt   │
├─────────────┤
│ $1$L3jVU.Bp │
└─────────────┘
(1 行)

時間: 0.448 ms
postgres@[local]# select gen_salt('bf');
┌───────────────────────────────┐
│           gen_salt            │
├───────────────────────────────┤
│ $2a$06$1KggITpHejtPGxf68zOD8u │
└───────────────────────────────┘
(1 行)

bf のパスワード長は 72文字まで、md5 のパスワード長は無制限である点は良いのですが、md5 はパスワードハッシュ関数として速すぎることと適用回数 (rounds) が指定できないので不適切です。PostgreSQL の crypt 関数で実用として利用できるハッシュ関数は現状では bf のみです。

==対称鍵暗号化/復号関数==

pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea
pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea
pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text
pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea

これらの関数はそれぞれ、data を psw で指定されたキーで暗号化/復号します。option で利用することが多い物は cipher-algo (暗号アルゴリズム) と compress-algo (圧縮アルゴリズム) でしょう。

cipher-algo: bf, aes128 (デフォルト), aes192, aes256, 3des, cast5
compress-algo: 0 (非圧縮), 1 (ZIP圧縮), 2 (zlib圧縮)

オプションの指定例

pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')

AES256 をサポートしているので、それより弱い AES128 を利用する意味はあまりありません。互換性などの理由がなければ AES256 を利用すべきです。

==公開鍵暗号化/復号関数==

pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea
pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea
pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) returns text
pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]]) returns bytea

PGP 公開鍵を利用した暗号化と復号を行います。暗号化する場合、key には公開 PGP 鍵を渡します。復号する場合、key には暗号化時に利用した PGP 公開鍵に対応する PGP 秘密鍵を渡します。

==PGP キーの ID 取得関数==

pgp_key_id(bytea) returns text

PGP 公開鍵または秘密鍵のキー ID を取得します。

==PGP の armor 変換/復号関数==

armor(data bytea) returns text
dearmor(data text) returns bytea

言葉で説明しても解りづらいので、出力例を記載します。

postgres@[local] # select armor('abcedf'::bytea);
┌─────────────────────────────┐
│            armor            │
├─────────────────────────────┤
│ -----BEGIN PGP MESSAGE-----↵│
│                            ↵│
│ YWJjZWRm                   ↵│
│ =0C6g                      ↵│
│ -----END PGP MESSAGE-----  ↵│
│                             │
└─────────────────────────────┘

==一般的な暗号化/復号関数==

encrypt(data bytea, key bytea, type text) returns bytea
decrypt(data bytea, key bytea, type text) returns bytea

encrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea

encrypt/decrypt は IV (初期化ベクター) 無しで暗号化/復号します。IV が無いので同じ data なら同じ暗号化データが生成されます。encrypt_iv/decrypt_iv は IV を利用して暗号化/復号します。pgp_sym_* 関数の方が優れているので、特に理由がなければ pgp_sym_* 関数を利用する方が良いです。

==ランダム関数==

gen_random_bytes(count integer) returns bytea
gen_random_uuid() returns uuid

gen_random_bytes 関数は count で指定したバイナリのランダムデータを返します。get_random_uuid 関数はランダムなバージョン 4UUID を返します。

pgcrypto を利用した暗号利用の注意点

データベースのデータだけ暗号化しても、復号済みのデータがネットワーク上で盗聴されては意味がありません。SSL (TLS) を利用するなど、ネットワークも適切に暗号を利用する必要があります。

pgcrypto を利用する場合、データベース管理者/システム管理者は暗号鍵にアクセスできます。つまり、暗号化されたデータを復号して参照可能です。データベース管理者/システム管理者を信頼できない場合は、データベースではなくアプリケーションで暗号を利用する必要があります。

pgcrypto の実装はサイドチャネル攻撃 (暗号アルゴリズムの脆弱性ではなく、処理時間などの副作用を利用して攻撃する手法) に対して脆弱です。

インデックスは「暗号化」されたデータがインデックスされます。つまりインデックスは通常通りに機能しません。インデックスが必要なデータの暗号化には注意が必要です。

実際に pgcrypto を利用する場合、SQL 文やビューに鍵が記載されてしまうのはあまり好ましくありません。postgresql.conf の “custom_variable_classes” を利用すると便利です。

postgresql.conf に記載

 # my_app クラスを定義 - PostgreSQL 9.3 以降はクラス定義は必要ない
 custom_variable_classes = 'my_app'
 # my_app.passwd に 'secret' というパスワードを指定
 my_app.passwd='secret'

custom_variable_class で定義した設定は current_setting 関数で取得できます。

SELECT current_setting('my_app.passwd')

暗号化/復号にこれを利用すると

暗号化したデータを即復号する例

SELECT pgp_sym_decrypt(pgp_sym_encrypt('abcdedf', current_setting('my_app.passwd')), current_setting('my_app.passwd'));

などとして利用できます。

まとめ

pgcrypto の導入と利用はとても簡単です。多少、注意しなければならない点があります。特に機密性が高いデータを保存する場合に利用すると良いです。

参考URL

https://www.postgresql.jp/document/9.4/html/pgcrypto.html
https://lets.postgresql.jp/documents/technical/contrib/pgcrypto/

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

1人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です