2015年6月6日

PostgreSQLの透過的暗号化(TDE)モジュールを使ってみる

先日、NECさんからPostgreSQLの暗号化モジュール「Transparent Data Encryption for PostgreSQL Free Edition」がGPLv3ライセンスのOSSとしてリリースされました。
多くの方がご存じの通り、データベースのセキュリティは、近年非常に重要なトピックの一つになっています。

PostgreSQLには、以前からpgcryptoと呼ばれるモジュールが提供されていましたが、これはあくまでも暗号化を実現するSQL関数を提供するレベルで、実際に使おうとするとアプリケーションから明示的に呼び出す必要があるなど、使いこなすには煩雑なモジュールでした。(私も昔のプロジェクトで使っていたことがあります)
今回NECさんからリリースされた暗号化モジュールは、この(主にアプリケーションから見た)煩雑さを解消する「透過的暗号化(TDE)」と呼ばれるテクノロジー領域です。

私自身も以前から非常に興味を持っていた領域ですので、公開されたOSS版のコードを試しに使ってみました。

■「Transparent Data Encryption for PostgreSQL Free Edition」とは何か?


「今回リリースされたモジュールはつまり何なのか?」という質問に対してダイレクトかつシンプルに答えると、「暗号化をサポートしたPostgreSQLのユーザ定義のデータ型、ユーザ定義関数、およびサポートスクリプト」となるかと思います。

PostgreSQLでは、ユーザ定義のデータ型を作成することができますが、アプリケーションからそのデータ型に対して入出力する処理も自由に実装することができます。今回のモジュールは、その入出力の処理に暗号化機能を追加したものだと考えればよいでしょう。そのため、暗号化に対応したデータ型を使っている特定のカラムに対して、透過的に暗号化処理を行うことができます。

オリジナルのソースコードは以下から参照することができます。

[snaga@devvm04 tdeforpg.orig]$ find -type f | grep -v .git | sort
./README
./SOURCES/COPYRIGHT
./SOURCES/INSTALL-NOTE.TXT
./SOURCES/LICENSE
./SOURCES/bin/cipher_key_regist.sh
./SOURCES/bin/cipher_setup.sh
./SOURCES/data_encryption/93/Makefile
./SOURCES/data_encryption/93/data_encryption.c
./SOURCES/data_encryption/93/data_encryption.h
./SOURCES/data_encryption/makedencryption.sh
./SOURCES/lib/init/cipher_definition.sql
./SOURCES/lib/init/cipher_key_function.sql
./SOURCES/lib/init/common_session_create.sql
./SOURCES/lib/init/common_session_dummy_create.sql
[snaga@devvm04 tdeforpg.orig]$
ざっと眺めてみると、コアの機能になるであろうコード data_encryption.c や、SQLスクリプト、暗号化鍵のセットアップや登録などを行うであろうシェルスクリプトも見つかります。

■インストール手順


オリジナルのリリースでは、ビルドするためにPostgreSQL 9.3のソースコードが必要とのことでしたが、私は普段、コミュニティリリース版のRPMを使っているので、そちらでビルドできるように修正してみました。

修正済みのソースコードは以下にあります。主にPostgreSQL関連のPATHの修正が追加されています。
なお、今回試したプラットフォームはCentOS 6.5で、ビルドする際にインストールされているPostgreSQLのRPMは以下の通りです。
[snaga@devvm04 tdeforpg]$ rpm -qa | grep postgresql93
postgresql93-debuginfo-9.3.5-1PGDG.rhel6.x86_64
postgresql93-plpython-9.3.8-1PGDG.rhel6.x86_64
postgresql93-libs-9.3.8-1PGDG.rhel6.x86_64
postgresql93-server-9.3.8-1PGDG.rhel6.x86_64
postgresql93-plperl-9.3.8-1PGDG.rhel6.x86_64
postgresql93-devel-9.3.8-1PGDG.rhel6.x86_64
postgresql93-9.3.8-1PGDG.rhel6.x86_64
postgresql93-contrib-9.3.8-1PGDG.rhel6.x86_64
[snaga@devvm04 tdeforpg]$
では、実際にビルドしてみます。pg_configコマンドへのPATHが通っている必要があります。
[snaga@devvm04 93]$ which pg_config
/usr/pgsql-9.3/bin/pg_config
[snaga@devvm04 93]$ make
/usr/bin/gcc -c -Wall -O3 -fPIC -I/usr/pgsql-9.3/include/server data_encryption.c
/usr/bin/gcc -shared -o data_encryption.so data_encryption.o /usr/pgsql-9.3/lib/pgcrypto.so -Wl
[snaga@devvm04 93]$ sudo su
[root@devvm04 93]# which pg_config
/usr/pgsql-9.3/bin/pg_config
[root@devvm04 93]# make install

[SUCCESS] data_encryption.so Installed Complete in '/usr/pgsql-9.3/lib'.

[root@devvm04 93]# exit
[snaga@devvm04 93]$

■セットアップ手順


まず、postgresql.confに二つのパラメータ shared_preload_libraries と encrypt.enable の設定を行う必要があります。
shared_preload_libraries = '/usr/pgsql-9.3/lib/data_encryption.so'
encrypt.enable = on
次にpgcryptoモジュールをデータベース上にセットアップします。

今回は、postgresデータベースを使います。
[snaga@devvm04 bin]$ psql -U postgres
psql (9.3.8)
Type "help" for help.

postgres=# create extension pgcrypto;
CREATE EXTENSION
postgres=# \q
次に、暗号化機能のセットアップ(有効化)を行います。接続するPostgreSQLを指定して、暗号化鍵の管理テーブルなどを作成します。
[snaga@devvm04 bin]$ sh cipher_setup.sh /usr/pgsql-9.3
Transparent data encryption feature setup script
Please select from the setup menu below
Transparent data encryption feature setup menu
1: activate  the transparent data encryption feature
2: inactivate the transparent data encryption feature
select menu [1 - 2] > 1
Please enter database server port to connect : 5432
Please enter database user name to connect : postgres
Please enter password for authentication :
Please enter database name to connect : postgres
CREATE LANGUAGE
INFO: Transparent data encryption feature has been activated
[snaga@devvm04 bin]$
最後に暗号化鍵を初期登録します。

接続するPostgreSQLを指定して、暗号化鍵と鍵の暗号化アルゴリズムを指定します。ここではアルゴリズムにAESを選んでいます。
[snaga@devvm04 bin]$ sh cipher_key_regist.sh  /usr/pgsql-9.3
=== Database connection information ===
Please enter database server port to connect : 5432
Please enter database user name to connect : postgres
Please enter password for authentication :
Please enter database name to connect : postgres
=== Regist new cipher key ===
Please enter the new cipher key : [ここで暗号化鍵を入力]
Please retype the new cipher key : [ここで暗号化鍵を再入力]
Please enter the algorithm for new cipher key : aes

Are you sure to register new cipher key(y/n) : y
 cipher_key_enable_log
-----------------------
 t
(1 row)

[snaga@devvm04 bin]$ 
以上でセットアップ完了です。

■暗号化対応のデータ型を試してみる


それでは、実際にデータを登録・検索してみます。

ここでは、ドキュメントにあったサンプルを実行してみます。
DROP TABLE IF EXISTS Employee;

CREATE TABLE Employee(
 EmployeeID Integer PRIMARY KEY,
 Name TEXT,
 Address ENCRYPT_TEXT,               -- 暗号化TEXTカラム
 TelephoneNumber ENCRYPT_TEXT        -- 暗号化TEXTカラム
);

-- 暗号化鍵設定のSQLをサーバログに出力しないようにする
select cipher_key_disable_log();

-- 暗号化鍵の設定
select pgtde_begin_session('bar');

-- サーバログの設定を戻す
select cipher_key_enable_log();

insert into Employee values(1,'従業員1','滋賀','003-0001-0001');
insert into Employee values(2,'従業員2','京都','003-0001-0002');
insert into Employee values(3,'従業員3','大阪','003-0001-0003');
insert into Employee values(4,'従業員4','兵庫','003-0001-0004');

-- 正常に検索できる(はず)
select * from Employee;

-- インデックス作成はできない(エラーになる)
CREATE INDEX Employee_Address_idx ON Employee(Address);
CREATE INDEX Employee_Address_idx ON Employee(TelephoneNumber);

-- 間違った暗号化鍵の設定
select pgtde_begin_session('wrongkey');

-- エラーになる(はず)
select * from Employee;
上記のスクリプトを実行すると、以下のような出力が得られます。
[snaga@devvm04 test]$ psql -U postgres -f test-tde.sql
DROP TABLE
CREATE TABLE
 cipher_key_disable_log
------------------------
 t
(1 row)

 pgtde_begin_session
---------------------
 t
(1 row)

 cipher_key_enable_log
-----------------------
 t
(1 row)

INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
 employeeid |  name   | address | telephonenumber
------------+---------+---------+-----------------
          1 | 従業員1 | 滋賀    | 003-0001-0001
          2 | 従業員2 | 京都    | 003-0001-0002
          3 | 従業員3 | 大阪    | 003-0001-0003
          4 | 従業員4 | 兵庫    | 003-0001-0004
(4 rows)

psql:test-tde.sql:30: ERROR:  data type encrypt_text has no default operator class for access method "btree"
HINT:  You must specify an operator class for the index or define a default operator class for the data type.
psql:test-tde.sql:31: ERROR:  data type encrypt_text has no default operator class for access method "btree"
HINT:  You must specify an operator class for the index or define a default operator class for the data type.
psql:test-tde.sql:34: ERROR:  TDE-E0012 cipher key is not correct(01)
psql:test-tde.sql:37: ERROR:  TDE-E0017 could not decrypt data, because key was not set(01)
[snaga@devvm04 test]$
データを登録した後、一回目の検索は正しく動作しているものの、間違った暗号化鍵を設定して検索した場合にはエラーとなっていることが分かります。

また、インデックス(Btree)を作ろうとしてもエラーが発生して作成できないことが分かります。

■暗号化鍵はどこに保存されるのか?


データベースの暗号化で難しいのは暗号化鍵の管理です。

暗号化そのものは、暗号化鍵とアルゴリズムがあれば実現できるのですが、実際には暗号化鍵の運用も検討しておきたいところです。

今回リリースされたモジュールでは、新規に登録または更新する暗号化鍵は、cipher_key_tableテーブルに保存されるようです。

その際、pgp_sym_encrypt()関数を使って「ソルト有りの256ビットAESアルゴリズム」で暗号化されています。
postgres=# select * from cipher_key_table ;
                                                                       key
                       | algorithm
---------------------------------------------------------------------------------------------------------------------------
-----------------------+-----------
 \xc30c0409010295ff6aec7b0b5d47d237013520178c35afe906f0f1ccac70735e08b695a95fe6112da75d452e36b1b3954a1a69d17765b5d775eadcf9
384816b3df41cfc632757f | aes
(1 row)

postgres=# 

■鍵を変更した場合には?


鍵を変更した場合には、データの再暗号化が行われます。暗号化鍵を変更したのにデータをそのままにしておくと、以前の暗号化鍵で暗号化されたデータを読めなくなってしまうからです。

実際に cypher_key_regist.shスクリプトで暗号化鍵を変更した際、暗号化されているカラムを一旦複合化して、再度暗号化する、という処理が行われている様子を見ることができました。
[snaga@devvm04 test]$ sh ../bin/cipher_key_regist.sh /usr/pgsql-9.3
=== Database connection information ===
Please enter database server port to connect : 5432
Please enter database user name to connect : postgres
Please enter password for authentication :
Please enter database name to connect : postgres
=== Regist new cipher key ===
Please enter the current cipher key : [ここで現在の暗号化鍵を入力]
Please enter the new cipher key : [ここで新規の暗号化鍵を入力]
Please retype the new cipher key : [ここで新規の暗号化鍵を再入力]
Please enter the algorithm for new cipher key : aes

Are you sure to register new cipher key(y/n) : y
INFO:  TDE-I0001 re-encryption of table "public"."employee" was started(01)
CONTEXT:  SQL statement "SELECT cipher_key_reencrypt_data(current_cipher_key, current_cipher_algorithm, cipher_key)"
PL/pgSQL function cipher_key_regist(text,text,text) line 65 at PERFORM
INFO:  TDE-I0002 re-encryption of table "public"."employee" was completed(01)
CONTEXT:  SQL statement "SELECT cipher_key_reencrypt_data(current_cipher_key, current_cipher_algorithm, cipher_key)"
PL/pgSQL function cipher_key_regist(text,text,text) line 65 at PERFORM
 cipher_key_enable_log
-----------------------
 t
(1 row)

[snaga@devvm04 test]$
それを内部で実際に行っているのは cipher_key_reencrypt_data() 関数です。

ここでは詳細は省きますが、PostgreSQLのシステムカタログを検索して、暗号化データ型(encrypt_textとencrypt_bytea)を使っているカラムを探して、再暗号化処理をしています。

■9.4での動作確認


なお、PostgreSQL 9.4でも上記の動作確認をしたのですが、ビルドする際に一部を修正することで、同様に動作しているようです。(今のところ)
興味のある方はこちらもどうぞ。

■まとめ


以上、簡単ではありましたが、先日リリースされた Transparent Data Encryption for PostgreSQL Free Edition を試してみました。

見てきたように、今までのPostgreSQLのpgcryptoでは足りていなかった部分を補うことを狙った、非常に面白いモジュールだと思います。

PostgreSQLでは、今まで本格的な暗号化機能があまり使われていなかったこともあり、暗号化鍵の管理や、再暗号化、インデックス、パフォーマンスなど、運用上いろいろと検討すべき、あるいは追加開発が必要な部分もありますが、より容易に暗号化機能が使えるようになるための、重要な一歩だと感じました。

PostgreSQLにおけるデータベースセキュリティや暗号化に興味のある方は、ぜひご自身でも試してみていただければと思います。

では、また。

0 件のコメント:

コメントを投稿