[英]Oracle SQL Insert Trigger to Hash Password is not working (Issue with CHAR)
I'm trying to build a insert trigger to hash a string into a SH-256 password:我正在尝试为 hash 构建一个插入触发器,将字符串转换为 SH-256 密码:
DROP TABLE client;
CREATE TABLE client (
id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
password CHAR(64) NOT NULL
);
-- Add Insert Trigger
CREATE OR REPLACE TRIGGER client_hash_trigger
BEFORE INSERT
ON client
FOR EACH ROW
DECLARE
password_hash CHAR(64);
BEGIN
SELECT STANDARD_HASH(:NEW.password, 'SHA256') INTO password_hash FROM dual;
:NEW.password := password_hash;
END;
INSERT INTO client VALUES (DEFAULT, 'BruteForce');
When I run the SQL I'll get a wrong hash in my table.当我运行 SQL 时,我的表中会出现错误的 hash。 When I change booth char to varchar it's working.
当我将booth char更改为varchar时,它正在工作。 When I put the STANDARD_HASH directly into the insert line it working as well.
当我将 STANDARD_HASH 直接放入插入行时,它也可以正常工作。 Where is the issue?
问题出在哪里?
You use a char
column to store a RAW
value, returned by standard_hash
;您使用
char
列存储由standard_hash
返回的RAW
值; you should cast your RAW
to a different type, for example by UTL_RAW
procedures.您应该将
RAW
转换为不同的类型,例如通过UTL_RAW
程序。
Also, consider that when you define a column as char(64)
you mean that all the values will be 64 characters long, so 'BruteForce'
will be padded to 64 characters and the hash will be computed on the padded string.另外,请考虑当您将列定义为
char(64)
时,您的意思是所有值的长度均为 64 个字符,因此'BruteForce'
将被填充为 64 个字符,并且 hash 将在填充的字符串上计算。
If you try to add something like如果您尝试添加类似
dbms_output.put_line('Password is: "' || :NEW.password || '"');
to your trigger, you will see:到你的触发器,你会看到:
Password is: "BruteForce "
Varchar2
, differently will only use the characters you need, so the hash would be computed exactly on 'BruteForce'
, that's why if you switch to varchar2
you see a different hash result. Varchar2
,不同的是只会使用您需要的字符,因此 hash 将在'BruteForce'
上精确计算,这就是为什么如果您切换到varchar2
您会看到不同的 hash 结果。
If you write something like如果你写类似
INSERT INTO client VALUES (DEFAULT, STANDARD_HASH('BruteForce', 'SHA256'));
the hash is computed not on che column content (which is a char(64)
, thus being 64 characters long), but over the string 'BruteForce'
and the resulting hash in casted into char(64)
. hash 不是在 che 列内容(这是一个
char(64)
,因此长度为 64 个字符)上计算,而是在字符串'BruteForce'
和生成的 hash 中计算,并转换为char(64)
。 What Oracle does is: Oracle 所做的是:
char(64)
char(64)
The issue with the padding comes when you use the trigger because the trigger is based on the table structure, so :NEW.password
is a char(64)
value and the hash is computed over the already casted value, so over a string made by 64 characters.使用触发器时会出现填充问题,因为触发器基于表结构,因此
:NEW.password
是一个char(64)
值,并且 hash 是根据已转换的值计算的,因此通过由64 个字符。 What happens with the trigger is触发器会发生什么
char(64)
, because you are inserting in a char(64)
column, thus paddingchar(64)
,因为您要插入char(64)
列,因此填充 [TL;DR] Use STANDARD_HASH( RTRIM(:new.PASSWORD ), 'SHA256' )
as you want to generate the password on 'BruteForce'
and not 'BruteForce '
(etc.) padded with white-space to a length of 64 characters (which is what using CHAR(64)
would give you). [TL;DR] 使用
STANDARD_HASH( RTRIM(:new.PASSWORD ), 'SHA256' )
因为你想在'BruteForce'
上生成密码,而不是在'BruteForce '
(等)上用空格填充到 64字符(这是使用CHAR(64)
会给你的)。
If you don't want to use a salt (which you should be using in this day-and-age) then just right-trim the password to get rid of the trailing white-space that the CHAR
data type have padded the string with:如果您不想使用盐(您应该在当今时代使用),那么只需正确修剪密码以消除
CHAR
数据类型填充字符串的尾随空格:
CREATE OR REPLACE TRIGGER client_hash_trigger
BEFORE INSERT ON client
FOR EACH ROW
BEGIN
SELECT STANDARD_HASH( RTRIM( :new.PASSWORD ), 'SHA256' )
INTO :new.PASSWORD
FROM DUAL;
END;
/
Then:然后:
CREATE TABLE client (
id NUMBER(10,0)
GENERATED ALWAYS AS IDENTITY
CONSTRAINT client__id__pk PRIMARY KEY,
password CHAR(64)
NOT NULL
);
INSERT INTO client (id, password) VALUES (DEFAULT, 'BruteForce');
Will output:请问output:
SELECT id, password FROM client;
ID |身份证 | PASSWORD -: |:--------------------------------------------------------------- 1 |
密码 -: |:--------------------------------------------- ------------------ 1 | 6BAFE79E0E7360094A490C0B84F2D5BA1C5A6E5C6FE1E45C2E7850966698AF29
6BAFE79E0E7360094A490C0B84F2D5BA1C5A6E5C6FE1E45C2E7850966698AF29
Adapted from this answer改编自这个答案
If you also want to follow best practice, then you should salt the password before hashing:如果您还想遵循最佳实践,那么您应该在散列之前对密码进行加盐:
CREATE TABLE client (
id NUMBER(10,0)
GENERATED ALWAYS AS IDENTITY
CONSTRAINT client__id__pk PRIMARY KEY,
password CHAR(64)
NOT NULL,
password_salt VARCHAR2(61)
NOT NULL
);
Then your trigger is:那么你的触发器是:
CREATE TRIGGER client_hash_trigger
BEFORE INSERT OR UPDATE ON client
FOR EACH ROW
BEGIN
IF :new.PASSWORD = :old.PASSWORD THEN
-- Assume things haven't changed (The chances of a hash collision are vanishingly small).
-- Make sure the old salt is not replaced if the password hash hasn't changed.
:new.PASSWORD_SALT := :old.PASSWORD_SALT;
ELSE
-- Regenerate a new salt and hash the password.
:new.PASSWORD_SALT := DBMS_RANDOM.STRING( 'P', FLOOR( DBMS_RANDOM.VALUE( 40, 61 ) ) );
SELECT STANDARD_HASH ( :new.PASSWORD_SALT || RTRIM( :new.PASSWORD ), 'SHA256' )
INTO :new.PASSWORD
FROM DUAL;
END IF;
END;
/
And then:接着:
INSERT INTO client (id, password) VALUES (DEFAULT, 'BruteForce');
May output: 5 月 output:
SELECT * FROM client;
ID |身份证 | PASSWORD |
密码 | PASSWORD_SALT -: |:--------------------------------------------------------------- |:------------------------------------------ 1 |
PASSWORD_SALT -: |:--------------------------------------------- ------------------ |:------------------------------ ------------ 1 | 7465541FDF9379B2112D4E92150F594732139E94B79D0EB0247593B4E8CEB3E4 |
7465541FDF9379B2112D4E92150F594732139E94B79D0EB0247593B4E8CEB3E4 | dhj4GOC8E(xA&8b9f)j@"Y- o$G.UECR\go.SrFaZ<&
dhj4GOC8E(xA&8b9f)j@"Y-o$G.UECR\go.SrFaZ<&
A slight change to MT0 's answer to remove the context switch in the trigger by using dbms_crypto
instead of standard_hash
.通过使用
dbms_crypto
而不是standard_hash
对MT0的答案稍作更改,以删除触发器中的上下文切换。
CREATE OR REPLACE TRIGGER client_hash_trigger
BEFORE INSERT ON client
FOR EACH ROW
BEGIN
:new.PASSWORD := rawtohex(dbms_crypto.hash(src => utl_raw.cast_to_raw(:new.PASSWORD),
typ => dbms_crypto.hash_sh256));
END;
/
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.