pgp_sym_encrypt/pgp_sym_decrypt错误处理

Cre*_*ion 7 encryption postgresql

我一直在使用MySQL作为数据库,并计划转移到postgresql.我在整个应用程序中广泛使用了MySQL中的aes_encrypt和aes_decrypt函数.因此,只要加密/解密失败,MySQL就会自动返回'null'.

我不确定如何在postgresql中处理相同的内容.尝试使用pgp_sym_encrypt/pgp_sym_decrypt函数.如果加密密钥错误,则会抛出"错误的密钥/损坏的数据"错误.我尝试搜索一些可以捕获此错误并返回'null'的函数,因为我不需要修改我的代码.我一直在寻找但找不到一个.

是否有人为个别查询使用了任何错误处理机制?我发现可以对程序进行错误处理.但是,我必须完全重写整个应用程序.

如果你可以分享一些细节,那将会有很大的帮助.谢谢.

Cra*_*ger 6

如果您希望避免修改代码并让函数在出错时返回,您可以通过将它们包装在使用块捕获错误的NULLPL/PgSQL 函数中来实现。BEGIN ... EXCEPTION

为此,我首先获取错误的 SQLSTATE:

regress=# \set VERBOSITY verbose
regress=# SELECT pgp_sym_decrypt('fred','key');
ERROR:  39000: Wrong key or corrupt data
LOCATION:  decrypt_internal, pgp-pgsql.c:607
Run Code Online (Sandbox Code Playgroud)

我可以直接在错误处理程序中使用它,但我更喜欢使用符号名称,因此我在附录 A - 错误代码中查找与 39000 相关的错误名称,发现它是通用函数调用错误external_routine_invocation_exception。虽然不像我们希望的那么具体,但也可以了。

现在需要一个包装函数。必须定义类似的东西,为pgp_sym_decrypt您希望支持的每个重载签名定义一个函数。对于(bytea,text)返回 的形式text,例如:

CREATE OR REPLACE FUNCTION pgp_sym_decrypt_null_on_err(data bytea, psw text) RETURNS text AS $$
BEGIN
  RETURN pgp_sym_decrypt(data, psw);
EXCEPTION
  WHEN external_routine_invocation_exception THEN
    RAISE DEBUG USING
       MESSAGE = format('Decryption failed: SQLSTATE %s, Msg: %s',
                        SQLSTATE,SQLERRM),
       HINT = 'pgp_sym_encrypt(...) failed; check your key',
       ERRCODE = 'external_routine_invocation_exception';
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

我选择在DEBUG级别消息中保留原始错误。这是原始和包装器的比较,具有完整的消息详细程度和调试级别输出。

启用调试输出以显示RAISE. 请注意,它还显示*调用的原始查询文本pgp_decrypt_sym,包括参数。

regress=# SET client_min_messages = DEBUG;
Run Code Online (Sandbox Code Playgroud)

如果启用了详细日志记录,新包装的函数仍会报告错误,但返回NULL

regress=# SELECT pgp_sym_decrypt_null_on_err('redsdfsfdsfd','bobsdf');
LOG:  00000: statement: SELECT pgp_sym_decrypt_null_on_err('redsdfsfdsfd','bobsdf');
LOCATION:  exec_simple_query, postgres.c:860
DEBUG:  39000: Decryption failed: SQLSTATE 39000, Msg: Wrong key or corrupt data
HINT:  pgp_sym_encrypt(...) failed; check your key
LOCATION:  exec_stmt_raise, pl_exec.c:2806
 pgp_sym_decrypt_null_on_err
-----------------------------

(1 row)
Run Code Online (Sandbox Code Playgroud)

与原始版本相比,失败了:

regress=# SELECT pgp_sym_decrypt('redsdfsfdsfd','bobsdf');
LOG:  00000: statement: SELECT pgp_sym_decrypt('redsdfsfdsfd','bobsdf');
LOCATION:  exec_simple_query, postgres.c:860
ERROR:  39000: Wrong key or corrupt data
LOCATION:  decrypt_internal, pgp-pgsql.c:607
Run Code Online (Sandbox Code Playgroud)

请注意,两种形式都显示了函数失败时调用的参数。如果您使用了绑定参数(“准备好的语句”),则不会显示这些参数,但如果您使用数据库内加密,您仍应将日志视为安全关键。

就我个人而言,我认为最好在应用程序中进行加密,这样数据库就永远无法访问密钥。