★PostgreSQLカンファレンス2024 12月6日開催/チケット販売中★
他のバージョンの文書 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

F.28. pgcrypto

pgcryptoモジュールはPostgreSQL用の暗号関数を提供します。

F.28.1. 汎用ハッシュ関数

F.28.1.1. digest()

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

与えられたdataのバイナリハッシュを計算します。 typeは使用するアルゴリズムです。 標準アルゴリズムはmd5sha1sha224sha256sha384、およびsha512です。 pgcryptoがOpenSSL付きで構築された場合、表F-18で詳解する、より多くのアルゴリズムを利用することができます。

ダイジェストを16進数表記の文字列としたい場合は、結果に対してencode()を使用してください。 以下に例を示します。

CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$
    SELECT encode(digest($1, 'sha1'), 'hex')
$$ LANGUAGE SQL STRICT IMMUTABLE;

F.28.1.2. hmac()

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

keyをキーとしたdataのハッシュ化MACを計算します。 typedigest()の場合と同じです。

digest()と似ていますが、ハッシュはキーを知った上で再計算されるだけです。 これは、誰かがデータを変更し、同時に一致するようにハッシュを変更するという状況を防ぎます。

キーがハッシュブロックサイズより大きい場合、まずハッシュ化され、その結果をキーとして使用します。

F.28.2. パスワードハッシュ化関数

crypt()およびgen_salt()関数は特にパスワードのハッシュ化のために設計されたものです。 crypt()がハッシュ処理を行い、gen_salt()はハッシュ処理用のアルゴリズム上のパラメータを準備します。

crypt()アルゴリズムは、以下の点でMD5やSHA1のような通常のハッシュ処理アルゴリズムと異なります。

  1. 低速です。 データ量が少ないためパスワード総当たり攻撃に対して頑健にする唯一の方法です。

  2. 結果にはソルトというランダムな値が含まれます。 このため同じパスワードのユーザでも異なった暗号化パスワードを持ちます。 これはアルゴリズムの逆処理に対する追加の防御です。

  3. 結果内にアルゴリズムの種類が含まれます。 このため異なるアルゴリズムでハッシュ化したパスワードが混在可能です。

  4. 一部は適応型です。 つまり、コンピュータが高速になったとしても、既存のパスワードとの互換性を損なうことなくアルゴリズムを低速に調整することができます。

crypt()関数がサポートするアルゴリズムを表F-15に列挙します。

表 F-15. crypt()がサポートするアルゴリズム

アルゴリズムパスワード最大長適応型かどうかソルトビット長説明
bf72はい128Blowfishベース、2a版
md5無制限いいえ48MD5ベースの暗号
xdes8はい24拡張DES
des8いいえ12元来のUNIX crypt

F.28.2.1. crypt()

crypt(password text, salt text) returns text

passwordのcrypt(3)形式のハッシュを計算します。 新しいパスワードを保管する時には、gen_salt()を使用して新しいsaltを生成する必要があります。 パスワードを検査する時、既存のハッシュ値をsaltとして渡し、結果が格納された値と一致するかどうかを確認します。

新しいパスワードの設定例を以下に示します。

UPDATE ... SET pswhash = crypt('new password', gen_salt('md5'));

認証の例です。

SELECT pswhash = crypt('entered password', pswhash) FROM ... ;

入力パスワードが正しければtrueを返します。

F.28.2.2. gen_salt()

gen_salt(type text [, iter_count integer ]) returns text

crypt()で使用するランダムなソルト文字列を新規に生成します。 また、このソルト文字列はcrypt()にどのアルゴリズムを使用するかを通知します。

typeパラメータはハッシュ化アルゴリズムを指定します。 受付可能な種類は、desxdesmd5bfです。

繰り返し回数を持つアルゴリズムでは、ユーザはiter_countパラメータを使用して繰り返し回数を指定できます。 指定する回数を高くすれば、パスワードのハッシュ化にかかる時間が長くなり、それを破るための時間も長くなります。 しかし、あまりに多くの回数を指定すると、ハッシュ計算にかかる時間は数年に渡ってしまう可能性があります。 これは実用的ではありません。 iter_countパラメータを省略した場合、デフォルトの繰り返し回数が使用されます。 iter_countで受け付けられる値はアルゴリズムに依存し、表F-16に示す通りです。

表 F-16. crypt()用の繰り返し回数

アルゴリズムデフォルト最小最大
xdes725116777215
bf6431

xdesの場合は他にも、回数が奇数でなければならないという制限があります。

適切な繰り返し回数を選択するために、元々のDES暗号は当時のハードウェアで1秒あたり4個のハッシュを持つことができるように設計されたことを考えてください。 毎秒4ハッシュより遅いと、おそらく使い勝手が悪いでしょう。 毎秒100ハッシュより速いというのは、十中八九、あまりにも速すぎるでしょう。

ハッシュ化アルゴリズム別に相対的な速度に関する概要を表F-17にまとめました。 この表は、8文字のパスワード内のすべての文字の組合せを取るためにかかる時間を示します。 また、すべて小文字の英字のみのパスワードである場合と大文字小文字が混在した英字と数字のパスワードの場合を仮定します。 crypt-bfの項では、スラッシュの後の数値はgen_saltiter_countです。

表 F-17. ハッシュアルゴリズムの速度

アルゴリズム1秒当たりのハッシュ数[a-z]の場合[A-Za-z0-9]の場合
crypt-bf/828246年251322年
crypt-bf/757121年123457年
crypt-bf/611262年62831年
crypt-bf/521133年33351年
crypt-md526812.6年2625年
crypt-des3628377日19年
sha15902234日12年
md523450861日3年

注意:

  • Pentium 4 1.5GHzのマシンを使用しました。

  • crypt-desおよびcrypt-md5アルゴリズムの数値はJohn the Ripper v1.6.38の-test出力から得たものです。

  • md5の数値はmdcrack 1.2のものです。

  • sha1の数値はlcrack-20031130-betaのものです。

  • crypt-bfの数は、1000個の8文字パスワードをループする単純なプログラムを使用して得たものです。 こうして、異なる回数の速度を示すことができました。 参考までに、john -testcrypt-bf/5で213 loops/secでした。 (結果の差異が非常に小さいことは、pgcryptoにおけるcrypt-bf実装がJohn the Ripperで使用されるものと同じであるという事実と一致します。)

"すべての組み合わせを試行する"ことは現実的な行使ではありません。 通常パスワード推定は、普通の単語とその変形の両方を含む辞書を使用して行われます。 ですので、いささかなりとも言葉に似たパスワードは上で示した数値よりも速く推定されます。 また6文字の単語に似ていないパスワードは推定を免れるかもしれませんし、免れないかもしれません

F.28.3. PGP暗号化関数

ここで示す関数はOpenPGP(RFC 4880)標準の暗号処理部分を実装します。 対称キーおよび公開キー暗号化がサポートされます。

暗号化されたPGPメッセージは次の2つの部品(またはパケット)から構成されます。

対称キー(つまりパスワード)で暗号化する場合

  1. 与えられたパスワードはString2Key(S2K)アルゴリズムでハッシュ化されます。 これはどちらかというとcrypt()アルゴリズムと似て、意図的に低速で、かつランダムなソルトを使用します。 しかし、全長のバイナリキーを生成します。

  2. 分離したセッションキーが要求された場合、新しいランダムなキーが生成されます。 さもなくば、S2Kキーがそのままセッションキーとして使用されます。

  3. S2Kキーがそのまま使用される場合、S2K設定のみがセッションキーパケットに格納されます。 さもなくば、セッションキーはS2Kキーで暗号化され、セッションキーパケットに格納されます。

公開キーで暗号化する場合

  1. 新しいランダムなセッションキーが生成されます。

  2. これは公開キーを使用して暗号化され、セッションキーパケットに格納されます。

どちらの場合でもデータ暗号化は以下のように処理されます。

  1. 省略可能なデータ操作として、圧縮、UTF-8への変換、改行の変換があります。

  2. データの前にはランダムなバイト数のブロックが付きます。 これはrandom IVを使用する場合と同じです。

  3. ランダムな前置ブロックとデータのSHA1ハッシュが後に付けられます。

  4. これをすべてセッションキーで暗号化し、データパケットに格納します。

F.28.3.1. pgp_sym_encrypt()

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

対称PGPキーpswdataを暗号化します。 optionsパラメータには後述のオプション設定を含めることができます。

F.28.3.2. pgp_sym_decrypt()

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

対称キーで暗号化されたPGPメッセージを復号化します。

pgp_sym_decryptbytea型のデータを復号化することはできません。 これは無効な文字データの出力を防止するためです。 元のテキストのデータをpgp_sym_decrypt_byteaで復号化することが正しい方法です。

optionsパラメータには後述のオプション設定を含めることができます。

F.28.3.3. pgp_pub_encrypt()

pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea
pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea

公開PGPキーkeydataを暗号化します。 この関数に秘密キーを与えるとエラーになります。

optionsパラメータには後述のオプション設定を含めることができます。

F.28.3.4. pgp_pub_decrypt()

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

公開キーで暗号化されたメッセージを復号化します。 keyは、暗号化に使用した公開キーに対応する秘密キーでなければなりません。 秘密キーがパスワードで保護されている場合は、そのパスワードをpswで指定しなければなりません。 パスワードはないが、オプションを指定したい場合は空のパスワードを指定する必要があります。

pgp_pub_decryptbytea型のデータを復号化することはできません。 これは無効な文字データの出力を防止するためです。 元のテキストのデータをpgp_pub_decrypt_byteaで復号化することが正しい方法です。

optionsパラメータには後述のオプション設定を含めることができます。

F.28.3.5. pgp_key_id()

pgp_key_id(bytea) returns text

pgp_key_idはPGP公開キーまたは秘密キーのキーIDを取り出します。 暗号化されたメッセージが指定された場合は、データの暗号化に使用されたキーIDを与えます。

2つの特殊なキーIDを返すことがあります。

  • SYMKEY

    メッセージは対称キーで暗号化されました。

  • ANYKEY

    メッセージは公開キーで暗号化されましたが、キーIDが消去されていました。 つまり、どれで復号化できるかを判定するためにはすべての秘密キーを試行しなければならないことを意味します。 pgcrypto自身はこうしたメッセージを生成しません。

異なるキーが同一IDを持つ場合があることに注意してください。 これは稀ですが、正常なイベントです。 この場合クライアントアプリケーションはどちらが当てはまるかを調べるために、ANYKEYの場合と同様に、それぞれのキーで復号化を試行しなければなりません。

F.28.3.6. armor(), dearmor()

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

PGPのASCIIアーマー形式にデータを隠す、または、データを取り出します。 ASCIIアーマーは基本的にCRC付きのBASE64という形式で、追加のフォーマットがあります。

F.28.3.7. PGP関数用のオプション

オプションはGnuPGに似せて命名しています。 オプションの値は等号記号の後に指定しなければなりません。 複数のオプションはカンマで区切ってください。 以下に例を示します。

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

convert-crlfを除くすべてのオプションは暗号化関数にのみ適用可能です。 復号化関数はPGPデータからこうしたパラメータを入手します。

もっとも興味深いオプションはおそらくcompress-algounicode-modeでしょう。 残りはデフォルトで問題ないはずです。

F.28.3.7.1. cipher-algo

使用する暗号アルゴリズム。

値: bf, aes128, aes192, aes256 (OpenSSL-only: 3descast5)
デフォルト: aes128
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt

F.28.3.7.2. compress-algo

使用する圧縮アルゴリズム。 PostgreSQLがzlib付きで構築されている場合のみ利用可能です。

値:
 0 - 非圧縮
 1 - ZIP圧縮
 2 - ZLIB圧縮 (ZIPにメタデータとブロックCRCを加えたもの)
デフォルト: 0
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt

F.28.3.7.3. compress-level

どの程度圧縮するかです。 レベルが大きい程小さくなりますが、低速になります。 0は圧縮を無効にします。

値: 0, 1-9
デフォルト: 6
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt

F.28.3.7.4. convert-crlf

暗号化の際に\n\r\nに、復号化の際に\r\n\n に変換するかどうか。 RFC 4880では、テキストデータは改行コードとして\r\n を使用して格納すべきであると規定されています。 完全にRFC準拠の動作を行いたければ、これを使用してください。

値: 0, 1
デフォルト: 0
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt

F.28.3.7.5. disable-mdc

データをSHA-1で保護しません。 このオプションを使用することが良い唯一の理由は、SHA-1で保護されたパケットがRFC 4880に追加される前の、古いPGP製品との互換性を得るためです。 最近のgnupg.orgおよびpgp.comのソフトウェアではこれを正しくサポートしています。

値: 0, 1
デフォルト: 0
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt

F.28.3.7.6. enable-session-key

分離したセッションキーを使用します。 公開キー暗号では常に分離したセッションキーを使用します。 これは対称キー暗号向けのもので、デフォルトではS2Kをそのまま使用します。

値: 0, 1
デフォルト: 0
適用範囲: pgp_sym_encrypt

F.28.3.7.7. s2k-mode

使用するS2Kアルゴリズム。

値:
  0 - ソルト無。危険です
  1 - ソルト有。固定繰り返し回数。
  3 - 可変繰り返し回数
デフォルト: 3
適用範囲: pgp_sym_encrypt

F.28.3.7.8. s2k-digest-algo

S2K計算で使用するダイジェストアルゴリズム。

値: md5, sha1
デフォルト: sha1
適用範囲: pgp_sym_encrypt

F.28.3.7.9. s2k-cipher-algo

分離したセッションキーの暗号化に使用する暗号。

値: bf, aes, aes128, aes192, aes256
デフォルト: cipher-algoを使用
適用範囲: pgp_sym_encrypt

F.28.3.7.10. unicode-mode

テキストデータをデータベース内部符号化方式からUTF-8に変換して戻すかどうかです。 データベースがすでにUTF-8であれば、変換は起こらず、データにUTF-8としてタグが付くのみです。 このオプションがないと、何も行われません。

値: 0, 1
デフォルト: 0
適用範囲: pgp_sym_encrypt, pgp_pub_encrypt

F.28.3.8. GnuPGを使用したキーの生成

新しいキーを生成します。

gpg --gen-key

推奨するキー種類は"DSA and Elgamal"です。

RSA暗号化のためには、マスタとしてDSAまたはRSAで署名のみのキーを作成し、そしてgpg --edit-keyを使用してRSAで暗号化された副キーを追加しなければなりません

キーを列挙します。

gpg --list-secret-keys

ASCIIアーマー形式で公開キーをエキスポートします。

gpg -a --export KEYID > public.key

ASCIIアーマー形式の秘密キーをエキスポートします。

gpg -a --export-secret-keys KEYID > secret.key

PGP関数にこれらのキーを渡す前にdearmor()`を使用する必要があります。 バイナリデータを扱うことができる場合、gpgから-aフラグを省略することができます。

詳細はman gpghttp://www.gnupg.org/gph/en/manual.htmlThe GNU Privacy Handbook、http://www.gnupg.orgサイトの各種文書を参照してください。

F.28.3.9. PGPコードの制限

  • 署名に関するサポートはありません。 これはまた、暗号化副キーがマスタキーに属しているかどうか検査しないことを意味します。

  • マスタキーとして暗号化キーをサポートしません。 一般的にこうした状況は現実的ではありませんので、問題にならないはずです。

  • 複数の副キーに関するサポートはありません。 よくありますので、これは問題になりそうに見えます。 一方、通常のGPG/PGPキーをpgcryptoで使用すべきではありません。 使用する状況が多少異なりますので新しく作成してください。

F.28.4. 暗号化そのものを行う関数

これらの関数はデータ全体を暗号化するためだけに実行します。 PGP暗号化の持つ先端的な機能はありません。 したがって、大きな問題がいくつか存在します。

  1. 暗号キーとしてユーザキーをそのまま使用します。

  2. 暗号化されたデータが変更されたかどうかを確認するための整合性検査をまったく提供しません。

  3. ユーザが、IVをも含め暗号化パラメータ自体をすべて管理していることを想定しています。

  4. テキストは扱いません。

このため、PGP暗号化の導入もあり、暗号化のみの関数はあまり使用されません。

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

typeで指定した暗号方法を使用してデータの暗号化・復号化を行います。 type文字列の構文は以下の通りです。

algorithm [ - mode ] [ /pad: padding ]

ここでalgorithmは以下の1つです。

またmodeは以下の1つです。

paddingは以下の1つです。

このため、例えば以下は同じです。

encrypt(data, 'fooz', 'bf')
encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')

encrypt_ivおよびdecrypt_ivでは、ivパラメータはCBCモード用の初期値となります。 ECBでは無視されます。 正確にブロック長でない場合、切り抜かれるか、もしくはゼロで埋められます。 このパラメータがない場合、関数のデフォルト値はすべてゼロです。

F.28.5. ランダムデータ関数

gen_random_bytes(count integer) returns bytea

暗号論的に強いランダムなcountバイトを返します。 一度に最大で1024バイトを抽出することができます。 ランダム性ジェネレータプールを空にすることを防止するためのものです。

F.28.6. 注釈

F.28.6.1. 構築

pgcryptoは自身で主PostgreSQLのconfigureスクリプトの検出結果に従って構築します。 構築に影響するオプションは--with-zlib--with-opensslです。

ZLIB付きでコンパイルされた場合、PGP暗号化関数は暗号化前にデータを圧縮することができます。

OpenSSL付きでコンパイルされた場合、より多くのアルゴリズムが利用できるようになります。 また、OpenSSLがより最適化されたBIGNUM関数を持つため、公開キー暗号化関数は高速になります。

表 F-18. OpenSSLの有無による機能のまとめ

機能組込みOpenSSL付き
MD5
SHA1
SHA224/256/384/512○(注1)
この他のダイジェストアルゴリズム×○(注2)
Blowfish
AES○(注3)
DES/3DES/CAST5×
暗号化そのもの
PGP対称暗号化
PGP公開キー暗号化

  1. SHA2はOpenSSLバージョン0.9.8から含まれています。 これより古いバージョンでは、pgcryptoは組込みのコードを使用します。

  2. OpenSSLがサポートする任意のダイジェストアルゴリズムが自動的に選択されます。 これは、明示的にサポートされなければならない暗号では使用できません。

  3. AESはOpenSSLバージョン0.9.7から含まれています。 これより古いバージョンでは、pgcryptoは組込みのコードを使用します。

F.28.6.2. NULLの扱い

標準SQLの通り、引数のいずれかがNULLの場合、すべての関数はNULLを返します。 注意せずに使用すると、これがセキュリティ上の問題になるかもしれません。

F.28.6.3. セキュリティ上の制限

pgcryptoの関数はすべてデータベースサーバ内部で実行されます。 これは、pgcryptoとクライアントアプリケーションとの間でやり取りされるデータはすべて平文であることを意味します。 したがって、以下を行う必要があります。

  1. ローカルまたはSSL接続で接続

  2. システム管理者およびデータベース管理者を信頼

これらが不可能であれば、クライアントアプリケーション内で暗号化する方が望まれます。

F.28.6.4. 有用な文書

F.28.6.5. 技術的な参考情報

F.28.7. 作者

Marko Kreen

pgcryptoは以下のソースを使用しています。

アルゴリズム

作者

元ソース

DES cryptDavid Burren and othersFreeBSD libcrypt
MD5 cryptPoul-Henning KampFreeBSD libcrypt
Blowfish cryptSolar Designerwww.openwall.com
Blowfish cipherSimon TathamPuTTY
Rijndael cipherBrian GladmanOpenBSD sys/crypto
MD5 and SHA1WIDE ProjectKAME kame/sys/crypto
SHA256/384/512 Aaron D. GiffordOpenBSD sys/crypto
BIGNUM mathMichael J. Frombergerdartmouth.edu/~sting/sw/imath