Db2ネイティブ暗号化


昨今は、「個人情報を保持するDBは暗号化しなさい!問答無用!」というような非機能要件があったりして、サーバ更改案件なんかでは、「今までやってなかったのに、、、性能ってどれくらい劣化するんだろう。。。」などと現場SEを悩ませたりします。
使用する暗号化アルゴリズム・鍵長やデータ、トランザクション特性によっても、CPUにどれほどの負荷がかかるかまちまちなので、ベンダー側も一概にこうです、とは言いづらいでしょうし、サイジングの際には気を使います。
実際、提供側は数%程度と言っていたのに、試してみたら15%くらい増加したケースもあったりして、、、

そもそも、どのレイヤで暗号化するのが適切なのか(ストレージ、ファイルシステム、RDBMS、アプリケーションの組み合わせ)は、その裏にあるはずのセキュリティ要件から決定する必要があるわけですが、実際の現場ではとりあえずそういう決まりだからということで、使える機能を使って(なるべく工数がかからないように)「暗号化してますよ!」アピールをする、なんていう温度感のところもあったりしますが、それはそれとしてDb2のネイティブ暗号化を使えるようにするまでの手順を簡単にまとめておきます。

前提環境

  • IBM Db2 Developer Community Edition v11.1.3.3(Dockerのコンテナとして提供されており、Db2とDSMの2コンテナが起動)
  • コンテナホストはMac OSX 10.13.6
  • Docker for Macのバージョンは18.06.0-ce
# db2level
DB21085I  This instance or install (instance name, where applicable: 
"db2inst1") uses "64" bits and DB2 code release "SQL11013" with level 
identifier "0204010F".
Informational tokens are "DB2 v11.1.3.3", "s1803021700", "DYN1803021700AMD64", 
and Fix Pack "3".
Product is installed at "/opt/ibm/db2/V11.1".
# docker version
Client:
 Version:           18.06.0-ce
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        0ffa825
 Built:             Wed Jul 18 19:05:26 2018
 OS/Arch:           darwin/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.06.0-ce
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.10.3
  Git commit:       0ffa825
  Built:            Wed Jul 18 19:13:46 2018
  OS/Arch:          linux/amd64
  Experimental:     true
# docker ps
CONTAINER ID        IMAGE                                          COMMAND                  CREATED             STATUS              PORTS                                                          NAMES
e56fd5062b66        ibmcorp/data_server_manager_dev:2.1.5-x86_64   "/var/dsm_setup/star…"   7 weeks ago         Up 7 weeks          0.0.0.0:11080-11082->11080-11082/tcp                           dsm
05a509afe32e        ibmcorp/db2_developer_c:11.1.3.3x-x86_64       "/var/db2_setup/lib/…"   7 weeks ago         Up 7 weeks          22/tcp, 55000/tcp, 60006-60007/tcp, 0.0.0.0:50000->50000/tcp   db2server

Db2ネイティブ暗号化の概要を簡単にまとめ

ネイティブ暗号化は、データベース単位にまるごと暗号化するか否か。ひとつのデータベース内で、このテーブルは暗号化するけど別のテーブルは暗号化しない、などといったことはできない。

以下、主要コンポーネント。

鍵ストア

暗号化鍵を保管するストレージオブジェクト(要はPKCS#12ファイルだが、お金に余裕があればPKCS#11を実装したHardware Security Moduleも使える。必要なDb2でサポート可能なライブラリを準備要)。
1インスタンスに1つだけ持たせられる(DBM CFGのパラメタで登録)。つまり複数のデータベースがあって、鍵ストアを切替えて使うといった運用はできない。暗号鍵のマスター鍵(いずれも後述)をデータベースによって分けたい場合は、この鍵ストアに複数のマスター鍵を登録しておく必要がある。

マスター鍵

暗号化鍵を暗号化するための鍵。1つの暗号化したDBに対して1つのマスター鍵を持たせる。

暗号化鍵

読んで字のごとく暗号化の鍵そのもの。これは鍵ストアではなく、データベース自体やバックアップデータ内に、マスター鍵を使って暗号化した上で保管される。

簡単な図にするとこのような格好になる。

データの暗号化鍵はDB内に保持し、鍵ストアの内のマスター鍵で暗号化鍵を暗号化する。
つまり、暗号化を有効にしたデータベース内のデータは全て(コンテナ、トランザクションログ、バッファプールなど)暗号化される
バックアップデータも暗号化されたまま、これまたマスター鍵で暗号化された「暗号化鍵」といっしょに保存される。なので、リストアするときにはマスター鍵を持っていなければならない。
権限のある(認証、認可された)ユーザからは透過的にSQLで照会できるし、データベースの外では復号される。
サーバがクラックされてOSファイルシステムのファイル(たとえばトランザクションログとかコンテナファイルとか)が流出した、けれども暗号化されてるから読めない、といったような割と限定的な効果しかないともいえる(仕組みさえ知っていれば鍵ストアとスタッシュファイルも一緒に流出してしまえばマスター鍵にアクセスできるので、暗号・復号鍵も入手できてしまう)。
あるいは、DBへRDBMSを経由してアクセスする権限のないOSユーザが、おかしなパーミッション設定されたDb2のファイルに直接アクセスしたけど読めない、といったような施策となる。

なお、暗号化ライブラリとしてDb2にはGSKit(Global Security Kit)というIBM社製の暗号化ライブラリが同梱されている。HSMを使ったりする場合はDb2製品的にサポートするライブラリを別途導入する必要がある。

実際にやってみる

(1)暗号化ツールキットのセットアップ

Developer-C Editionでは既にセットアップ済み。

echo ${LD_LIBRARY_PATH}                        
/database/config/db2inst1/sqllib/lib64:/database/config/db2inst1/sqllib/lib64/gskit:/database/config/db2inst1/sqllib/lib32

${INST_HOME}/sqllib/lib64/gskitがライブラリパス(LD_LIBRARY_PATHやSH_LIBPATH)に設定されていればOK

(2)鍵ストアの作成

${INST_HOME}/sqllib/gskit/bin/gsk8capicmd_64を使う(JDKに入っているkeytoolのようなもの)。

mkdir ~/keystore
gsk8capicmd_64 -keydb -create -db ~/keystore/myowndbKeystore.p12 -pw p@ssw0rd -type pkcs12 -stash

鍵ストアを保存するディレクトリを作成して、鍵ストアを作成する。サポートする鍵ストアタイプがPKCS#12なので-type pkcs12は必須であり、かつファイルの拡張子は.p12である必要あり。
最後の-stashは鍵ストアのパスワードをスタッシュする(隠しておく)オプション。これがないとDB起動時にこの鍵ストアパスワードを手動入力する羽目に。
※IBM HTTPServerのパスワードの自動入力機能(ikeyman)、JDK(keytool)の.keypass.storepassファイルと似た仕組み
クラスタ構成を組んでいたり、ストレージの機能で他のインスタンスにデータを同期・共有したりしているときは、このファイル群も同様に同期・共有する必要がある。

ll ~/keystore/
total 4
-rw------- 1 db2inst1 db2iadm1   0 Sep 22 17:19 myowndbKeystore.p12
-rw------- 1 db2inst1 db2iadm1 193 Sep 22 17:19 myowndbKeystore.sth

鍵ストア(.p12ファイル)とスタッシュファイル(.sthファイル)のできあがり。
もちろん、鍵ストア作成時にパスワードをスタッシュしなかった場合でも後からスタッシュできる。

(3)マスター鍵の作成

gsk8capicmd_64 -secretkey -create -db ~/keystore/myowndbKeystore.p12 -stashed -label "MasterKey4MyOwnDB" -size 32 

-sizeで指定する鍵長はバイト単位なので注意。省略すると適当なサイズにする。暗号化アルゴリズムについては不明。

(4)鍵ストアのDBへの登録

作成した鍵ストアをDBインスタンスに登録する。

db2 "UPDATE DBM CFG USING
     keystore_type pkcs12
     keystore_location /database/config/db2inst1/keystore/myowndbKeystore.p12"
# 再起動を促されたのでそうする
db2stop
db2start

(5)DBを暗号化オプションつきで作成

あとはCREATE DBENCRYPTオプションをつけるだけ(とりあえず暗号化関連以外デフォルト)。

db2 "CREATE DATABASE ENCDB ENCRYPT CIPHER AES KEY LENGTH 256 MASTER KEY LABEL MasterKey4MyOwnDB"

暗号化設定の確認は以下の表関数から取得可能。

db2 "SELECT * FROM TABLE(SYSPROC.ADMIN_GET_ENCRYPTION_INFO())"

確認

つないでみる。

db2 "CONNECT TO ENCDB"

コンテナのパスを確認。

db2 "SELECT TBSP_ID,CONTAINER_ID,CONTAINER_NAME,CONTAINER_TYPE FROM TABLE(MON_GET_CONTAINER(NULL,NULL)) AS T"

stringsで中身をみてみる。

strings /database/data/db2inst1/NODE0000/ENCDB/T0000002/C0000000.LRG
〜略〜
1!")
+/=f
T0rO]
up#~A
%\Jt}f
〜略〜

まったく読めない一方で、暗号化していないDBは、

strings /database/data/db2inst1/NODE0000/MYDB/T0000002/C0000000.LRG | head -10
〜略〜
SOFTWARE SUPPORT
OPERATIONS
〜略〜

このとおり結構読める。

トランザクションログも確認しておく。

db2 GET DB CFG | grep "Path to log"
strings /database/data/db2inst1/NODE0000/SQL00006/LOGSTREAM0000/S0000000.LOG | head -20
〜略〜
-hT_
B_r 
k>,%
G>o}
〜略〜

コンテナと同様、暗号化したデータベースのトランザクションは普通には読めず、暗号化していないデータベースのトランザクションログは結構見える、という当たり前のことが確認できた。

以上、簡単にDb2ネイティブ暗号化を使用してDBを暗号化する手順の覚書でした。

参考資料