PostgreSQL password security

一般来说数据库密码安全管理要考虑以下几个方面 : 

1. 密码过期策略, 决定密码的有效期, 多长时间过期. 

PostgreSQL不支持密码过期策略, 但是可以通过其他方式来实现过期提醒.


2. 密码复用策略, 密码修改时需要对比以前的密码, 多少次以后才可以复用, 或者永不能使用与以前密码相同的密码.

PostgreSQL 不支持密码复用策略, 但是可以通过其他方式来实现强制密码复用策略.


3. 密码长度策略, 密码的最小长度, 太短的话很容易被穷举法破解, 一般至少使用8位以上长度的密码.

PostgreSQL 不支持密码长度策略, 但是可以通过其他方式来实现强制密码长度策略.


4. 密码复杂度策略, 密码包含的字符复杂度, 一般要求包含数字字母大小写以及特殊字符. 另外不要使用与数据库名, 用户名相似或一致的密码.

PostgreSQL 不支持密码复杂度策略, 但是可以通过其他方式来实现强制密码复杂度策略.


5. 密码字典过滤, 过滤掉一些用户常用的或者有意义的字符, 也是为了避免密码很容易被穷举破解. 

PostgreSQL 不支持密码字典过滤策略, 但是可以通过其他方式来实现强制字典过滤策略.


6. 密码存储策略, 使用加密存储, 不要使用明文存储. 
加密存储在创建用户时使用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)


7. 密码锁策略, 密码输入错误多少次后锁用户, 多长时间解锁.

PostgreSQL 不支持密码锁策略.


8. 密码保护策略, 密码输入错误多少次后延迟认证. 可用来防止暴力破解.
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来实现邮件提醒数据库管理员.
是的,有很多使用C#的Npgsql.dll以及Mono.Security.dll连接PostgreSQL库并使用MD5和SHA-256认证方法的案例。以下是一个使用MD5认证方法的示例代码: ``` using System; using System.Security.Cryptography; using System.Text; using Npgsql; class Program { static void Main(string[] args) { var connString = "Host=myserver;Username=myuser;Password=mypassword;Database=mydatabase"; using (var conn = new NpgsqlConnection(connString)) { conn.Open(); // Get the salt var saltCommand = new NpgsqlCommand("SELECT rolpassword FROM pg_authid WHERE rolname = @user", conn); saltCommand.Parameters.AddWithValue("user", "myuser"); var salt = (string)saltCommand.ExecuteScalar(); // Calculate the MD5 hash with salt var md5Password = CalculateMD5Password("mypassword", salt); // Login with MD5 password var loginCommand = new NpgsqlCommand("SELECT 1", conn); loginCommand.ExecuteNonQuery(); conn.Close(); } } // Function to calculate the MD5 password hash with salt private static string CalculateMD5Password(string password, string salt) { var passwordBytes = Encoding.UTF8.GetBytes(password); var saltBytes = Encoding.UTF8.GetBytes(salt); var md5 = new MD5CryptoServiceProvider(); var hashBytes = md5.ComputeHash(passwordBytes.Concat(saltBytes).ToArray()); return "md5" + BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); } } ``` 上述代码中,我们首先创建一个Npgsql连接,并打开它。然后,我们发送一个查询来获取用户的salt值。我们使用计算MD5哈希值的函数来计算密码的哈希值,并将salt值附加到密码的末尾。最后,我们使用MD5密码登录,并关闭连接。 对于SHA-256认证方法,您可以使用以下示例代码: ``` using Npgsql; class Program { static void Main(string[] args) { var connString = "Host=myserver;Username=myuser;Password=mypassword;Database=mydatabase"; using (var conn = new NpgsqlConnection(connString)) { conn.Open(); // Login with SHA-256 password var loginCommand = new NpgsqlCommand("SELECT 1", conn); loginCommand.ExecuteNonQuery(); conn.Close(); } } } ``` 在上述代码中,我们没有使用任何密码哈希函数,因为SHA-256认证方法不需要使用salt值。我们直接使用密码进行登录,并关闭连接。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值