PostgreSQL psql 安全设置数据库用户密码的方法之一
背景
密码有多重要就不需要多说了,但是你知道密码有多少可能泄露的渠道吗?
大多数人可能觉得在设置好密码后,保管好不被泄露就可以了。
但是你有没有想过,在设置密码的过程中就泄露了呢?
比如数据库中设置用户密码,有多少种可能泄露的渠道?
比如,我们在修改数据库用户密码时,可能经历这么长的流程才能最终将新的密码写入数据库的元数据pg_authid中.
这么多环节,都有可能被不法分子有机可乘。是不是不能简单的认为设置好密码之后就万事大吉了呢?很有可能在你设置的过程中就被截获了。
即使MD5被截获或者泄露,也是危险的,详见
当然,现在PostgreSQL已经意识到这个问题,在进行协议层认证方面的改造,如下:
《元旦技术大礼包 - 2017金秋将要发布的PostgreSQL 10.0已装备了哪些核武器?》
所以为了你的安全,我建议你仔细阅读以下数据库的安全加固方法
《DBA专供 冈本003系列 - 数据库安全第一,过个好年》
本文主要介绍以下psql这个客户端做的一个改进,在设置密码时,隐藏掉明文。(但是你要知道,即使这样,也是不够安全的,安全都是相对的)
psql \password
psql 新增的一个指令如下
\password [USERNAME] securely change the password for a user
对应的源码如下,会将用户输入的文本通过PQencryptPassword函数转换为md5,然后调用PSQLexec执行该ALTER USER XX PASSWORD ‘MD5XX’;
src/bin/psql/command.c
/* \password -- set user password */
else if (strcmp(cmd, "password") == 0)
{
char *pw1;
char *pw2;
pw1 = simple_prompt("Enter new password: ", 100, false);
pw2 = simple_prompt("Enter it again: ", 100, false);
if (strcmp(pw1, pw2) != 0)
{
psql_error("Passwords didn't match.\n");
success = false;
}
else
{
char *opt0 = psql_scan_slash_option(scan_state, OT_SQLID, NULL, true);
char *user;
char *encrypted_password;
if (opt0)
user = opt0;
else
user = PQuser(pset.db);
encrypted_password = PQencryptPassword(pw1, user);
if (!encrypted_password)
{
psql_error("Password encryption failed.\n");
success = false;
}
else
{
PQExpBufferData buf;
PGresult *res;
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD ",
fmtId(user));
appendStringLiteralConn(&buf, encrypted_password, pset.db);
res = PSQLexec(buf.data);
termPQExpBuffer(&buf);
if (!res)
success = false;
else
PQclear(res);
PQfreemem(encrypted_password);
}
if (opt0)
free(opt0);
}
free(pw2);
}
src/bin/psql/common.c
/*
* PSQLexec
*
* This is the way to send "backdoor" queries (those not directly entered
* by the user). It is subject to -E but not -e.
*
* Caller is responsible for handling the ensuing processing if a COPY
* command is sent.
*
* Note: we don't bother to check PQclientEncoding; it is assumed that no
* caller uses this path to issue "SET CLIENT_ENCODING".
*/
PGresult *
PSQLexec(const char *query)
{
PGresult *res;
if (!pset.db)
{
psql_error("You are currently not connected to a database.\n");
return NULL;
}
if (pset.echo_hidden != PSQL_ECHO_HIDDEN_OFF)
{
printf(_("********* QUERY **********\n"
"%s\n"
"**************************\n\n"), query);
fflush(stdout);
if (pset.logfile)
{
fprintf(pset.logfile,
_("********* QUERY **********\n"
"%s\n"
"**************************\n\n"), query);
fflush(pset.logfile);
}
if (pset.echo_hidden == PSQL_ECHO_HIDDEN_NOEXEC)
return NULL;
}
SetCancelConn();
res = PQexec(pset.db, query);
ResetCancelConn();
if (!AcceptResult(res))
{
ClearOrSaveResult(res);
res = NULL;
}
return res;
}
测试
为什么说它不是绝对安全呢?因为MD5本身就不安全,另外同样会有诸多渠道可能泄露这个MD5。
不过任何数据库都一样,没有绝对的安全,都是相对的安全,所以非常建议大伙参考一下文章末尾的几篇文章来加固你的数据库。
postgres=# set log_statement='all';
postgres=# set client_min_messages ='log';
postgres=# \password digoal
Enter new password:
Enter it again:
LOG: statement: ALTER USER digoal PASSWORD 'md5462f71c79368ccf422f8a773ef40074d'
postgres=# select * from pg_authid where rolname='digoal';
LOG: statement: select * from pg_authid where rolname='digoal';
rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil
---------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------------------------------+---------------
digoal | f | t | f | f | t | f | f | -1 | md5462f71c79368ccf422f8a773ef40074d |
(1 row)
源码层思考
从源码层面如何杜绝单一的加密方法呢?
比如引入可以识别instance的UUID,例如systemid,多重加密,这样的话可能破解难度会进一步加大,或者避免一些重复的密码问题。
参考
《元旦技术大礼包 - 2017金秋将要发布的PostgreSQL 10.0已装备了哪些核武器?》
《DBA专供 冈本003系列 - 数据库安全第一,过个好年》