一般来说数据库密码安全管理要考虑以下几个方面 :
1. 密码过期策略, 决定密码的有效期, 多长时间过期.PostgreSQL不支持密码过期策略, 但是可以通过其他方式来实现过期提醒.
PostgreSQL 不支持密码复用策略, 但是可以通过其他方式来实现强制密码复用策略.
PostgreSQL 不支持密码长度策略, 但是可以通过其他方式来实现强制密码长度策略.
PostgreSQL 不支持密码复杂度策略, 但是可以通过其他方式来实现强制密码复杂度策略.
PostgreSQL 不支持密码字典过滤策略, 但是可以通过其他方式来实现强制字典过滤策略.
加密存储在创建用户时使用encrypted password即可.
如果设置了password_encryption=off, 同时创建用户时不加encrypted, 那么密码将以明文存储.
例如 :
加密存储 :
digoal=# create role u1 login encrypted password 'digoal';
CREATE ROLE
digoal=# select * from pg_shadow where usename='u1';
usename | usesysid | usecreatedb | usesuper | usecatupd | userepl | passwd | valuntil | useconfig
---------+----------+-------------+----------+-----------+---------+-------------------------------------+----------+-----------
u1 | 26383 | f | f | f | f | md5bdf22a6622939c7c7ab2377eaca514d5 | |
(1 row)
明文存储 :
digoal=# set password_encryption=off;
SET
digoal=# create role u2 login password 'digoal';
CREATE ROLE
digoal=# select * from pg_shadow where usename='u2';
usename | usesysid | usecreatedb | usesuper | usecatupd | userepl | passwd | valuntil | useconfig
---------+----------+-------------+----------+-----------+---------+--------+----------+-----------
u2 | 26387 | f | f | f | f | digoal | |
(1 row)
PostgreSQL 不支持密码锁策略.
PostgreSQL 不支持密码保护策略.
目前PostgreSQL在密码管理这块做得较弱,
以下举例通过函数来实现其中的部分安全策略 :
创建一个字典表, 用于存放已经使用过的密码的md5值, 以及用来进行暴力破解的密码字典的md5值.
digoal=# create table pwd_dictionary(pwd text unique);
CREATE TABLE
创建一个记录用户最后一次修改密码时间的表. 用于实施密码过期提醒策略.
digoal=# create table user_pwd(rolename name not null unique, pwd_modify_time timestamp not null);
CREATE TABLE
创建用户的函数 :
digoal=# create or replace function create_role(i_rolename name, i_pwd text) returns void as $$
declare
v_length int := 8;
begin
-- 密码长度策略
if length(i_pwd) < v_length then
raise notice 'password too short, please use password long than %.', v_length;
return;
end if;
-- 密码复杂度策略, 包含数字, 字母大小写.
if not(i_pwd ~ '[a-z]' and i_pwd ~ '[A-Z]' and i_pwd ~ '[0-9]') then
raise notice 'password too simple, please ensure password contain a-z,A-Z and 0-9.';
return;
end if;
-- 密码复用策略, 不允许重复使用已经存在的密码.
-- 密码字典策略, 不允许使用密码字典中的密码.
insert into pwd_dictionary(pwd) values (md5(i_pwd));
-- 插入用户表, 记录用户最后一次修改密码的时间, 用于密码过期策略
insert into user_pwd(rolename, pwd_modify_time) values (i_rolename, now());
-- 创建用户
execute 'create role '||i_rolename||' encrypted password '||quote_literal(i_pwd);
raise notice 'create role % successed.', i_rolename;
return;
end;
$$ language plpgsql strict;
CREATE FUNCTION
创建用户密码过短, 不允许创建.
digoal=# select * from create_role('u4','pwd');
NOTICE: password too short, please use password long than 8.
create_role
-------------
(1 row)
创建用户密码过于简单, 不允许创建.
digoal=# select * from create_role('u4','abcdefee');
NOTICE: password too simple, please ensure password contain a-z,A-Z and 0-9.
create_role
-------------
(1 row)
创建用户正常.
digoal=# select * from create_role('u4','aA0ffffffff');
NOTICE: create role u4 successed.
create_role
-------------
(1 row)
创建用户密码与现有密码重复, 不允许创建.
digoal=# select * from create_role('new','aA0ffffffff');
ERROR: duplicate key value violates unique constraint "pwd_dictionary_pwd_key"
DETAIL: Key (pwd)=(2b9aa88182d13d35930180b4cc791beb) already exists.
CONTEXT: SQL statement "insert into pwd_dictionary(pwd) values (md5(i_pwd))"
PL/pgSQL function create_role(name,text) line 17 at SQL statement
修改用户密码的函数 :
digoal=# create or replace function alter_role_pwd(i_rolename name, i_pwd text) returns void as $$
declare
v_length int := 8;
begin
-- 密码长度策略
if length(i_pwd) < v_length then
raise notice 'password too short, please use password long than %.', v_length;
return;
end if;
-- 密码复杂度策略, 包含数字, 字母大小写.
if not(i_pwd ~ '[a-z]' and i_pwd ~ '[A-Z]' and i_pwd ~ '[0-9]') then
raise notice 'password too simple, please ensure password contain a-z,A-Z and 0-9.';
return;
end if;
-- 密码复用策略, 不允许重复使用已经存在的密码.
-- 密码字典策略, 不允许使用密码字典中的密码.
insert into pwd_dictionary(pwd) values (md5(i_pwd));
-- 更新用户表, 记录用户最后一次修改密码的时间, 用于密码过期策略
update user_pwd set pwd_modify_time=now() where rolename=i_rolename;
-- 修改用户密码
execute 'alter role '||i_rolename||' encrypted password '||quote_literal(i_pwd);
raise notice 'modify role % password successed.', i_rolename;
return;
end;
$$ language plpgsql strict;
CREATE FUNCTION
使用该函数修改用户密码
digoal=# select * from alter_role_pwd('u4','new');
NOTICE: password too short, please use password long than 8.
alter_role_pwd
----------------
(1 row)
digoal=# select * from alter_role_pwd('u4','new22222222');
NOTICE: password too simple, please ensure password contain a-z,A-Z and 0-9.
alter_role_pwd
----------------
(1 row)
digoal=# select * from alter_role_pwd('u4','new2222222z2');
NOTICE: password too simple, please ensure password contain a-z,A-Z and 0-9.
alter_role_pwd
----------------
(1 row)
digoal=# select * from alter_role_pwd('u4','new2222222z2A');
NOTICE: modify role u4 password successed.
alter_role_pwd
----------------
(1 row)
digoal=# select * from alter_role_pwd('u4','new2222222z2A');
ERROR: duplicate key value violates unique constraint "pwd_dictionary_pwd_key"
DETAIL: Key (pwd)=(9a5c46207db775d4d98e64d427481cbc) already exists.
CONTEXT: SQL statement "insert into pwd_dictionary(pwd) values (md5(i_pwd))"
PL/pgSQL function alter_role_pwd(name,text) line 17 at SQL statement
密码过期提醒 :
因为密码最后一次修改时间已经更新到user_pwd表, 所以结合这个可以在系统crontab中或者nagios监控软件中实施密码过期提醒.
digoal=# select * from user_pwd ;
rolename | pwd_modify_time
----------+----------------------------
u4 | 2013-05-25 18:21:59.376404
(1 row)
[小结]
1. 为了密码安全性, 在PostgreSQL 中, 创建用户请使用create_role函数, 不要直接使用create role 命令.
2. 修改用户密码请使用alter_role_pwd函数. 不要直接使用alter role 命令.
3. 密码过期提醒请结合nagios软件或者操作系统crontab来实现邮件提醒数据库管理员.