由于以前设计不佳的结构,我必须使用的当前数据库将用户的密码存储为文本。
现在,我正在构建一个必须使用这些密码的前端部分,我当然不想发送未加密的密码。
我的想法是编写一个 Oracle 函数来加密和解密文本密码,并在将返回加密数据的存储过程中使用这些函数。
在 Oracle 中这样做的最佳方法是什么?
由于以前设计不佳的结构,我必须使用的当前数据库将用户的密码存储为文本。
现在,我正在构建一个必须使用这些密码的前端部分,我当然不想发送未加密的密码。
我的想法是编写一个 Oracle 函数来加密和解密文本密码,并在将返回加密数据的存储过程中使用这些函数。
在 Oracle 中这样做的最佳方法是什么?
如果您想编写自己的函数来加密和解密数据,您只需调用DBMS_CRYPTO encrypt
和decrypt
带有适当参数的方法(即选择您的加密算法、您的密钥等)。
当然,如果您编写自己的例程,假设您将密钥存储在数据库中或数据库可以访问的某个位置,那么您在安全方面并没有做太多事情。通过网络发送未加密的密码是不好的,但将未加密的密码存储在数据库中通常更糟糕(或者如果decrypt
数据库中有一种方法可以访问密钥来解密数据,则使用加密的密码)。从数据库中窃取数据通常比嗅探通过网络发送的数据以找到密码要容易得多。
当然,正确的答案是重新构建系统,以便您根本不存储密码。您应该存储DBMS_CRYPTO
不可逆的密码哈希(您也可以使用包生成)。
看看 DBMS_CRYPTO
它具有加密和解密内置数据的方法。比自己编写更好。
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_crypto.htm
这是一个使用 ( ) 加密密码的打包函数(成功实现DBMS_CRYPTO
)-
CREATE OR REPLACE
PACKAGE BODY encrypt_paswd
AS
G_CHARACTER_SET VARCHAR2(10) := 'AL32UTF8';
G_STRING VARCHAR2(32) := '12345678901234567890123456789012';
G_KEY RAW(250) := utl_i18n.string_to_raw
( data => G_STRING,
dst_charset => G_CHARACTER_SET );
G_ENCRYPTION_TYPE PLS_INTEGER := dbms_crypto.encrypt_aes256
+ dbms_crypto.chain_cbc
+ dbms_crypto.pad_pkcs5;
------------------------------------------------------------------------
--Encrypt a password
--Salt the password
------------------------------------------------------------------------
FUNCTION encrypt_val( p_val IN VARCHAR2 ) RETURN RAW
IS
l_val RAW(32) := UTL_I18N.STRING_TO_RAW( p_val, G_CHARACTER_SET );
l_encrypted RAW(32);
BEGIN
l_val := utl_i18n.string_to_raw
( data => p_val,
dst_charset => G_CHARACTER_SET );
l_encrypted := dbms_crypto.encrypt
( src => l_val,
typ => G_ENCRYPTION_TYPE,
key => G_KEY );
RETURN l_encrypted;
END encrypt_val;
END encrypt_paswd;
这使用encrypt_aes256
-“高级加密标准。分组密码。使用 256 位密钥大小。” , chain_cbc
- “密码块链接。明文在加密之前与前一个密文块进行异或运算。” - pad_pkcs5
“提供符合 PKCS #5:基于密码的加密标准的填充”。
除此之外,您还可以创建一个类似的函数来解密。像 -
FUNCTION decrypt_val( p_val IN RAW ) RETURN VARCHAR2
IS
l_decrypted RAW(32);
l_decrypted_string VARCHAR2(32);
l_user VARCHAR2(32);
BEGIN
SELECT user
INTO l_user
FROM dual;
if l_user = 'ADMIN' -- you can restrict usage of decrypt to certain db users only.
then
l_decrypted := dbms_crypto.decrypt
( src => p_val,
typ => G_ENCRYPTION_TYPE,
key => G_KEY );
l_decrypted_string := utl_i18n.raw_to_char
( data => l_decrypted,
src_charset => G_CHARACTER_SET );
RETURN l_decrypted_string;
else
RAISE_APPLICATION_ERROR(-20101, 'You are not authorized to use this function - decrypt_val()');
end if;
RETURN 'Unknown';
END decrypt_val;
您还可以考虑在将包编译到数据库中之前先包装包wrap iname=package_name.pkb
,然后再编译生成的plb
.
或者你可以像这样使用密码散列算法 pbkdf2 http://mikepargeter.wordpress.com/2012/11/26/pbkdf2-in-oracle/ 我用 rfc https://www.ietf.org/验证了结果rfc/rfc6070.txt它工作正常。
检查此链接以获取最小迭代和 key_size:PBKDF2 推荐的密钥大小? 请注意,结果是长度的两倍,因为它是十六进制编码的。
我们将这些列存储在数据库中
PASSWORD_HASH VARCHAR2(512 BYTE),
PASSWORD_SALT VARCHAR2(256 BYTE),
PASSWORD_ITERATIONS NUMBER(10),
PASSWORD_HASH_METHOD VARCHAR2(30 BYTE),
PASSWORD_CHANGED_DT DATE
hash、salt 和迭代用于提供 pbkdf2 算法 hash_method 用于迁移目的, changed_dt 用于使密码过期
用户此代码绝对有效
create or replace PACKAGE "PKG_LOGI_PWD_REG"
AS
function ENCRYPT_VAL( P_VAL in varchar2 ) return varchar2;
function DECRYPT_VAL( P_VAL in raw ) return varchar2;
end;
/
create or replace PACKAGE BODY "PKG_LOGI_PWD_REG"
as
FUNCTION decrypt_val( p_val IN RAW ) RETURN VARCHAR2
IS
l_decrypted RAW(32);
l_decrypted_string VARCHAR2(32);
L_USER varchar2(32);
L_CHARACTER_SET varchar2(10);
L_STRING varchar2(32);
L_KEY raw(250);
L_ENCRYPTION_TYPE PLS_INTEGER;
BEGIN
L_KEY := UTL_I18N.STRING_TO_RAW
( data => '98345678901234567890123456789012',
DST_CHARSET => 'AL32UTF8' );
L_ENCRYPTION_TYPE := dbms_crypto.encrypt_aes256
+ DBMS_CRYPTO.CHAIN_CBC
+ DBMS_CRYPTO.PAD_PKCS5;
l_decrypted := dbms_crypto.decrypt
( SRC => P_VAL,
TYP => L_ENCRYPTION_TYPE,
key => L_KEY );
l_decrypted_string := utl_i18n.raw_to_char
( data => l_decrypted ,
src_charset => 'AL32UTF8' );
RETURN l_decrypted_string;
end DECRYPT_VAL;
FUNCTION encrypt_val( p_val IN VARCHAR2 ) RETURN VARCHAR2
is
L_VAL RAW(32);
L_ENCRYPTED raw(32);
L_CHARACTER_SET varchar2(10);
L_STRING varchar2(32);
L_KEY RAW(250);
L_ENCRYPTION_TYPE PLS_INTEGER;
begin
L_KEY := UTL_I18N.STRING_TO_RAW
( data => '98345678901234567890123456789012',
DST_CHARSET => 'AL32UTF8' );
L_ENCRYPTION_TYPE := dbms_crypto.encrypt_aes256
+ DBMS_CRYPTO.CHAIN_CBC
+ DBMS_CRYPTO.PAD_PKCS5;
L_VAL := utl_i18n.string_to_raw
( data => p_val,
dst_charset => 'AL32UTF8' );
L_ENCRYPTED := dbms_crypto.encrypt
( SRC => L_VAL,
TYP => L_ENCRYPTION_TYPE,
key => L_KEY );
return L_ENCRYPTED;
EXCEPTION when OTHERS then
RETURN SQLCODE||'-'||SQLERRM;
end ENCRYPT_VAL;
end PKG_LOGI_PWD_REG;
/