【问题标题】:Hash a column in postgres using sha-256使用 sha-256 在 postgres 中散列一列
【发布时间】:2021-12-08 13:30:02
【问题描述】:

我有一个 postrges 数据库,其中一个表包含我使用 python 函数生成的关键代码。我希望能够对该列进行散列,以便每次将键代码添加到其中时,对键进行散列。我怎样才能让 postgres 做到这一点?或者存储这些代码的最佳方式是什么。这是我想在表中散列的列示例。

 key_codes | 
-----------+
 L7G4J83K  |        
 J70KG169  |         
 L69E540K  |        
 GL8E9C3J  |         
 6C0LE215  |         
 9G01C8JA  |         
 1G9KC58A  |         

【问题讨论】:

  • 为什么要散列这些小值?它是用来混淆的吗?某种 cookie/auth?

标签: postgresql


【解决方案1】:

使用触发器在插入和更新时设置哈希列。对于 SHA-256,use the pgcrypto extension module's digest function

由于您没有指定您的 PostgreSQL 版本,我假设您在以下示例中使用的是当前的 9.2。

以下是调用 sha256 摘要函数的方法:

regress=# CREATE EXTENSION pgcrypto;
CREATE EXTENSION

regress=> SELECT digest('blah', 'sha256');
                               digest                               
--------------------------------------------------------------------
 \x8b7df143d91c716ecfa5fc1730022f6b421b05cedee8fd52b1fc65a96030ad52
(1 row)

请注意,CREATE EXTENSION 函数必须以超级用户身份运行。

触发器非常简单。假设您的表格如下所示:

CREATE TABLE some_table ( key_codes text, hash bytea );

CREATE OR REPLACE FUNCTION hash_update_tg() RETURNS trigger AS $$
BEGIN
    IF tg_op = 'INSERT' OR tg_op = 'UPDATE' THEN
        NEW.hash = digest(NEW.key_codes, 'sha256');
        RETURN NEW;
    END IF;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER some_table_hash_update 
BEFORE INSERT OR UPDATE ON some_table 
FOR EACH ROW EXECUTE PROCEDURE hash_update_tg();

用法:

regress=> INSERT INTO some_table(key_codes) VALUES ('fred');
INSERT 0 1
regress=> SELECT * FROM some_table;
 key_codes |                                hash                                
-----------+--------------------------------------------------------------------
 fred      | \xd0cfc2e5319b82cdc71a33873e826c93d7ee11363f8ac91c4fa3a2cfcd2286e5
(1 row)

您可以通过设置更新触发器的条件来减少触发器执行的开销。而不是上面的CREATE TRIGGER,使用这两个:

CREATE TRIGGER some_table_hash_insert
BEFORE INSERT ON some_table 
FOR EACH ROW 
EXECUTE PROCEDURE hash_update_tg(); 

CREATE TRIGGER some_table_hash_update 
BEFORE UPDATE ON some_table 
FOR EACH ROW 
WHEN ( NEW.key_codes IS DISTINCT FROM OLD.key_codes ) 
EXECUTE PROCEDURE hash_update_tg(); 

【讨论】:

  • 为什么不用CREATE TRIGGER ... BEFORE INSERT OR UPDATE 而不是IF tg_op = 'INSERT' OR tg_op = 'UPDATE'
  • 感谢克雷格,这真的很有帮助。
  • + IF NEW.key_codes IS DISTINCT FROM OLD.key_codes THEN NEW.hash = ...。哈希函数真的很慢。
  • @CraigRinger 谢谢。不知道 WHEN ( condition ) 的触发器。
  • @user72840184 表示二进制的十六进制表示
【解决方案2】:

从 PostgreSQL 11 开始,您可以使用内置函数来计算哈希值:

SELECT sha256('hello world!');
-- x7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9

db<>fiddle demo

Other Binary String Functions

+----------------+--------------+---------------+
|   Function     | Return Type  |  Description  |
+----------------+--------------+---------------+
| sha224(bytea)  | bytea        | SHA-224 hash  |
| sha256(bytea)  | bytea        | SHA-256 hash  |
| sha384(bytea)  | bytea        | SHA-384 hash  |
| sha512(bytea)  | bytea        | SHA-512 hash  |
+----------------+--------------+---------------+

请注意,由于历史原因,函数 md5 返回文本类型的十六进制编码值,而 SHA-2 函数返回类型 bytea。使用函数 encode 和 decode 在两者之间进行转换,例如 encode(sha256('abc'), 'hex') 以获得十六进制编码的文本表示。

【讨论】:

  • 为什么以x开头?
  • @user72840184 I 是指示它以十六进制返回值
  • 请注意,如果输入字符串包含反斜杠 (invalid input syntax for type bytea),这将不起作用。
【解决方案3】:

从 PostgreSQL 12 开始,您可以使用生成的列;它可以很简单:

CREATE TABLE codes (
    ...,
    key_code text,
    sha_code text GENERATED ALWAYS AS (encode(sha256(key_code::bytea), 'hex')) STORED
);

与其他方法一样,如果您还不是架构的一部分,您可能需要CREATE EXTENSION IF NOT EXISTS pgcrypto;

生成的列可以像其他列一样被选择和索引:

CREATE INDEX idx_sha_codes ON codes USING btree (sha_code);
SELECT id, sha_code FROM codes WHERE sha_code = '...';

有明显的限制,例如他们不能引用其他生成的列或其他表。但是,这种派生值几乎是一个完美的用例。更多信息请访问https://www.postgresql.org/docs/12/ddl-generated-columns.html

【讨论】:

  • 这种方法似乎会引发错误。 [42P17] ERROR: generation expression is not immutable
  • 这是直接取自一个真实的工作应用程序,所以不,它不会引发错误。如果您的表达式有所不同,请确保它是不可变的结果,否则 postgresql 将不允许它作为生成的列。
  • 你为什么把它编码成文本?我们可以将其保存为 bytea 列吗?
  • 它被编码为文本以适应原始问题、应用程序中使用的 ORM 的自然行为以及作者(我自己)使用人类可读格式的倾向首先,对于 SHA,常见的选择是 hex 或 base64。如果您愿意,您确实可以将其存储为 bytea。在我的情况下,这会将转换的负担转移到读取或应用程序上,因此结果将是一个清洗,如果需要任何后续调试或人为驱动的分析,肯定会更糟。
猜你喜欢
  • 2018-07-14
  • 2011-06-28
  • 2016-04-10
  • 2019-02-24
  • 1970-01-01
  • 2017-02-13
  • 2011-05-18
  • 2023-04-06
  • 1970-01-01
相关资源
最近更新 更多