|
| 1 | +## PostgreSQL psql 安全设置数据库用户密码的方法之一 |
| 2 | + |
| 3 | +### 作者 |
| 4 | +digoal |
| 5 | + |
| 6 | +### 日期 |
| 7 | +2017-01-12 |
| 8 | + |
| 9 | +### 标签 |
| 10 | +PostgreSQL , psql , 安全 , 密码 , 日志 , password |
| 11 | + |
| 12 | +---- |
| 13 | + |
| 14 | +## 背景 |
| 15 | +密码有多重要就不需要多说了,但是你知道密码有多少可能泄露的渠道吗? |
| 16 | + |
| 17 | +大多数人可能觉得在设置好密码后,保管好不被泄露就可以了。 |
| 18 | + |
| 19 | +但是你有没有想过,在设置密码的过程中就泄露了呢? |
| 20 | + |
| 21 | +比如数据库中设置用户密码,有多少种可能泄露的渠道? |
| 22 | + |
| 23 | +比如,我们在修改数据库用户密码时,可能经历这么长的流程才能最终将新的密码写入数据库的元数据pg_authid中. |
| 24 | + |
| 25 | + |
| 26 | + |
| 27 | +这么多环节,都有可能被不法分子有机可乘。是不是不能简单的认为设置好密码之后就万事大吉了呢?很有可能在你设置的过程中就被截获了。 |
| 28 | + |
| 29 | +即使MD5被截获或者泄露,也是危险的,详见 |
| 30 | + |
| 31 | +[《PostgreSQL 对比 MySQL - 秘钥认证》](../201610/20161009_01.md) |
| 32 | + |
| 33 | +当然,现在PostgreSQL已经意识到这个问题,在进行协议层认证方面的改造,如下: |
| 34 | + |
| 35 | +[《元旦技术大礼包 - 2017金秋将要发布的PostgreSQL 10.0已装备了哪些核武器?》](../201701/20170101_01.md) |
| 36 | + |
| 37 | +所以为了你的安全,我建议你仔细阅读以下数据库的安全加固方法 |
| 38 | + |
| 39 | +[《PostgreSQL 密码安全指南》](../201410/20141009_01.md) |
| 40 | + |
| 41 | +[《PostgreSQL 数据库安全指南》](../201506/20150601_01.md) |
| 42 | + |
| 43 | +[《DBA专供 冈本003系列 - 数据库安全第一,过个好年》](../201612/20161224_01.md) |
| 44 | + |
| 45 | +本文主要介绍以下psql这个客户端做的一个改进,在设置密码时,隐藏掉明文。(但是你要知道,即使这样,也是不够安全的,安全都是相对的) |
| 46 | + |
| 47 | +## psql \password |
| 48 | +psql 新增的一个指令如下 |
| 49 | + |
| 50 | +``` |
| 51 | + \password [USERNAME] securely change the password for a user |
| 52 | +``` |
| 53 | + |
| 54 | +对应的源码如下,会将用户输入的文本通过PQencryptPassword函数转换为md5,然后调用PSQLexec执行该ALTER USER XX PASSWORD 'MD5XX'; |
| 55 | + |
| 56 | +src/bin/psql/command.c |
| 57 | + |
| 58 | +``` |
| 59 | + /* \password -- set user password */ |
| 60 | + else if (strcmp(cmd, "password") == 0) |
| 61 | + { |
| 62 | + char *pw1; |
| 63 | + char *pw2; |
| 64 | + |
| 65 | + pw1 = simple_prompt("Enter new password: ", 100, false); |
| 66 | + pw2 = simple_prompt("Enter it again: ", 100, false); |
| 67 | + |
| 68 | + if (strcmp(pw1, pw2) != 0) |
| 69 | + { |
| 70 | + psql_error("Passwords didn't match.\n"); |
| 71 | + success = false; |
| 72 | + } |
| 73 | + else |
| 74 | + { |
| 75 | + char *opt0 = psql_scan_slash_option(scan_state, OT_SQLID, NULL, true); |
| 76 | + char *user; |
| 77 | + char *encrypted_password; |
| 78 | + |
| 79 | + if (opt0) |
| 80 | + user = opt0; |
| 81 | + else |
| 82 | + user = PQuser(pset.db); |
| 83 | + |
| 84 | + encrypted_password = PQencryptPassword(pw1, user); |
| 85 | + |
| 86 | + if (!encrypted_password) |
| 87 | + { |
| 88 | + psql_error("Password encryption failed.\n"); |
| 89 | + success = false; |
| 90 | + } |
| 91 | + else |
| 92 | + { |
| 93 | + PQExpBufferData buf; |
| 94 | + PGresult *res; |
| 95 | + |
| 96 | + initPQExpBuffer(&buf); |
| 97 | + printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD ", |
| 98 | + fmtId(user)); |
| 99 | + appendStringLiteralConn(&buf, encrypted_password, pset.db); |
| 100 | + res = PSQLexec(buf.data); |
| 101 | + termPQExpBuffer(&buf); |
| 102 | + if (!res) |
| 103 | + success = false; |
| 104 | + else |
| 105 | + PQclear(res); |
| 106 | + PQfreemem(encrypted_password); |
| 107 | + } |
| 108 | + |
| 109 | + if (opt0) |
| 110 | + free(opt0); |
| 111 | + } |
| 112 | + free(pw2); |
| 113 | + } |
| 114 | +``` |
| 115 | + |
| 116 | +src/bin/psql/common.c |
| 117 | + |
| 118 | +``` |
| 119 | +/* |
| 120 | + * PSQLexec |
| 121 | + * |
| 122 | + * This is the way to send "backdoor" queries (those not directly entered |
| 123 | + * by the user). It is subject to -E but not -e. |
| 124 | + * |
| 125 | + * Caller is responsible for handling the ensuing processing if a COPY |
| 126 | + * command is sent. |
| 127 | + * |
| 128 | + * Note: we don't bother to check PQclientEncoding; it is assumed that no |
| 129 | + * caller uses this path to issue "SET CLIENT_ENCODING". |
| 130 | + */ |
| 131 | +PGresult * |
| 132 | +PSQLexec(const char *query) |
| 133 | +{ |
| 134 | + PGresult *res; |
| 135 | + |
| 136 | + if (!pset.db) |
| 137 | + { |
| 138 | + psql_error("You are currently not connected to a database.\n"); |
| 139 | + return NULL; |
| 140 | + } |
| 141 | + |
| 142 | + if (pset.echo_hidden != PSQL_ECHO_HIDDEN_OFF) |
| 143 | + { |
| 144 | + printf(_("********* QUERY **********\n" |
| 145 | + "%s\n" |
| 146 | + "**************************\n\n"), query); |
| 147 | + fflush(stdout); |
| 148 | + if (pset.logfile) |
| 149 | + { |
| 150 | + fprintf(pset.logfile, |
| 151 | + _("********* QUERY **********\n" |
| 152 | + "%s\n" |
| 153 | + "**************************\n\n"), query); |
| 154 | + fflush(pset.logfile); |
| 155 | + } |
| 156 | + |
| 157 | + if (pset.echo_hidden == PSQL_ECHO_HIDDEN_NOEXEC) |
| 158 | + return NULL; |
| 159 | + } |
| 160 | + |
| 161 | + SetCancelConn(); |
| 162 | + |
| 163 | + res = PQexec(pset.db, query); |
| 164 | + |
| 165 | + ResetCancelConn(); |
| 166 | + |
| 167 | + if (!AcceptResult(res)) |
| 168 | + { |
| 169 | + ClearOrSaveResult(res); |
| 170 | + res = NULL; |
| 171 | + } |
| 172 | + |
| 173 | + return res; |
| 174 | +} |
| 175 | +``` |
| 176 | + |
| 177 | +## 测试 |
| 178 | +为什么说它不是绝对安全呢?因为MD5本身就不安全,另外同样会有诸多渠道可能泄露这个MD5。 |
| 179 | + |
| 180 | +不过任何数据库都一样,没有绝对的安全,都是相对的安全,所以非常建议大伙参考一下文章末尾的几篇文章来加固你的数据库。 |
| 181 | + |
| 182 | +``` |
| 183 | +postgres=# set log_statement='all'; |
| 184 | +postgres=# set client_min_messages ='log'; |
| 185 | +postgres=# \password digoal |
| 186 | +Enter new password: |
| 187 | +Enter it again: |
| 188 | +LOG: statement: ALTER USER digoal PASSWORD 'md5462f71c79368ccf422f8a773ef40074d' |
| 189 | + |
| 190 | +postgres=# select * from pg_authid where rolname='digoal'; |
| 191 | +LOG: statement: select * from pg_authid where rolname='digoal'; |
| 192 | + rolname | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolbypassrls | rolconnlimit | rolpassword | rolvaliduntil |
| 193 | +---------+----------+------------+---------------+-------------+-------------+----------------+--------------+--------------+-------------------------------------+--------------- |
| 194 | + digoal | f | t | f | f | t | f | f | -1 | md5462f71c79368ccf422f8a773ef40074d | |
| 195 | +(1 row) |
| 196 | +``` |
| 197 | + |
| 198 | +## 源码层思考 |
| 199 | +从源码层面如何杜绝单一的加密方法呢? |
| 200 | + |
| 201 | +比如引入可以识别instance的UUID,例如systemid,多重加密,这样的话可能破解难度会进一步加大,或者避免一些重复的密码问题。 |
| 202 | + |
| 203 | +## 参考 |
| 204 | +[《PostgreSQL 对比 MySQL - 秘钥认证》](../201610/20161009_01.md) |
| 205 | + |
| 206 | +[《元旦技术大礼包 - 2017金秋将要发布的PostgreSQL 10.0已装备了哪些核武器?》](../201701/20170101_01.md) |
| 207 | + |
| 208 | +[《PostgreSQL 密码安全指南》](../201410/20141009_01.md) |
| 209 | + |
| 210 | +[《PostgreSQL 数据库安全指南》](../201506/20150601_01.md) |
| 211 | + |
| 212 | +[《DBA专供 冈本003系列 - 数据库安全第一,过个好年》](../201612/20161224_01.md) |
| 213 | + |
| 214 | + |
| 215 | +[Count](http://info.flagcounter.com/h9V1) |
| 216 | + |
0 commit comments