MySQL のテーブルに透過的暗号化 (TDE) を実施する (Windows 版)

ページ更新日 :
ページ作成日 :

動作確認環境

MySQL
  • MySQL 8.0 Community Edition
Windows
  • Windows 11

動作必須環境

MySQL
  • MySQL 8.0 Community Edition
  • MySQL 8.0 Enterprise Edition
Windows
  • Windows 11
  • Windows 10
Windows Server
  • Windows Server 2022
  • Windows Server 2019
  • Windows Server 2016
  • Windows Server 2012 R2

前提条件

  • MySQL のデータベースがインストールされていること

透過的暗号化について

透過的暗号化 (Enterprise Transparent Data Encryption) とは名前にある通り暗号化を行う技術であり、これを行わないものに比べるとセキュリティは高くなります。 通常暗号化を行う場合、暗号化処理と復号処理を行う必要が出てくるので実装者の負担になることが多いのですが、 この「透過的」と名付けられてる意味としては「実装者が何も処理に変更を加えることなく暗号化できる」という意味が込められています。 そのため、最初に少し設定に手を加えるだけで後は勝手に暗号化されるのでセキュリティ対策としては非常に有効な手段となります。

ですが「何も変更を加える必要はない」という文言の通り実際にデータベースにログインすれば普通にデータは見えますし、プログラムからデータベースのレコードを取得すれば普通にデータは見れてしまいます。 もし取得したデータが暗号化されていたら処理に変更を加える必要が出てしまうので「処理に変更を加える必要はない」という文言とは一致しなくなります。

では MySQL において透過的暗号化とはいったい何を暗号化しているかというと「保存されているデータベースファイル」または「テーブルファイル」が暗号化の対象となります。 後で確認しますが、直接ファイルを開いても中身が分からないように暗号化するだけですので、実際の運用ではデータベースにログインされないように運用する必要があります。

普通にテーブルを作成し中身を確認する

まずは普通にテーブルとレコードを作成してみます。 どのように作成してもいいですが今回は Workbench で SQL を実行して作成します。 今回はテーブルに対して透過的暗号化を設定する予定なのでデータベースは普通に作ります。

CREATE DATABASE `test_database`

テーブルも普通に作ります。列の内容は何でもいいです。

CREATE TABLE `plain_user` (
  `id` int NOT NULL,
  `name` varchar(32) ,
  `age` int ,
  `address` varchar(256) ,
  `phone` varchar(32) ,
  `email` varchar(128) ,
  `remarks` varchar(1024) ,
  `height` decimal(5, 2) ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

暗号化を確認するためにレコードも適当に入れておきます。

INSERT INTO `test_database`.`plain_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('1','名前1','11','宮城県仙台市1','000-0000-0001','example1@example.com','備考1','170.00');
INSERT INTO `test_database`.`plain_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('2','名前2','12','宮城県仙台市2','000-0000-0002','example2@example.com','備考2','171.01');
INSERT INTO `test_database`.`plain_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('3','名前3','13','宮城県仙台市3','000-0000-0003','example3@example.com','備考3','172.02');
INSERT INTO `test_database`.`plain_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('4','名前4','14','宮城県仙台市4','000-0000-0004','example4@example.com','備考4','173.03');
INSERT INTO `test_database`.`plain_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('5','名前5','15','宮城県仙台市5','000-0000-0005','example5@example.com','備考5','174.04');
INSERT INTO `test_database`.`plain_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('6','名前6','16','宮城県仙台市6','000-0000-0006','example6@example.com','備考6','175.05');
INSERT INTO `test_database`.`plain_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('7','名前7','17','宮城県仙台市7','000-0000-0007','example7@example.com','備考7','176.06');
INSERT INTO `test_database`.`plain_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('8','名前8','18','宮城県仙台市8','000-0000-0008','example8@example.com','備考8','177.07');
INSERT INTO `test_database`.`plain_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('9','名前9','19','宮城県仙台市9','000-0000-0009','example9@example.com','備考9','178.08');
INSERT INTO `test_database`.`plain_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('10','名前10','20','宮城県仙台市10','000-0000-0010','example10@example.com','備考10','179.09');

データベースやテーブルを作成するとデフォルトでは以下のフォルダに作成されます。

  • C:\ProgramData\MySQL\MySQL Server 8.0\Data

ProgramData フォルダは隠しフォルダになっているのでエクスプローラーの設定で見えるようにしてください。

フォルダの中には作成したデータベース名のフォルダがあるので開きます。

中にはテーブル単位のファイルがあります。

このファイルをメモ帳などのテキストエディタで開いてください。 バイナリファイルなので基本的にはよくわからないテキストが並んでいると思いますが、 下の方を見てみると登録したレコードのテキストが見えると思います。

このように暗号化していない場合はファイルを直接開いただけでもレコードの内容が分かってしまうため、 データベースにログインせずとも中身が分かってしまいます。

透過的暗号化の設定を行う

透過的暗号化を有効にするにはプラグインのインストールが必要です。 まずはプラグインが入っているかどうかコマンドで確認します。

スタートメニューから「MySQL 8.0 Command Line Client」を選択して起動します。

パスワードを入力してログインしたら以下のコマンドを入力します。

SELECT PLUGIN_NAME,PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'keyring%';

まだインストールしていないので空の状態のはずです。

ではプラグインの設定を行います。まず以下のフォルダを作成します。 実際には作成する場所や名前はどこでもいいです。後でキーファイルを保存する場所として指定します。

  • C:\ProgramData\MySQL\mysql-keyring

次に以下のファイルをテキストエディタで開きます。

  • C:\ProgramData\MySQL\MySQL Server 8.0\my.ini

一番下の空いているところに以下のテキストを追加して保存します。 管理者権限で保存できない場合はいったん別の場所に保存してからファイルを上書きすればよいです。

early-plugin-load=keyring_file.dll
keyring_file_data=C:\ProgramData\MySQL\mysql-keyring\keyring

MySQL のサービスを再起動します。コマンドで再起動してもいいです。

再起動すると指定したフォルダに自動的にファイルが作成されます。

もう一度プラグインの確認コマンドを実行するとプラグインがインストールされていることを確認できます。

SELECT PLUGIN_NAME,PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'keyring%';

暗号化テーブルの作成

今度は暗号化されたテーブルを作成してみます。 先ほどと同じようなテーブルを作成しますが、今度はオプションに ENCRYPTION='Y' を追加します。 この暗号化設定は現時点では GUI で設定できないようなのでコマンドで設定する必要があります。

CREATE TABLE `encrypt_user` (
  `id` int NOT NULL,
  `name` varchar(32) ,
  `age` int ,
  `address` varchar(256) ,
  `phone` varchar(32) ,
  `email` varchar(128) ,
  `remarks` varchar(1024) ,
  `height` decimal(5, 2) ,
PRIMARY KEY (`id`)
) ENCRYPTION='Y' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

ちなみにプラグイン設定前は暗号化テーブルを作成しようとしても以下のようなエラーが表示されます。

Can't find master key from keyring, please check in the server log if a keyring is loaded and initialized successfully.
キーリングからマスターキーが見つかりません。キーリングが正常にロードされ、初期化されているかどうかをサーバーログで確認してください。

データがないと暗号化されているか分からないので同じようなデータを入れてみます。

INSERT INTO `test_database`.`encrypt_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('1','名前1','11','宮城県仙台市1','000-0000-0001','example1@example.com','備考1','170.00');
INSERT INTO `test_database`.`encrypt_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('2','名前2','12','宮城県仙台市2','000-0000-0002','example2@example.com','備考2','171.01');
INSERT INTO `test_database`.`encrypt_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('3','名前3','13','宮城県仙台市3','000-0000-0003','example3@example.com','備考3','172.02');
INSERT INTO `test_database`.`encrypt_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('4','名前4','14','宮城県仙台市4','000-0000-0004','example4@example.com','備考4','173.03');
INSERT INTO `test_database`.`encrypt_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('5','名前5','15','宮城県仙台市5','000-0000-0005','example5@example.com','備考5','174.04');
INSERT INTO `test_database`.`encrypt_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('6','名前6','16','宮城県仙台市6','000-0000-0006','example6@example.com','備考6','175.05');
INSERT INTO `test_database`.`encrypt_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('7','名前7','17','宮城県仙台市7','000-0000-0007','example7@example.com','備考7','176.06');
INSERT INTO `test_database`.`encrypt_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('8','名前8','18','宮城県仙台市8','000-0000-0008','example8@example.com','備考8','177.07');
INSERT INTO `test_database`.`encrypt_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('9','名前9','19','宮城県仙台市9','000-0000-0009','example9@example.com','備考9','178.08');
INSERT INTO `test_database`.`encrypt_user` (`id`,`name`,`age`,`address`,`phone`,`email`,`remarks`,`height`) VALUES ('10','名前10','20','宮城県仙台市10','000-0000-0010','example10@example.com','備考10','179.09');

encrypt_user のテーブルファイルが作成されているので中身を確認してみます。

ファイルが暗号化されておりレコードの内容を確認することができなくなっています。

とりあえずこれで第三者にサーバーにログインしてテーブルファイルを直接盗んだだけでは中のレコードを読み取られる心配はなくなりました。 実装の変更も一切必要ないのでお手軽のセキュリティ設定だと思います。

ただデータベースにログインされれは普通にデータは見れてしまいますので、 よりセキュリティレベルを上げるのであればデータベースログインのアカウントをしっかり管理し、別途暗号化の仕組みを追加導入する必要があります。

途中で暗号化テーブルに変換する

最初に暗号化されてないテーブルを作成してしまった場合でも後から暗号化テーブルに変換することができます。 その場合は以下の SQL で変換できます。

use <データベース名>;
ALTER TABLE <テーブル名> ENCRYPTION='Y';

use test_database;
ALTER TABLE plain_user ENCRYPTION='Y';

ただしテーブルファイルを全て書き換えるためレコードが多いと時間がかかるので注意してください。