TL;DR
- OracleはDBMS_CRYPTOという暗号化関数がまとまった標準パッケージがある。
- 引数のデータ型はRAW型(デフォルトで最大2000bytes、設定変更で32757bytes)。CHAR/VARCHAR型との変換はUTL_I18Nパッケージを用いる。
- 暗号化関数DBMS_CRYPTO.ENCRYPT関数 / 復号化DBMS_CRYPTO.DECRYPT関数は非DETERMINISTICなので直接関数インデックスに用いることはできない。ストアドファンクションでラップする必要がある。
- 暗号化 / 復号化キーを隠匿するのはできるが各方面満点の解はない
前提
- Oracle19c
DBMS_CRYPTOパッケージ
公式ドキュメント
利用の前提条件
- Oracle19c現在、追加ライセンス不要。
- 初期では権限不足でどのユーザーも使えないため、権限付与SQLの実行要(たぶんsysユーザーが必要)
-- 権限付与 GRANT execute ON SYS.DBMS_CRYPTO TO youruser;
利用例
使い方例(暗号化)
-- 暗号化 SELECT DBMS_CRYPTO.ENCRYPT( UTL_I18N.STRING_TO_RAW('暗号化対象データ', 'AL32UTF8'), 6+256+4096, -- 暗号化指定。このパラメータはAES128 HEXTORAW('010203040506070809a0b0c0d0e0f000') -- 暗号化キー ) AS crypt FROM dual;
result
CRYPT -------------------------------------------------------------------------------- 2F5C489FB4BE399A4EB81FFBA6B6E28645B7060B5EC1D93F63571A4E0F119C02
- 第二引数の値は、DBMS_CRYPTOパッケージ配下の定数を参照。
- URL:DBMS_CRYPTO
- 例えば
6 + 256 + 4096
は、以下で確認できる
SET SERVEROUTPUT ON BEGIN DBMS_OUTPUT.PUT_LINE(DBMS_CRYPTO.ENCRYPT_AES128); DBMS_OUTPUT.PUT_LINE(DBMS_CRYPTO.CHAIN_CBC); DBMS_OUTPUT.PUT_LINE(DBMS_CRYPTO.PAD_PKCS5); END; /
使い方例(復号化)
-- 復号化 SELECT UTL_I18N.RAW_TO_NCHAR( DBMS_CRYPTO.DECRYPT( crypt, 6+256+4096, HEXTORAW('010203040506070809a0b0c0d0e0f000') ) , 'AL32UTF8') as plain FROM (SELECT DBMS_CRYPTO.ENCRYPT( UTL_I18N.STRING_TO_RAW('暗号化対象データ', 'AL32UTF8'), 6+256+4096, HEXTORAW('010203040506070809a0b0c0d0e0f000') ) AS crypt FROM dual );
関数インデックス
- 以下はエラーとなる。
CREATE TABLE sample(crypt RAW(2000)); CREATE INDEX idx_sample ON sample(DBMS_CRYPTO.DECRYPT(crypt, 0, HEXTORAW('00')));
CREATE INDEX idx_sample ON sample(DBMS_CRYPTO.DECRYPT(crypt, 0, HEXTORAW('00'))) * 行1でエラーが発生しました。: ORA-30553: 関数がDETERMINISTICではありません。
- 以下のように、DETERMINISTICなストアドファンクションを定義することで回避できる。
CREATE OR REPLACE FUNCTION DECRYPT_AES128 ( -- 暗号データ src IN VARCHAR2, -- 復号キー。16bytesであること key IN VARCHAR2 ) RETURN RAW DETERMINISTIC IS BEGIN RETURN DBMS_CRYPTO.DECRYPT( UTL_I18N.STRING_TO_RAW(src, 'AL32UTF8'), DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5, UTL_I18N.STRING_TO_RAW(key, 'AL32UTF8') ); END; /
CREATE INDEX idx_sample ON sample(DECRYPT_AES128(crypt, 0, HEXTORAW('00')))
- 後述するが、関数インデックスはデータファイル上暗号化されないので注意。
暗号化キーの隠匿化
- SQLのパラメータとしてキー値を渡したりSQL文にベタ書きすると、Oracle内ログ、アプリログ、通信にキーが曝露するのでセキュリティ上非常によくない。
- 色々方式はあるが、PL/SQL内にキーを隠匿し、SOURCEも隠匿する手法にいきついたので紹介
SOURCEを秘匿化しながらストアドファンクションを定義
- 定義SQL
DECLARE ddl_text VARCHAR2(32767); key_nchar VARCHAR2(32) := '010203040506070809a0b0c0d0e0f000'; FUNCTION generaye_ENCRYPT_AES128_DDL RETURN VARCHAR2 IS BEGIN return 'CREATE OR REPLACE FUNCTION ENCRYPT_AES128' || ' (' || ' src IN VARCHAR2' || ' )' || ' RETURN RAW DETERMINISTIC' || ' IS' || ' BEGIN' || ' RETURN DBMS_CRYPTO.ENCRYPT(' || ' UTL_I18N.STRING_TO_RAW(src, ''AL32UTF8''),' || ' DBMS_CRYPTO.ENCRYPT_AES128 +' || ' DBMS_CRYPTO.CHAIN_CBC +' || ' DBMS_CRYPTO.PAD_PKCS5,' || ' HEXTORAW('''|| key_nchar ||''')' || ' );' || 'END;'; END generaye_ENCRYPT_AES128_DDL; FUNCTION generaye_DECRYPT_AES128_DDL RETURN VARCHAR2 IS BEGIN return 'CREATE OR REPLACE FUNCTION DECRYPT_AES128' || ' (' || ' src IN RAW' || ' )' || ' RETURN VARCHAR2 DETERMINISTIC' || ' IS' || ' BEGIN' || ' RETURN UTL_I18N.RAW_TO_NCHAR(DBMS_CRYPTO.DECRYPT(' || ' src,' || ' DBMS_CRYPTO.ENCRYPT_AES128 +' || ' DBMS_CRYPTO.CHAIN_CBC +' || ' DBMS_CRYPTO.PAD_PKCS5,' || ' HEXTORAW('''|| key_nchar ||''')' || ' ), ''AL32UTF8'');' || 'END;'; END generaye_DECRYPT_AES128_DDL; BEGIN ddl_text := generaye_ENCRYPT_AES128_DDL(); EXECUTE IMMEDIATE ddl_text; SYS.DBMS_DDL.CREATE_WRAPPED(ddl_text); ddl_text := generaye_DECRYPT_AES128_DDL(); EXECUTE IMMEDIATE ddl_text; SYS.DBMS_DDL.CREATE_WRAPPED(ddl_text); END; /
- デモ
select ENCRYPT_AES128('テストデータ') as crypt, DECRYPT_AES128(ENCRYPT_AES128('テストデータ')) as plain from dual;
CRYPT -------------------------------------------------------------------------------- PLAIN -------------------------------------------------------------------------------- 21E108C3DCA3AFFDAC709FB1079F71724C90C3EB22FC90E7D03EC066598568D8 テストデータ
- 仕組みはよく分からないが、SYS.DBMS_DDL.CREATE_WRAPPED()を介すことでSOURCEが簡単に見れなくなる
- Oracleのドキュメントを見ると動的に生成、とあるので、もしかしたらパフォーマンスに影響があるかもしれない(未検証)
SELECT text FROM ALL_SOURCE WHERE name = 'ENCRYPT_AES128';
FUNCTION ENCRYPT_AES128 wrapped a000000 369 abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd abcd 8 12a 128 rcNM2xfPeGeX3nLRY90YTcVHg+owg3nQDEgVZy+ix4kZiJlGlznTgNjUhvtgN2E96/8XKgYS te98VEs30mFBh1xkI2H8uhvGK/l78NQCUdjjYInR42xy0nnbSF+Joq33V80CoUcoKtyQinr4 DiI3NyfE3M4d3vxM49YNkBmB1g2xedLplesOBkKkDfD63/IBtpdHnyszReXCz8ALtVJ6G0pP TqCzx6ANEPB769u52UCVylVTD3kgjYieiXU4SkQZCDeeTxJ2uNu9F3GmkSnuzylTk7xM+2ha DIA=
セキュリティ的な評価
脅威 | 評価 | 追加対策の余地 |
---|---|---|
OSに不正ログインされ、Oracleのテーブルデータファイルを詐取される | 〇:暗号化されており、復号キーも漏洩していない | |
OSに不正ログインされ、Oracleのインデックスデータファイルを詐取される | ×:インデックス値は平文で保存されている | Oracle Advanced Security(有料オプション)のTDEでデータファイルを暗号化する。問題のあるキーの組み合わせ(個人名, 電話番号の複合インデックスなど)を用いない、インデックス化を避けるなどの設計による対処 |
DBに不正ログインされ、SQLを実行される | △:復号化関数や手法を知っていれば平文を漏洩してしまう | 関数名の難読化、手掛かりにいきつくまでの妨害・ログの秘匿化 |
DBにログインした作業者による詐取 | △:復号化関数や手法を知っていれば平文を漏洩してしまう | 保守ユーザ/アプリユーザの分離・権限の制限 |
アプリケーション脆弱性によるSQLインジェクション経由での詐取 | 〇:復号化関数の存在を知ることは困難と考えられるため、暗号化したデータしか表示されない |
TDEとの比較
- TDEは、Oracle Advanced Security(有料オプション)の機能の1つ。
- TDEは、Oracleのデータファイルを暗号化する。そのため、SQL実行による参照は保護されず、アプリケーションからのSQLインジェクションにも対応できない。
- 情報のマスキングは、Oracle Advanced Securtyの他オプションでも可能だが、特定ユーザーはマスキング項目を非表示にするなどの作業者へのいじわると、暗号化による保護というよりは内部統制のソリューションであり、DBへの不正接続・SQLインジェクションなどによる任意クエリ実行の対策にはならないと考えている。
- 詳しく調べてないので、有識者に聞いてみたい。