原文来自静雅斋,转载请注明出处。

0x00 状态

笔者博客是用 Ghost + PostgreSQL 搭建的,最近官方出了 RoadMap 做了 LTS 支持,因此做了 Ghost 的升级,同时也顺手把 node 升级到了 6.x 版本,本以为小版本升级轻松无压力,结果重启 Ghost 的时候直接报错

ERROR: password authentication failed for user "ghost" 
 
 error: password authentication failed for user "ghost"
    at Connection.parseE (/home/ghost/node_modules/.4.1.1@pg/lib/connection.js:534:11)
    at Connection.parseMessage (/home/ghost/node_modules/.4.1.1@pg/lib/connection.js:361:17)
    at TLSSocket.<anonymous> (/home/ghost/node_modules/.4.1.1@pg/lib/connection.js:105:22)
    at emitOne (events.js:96:13)
    at TLSSocket.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:176:18)
    at TLSSocket.Readable.push (_stream_readable.js:134:10)
    at TLSWrap.onread (net.js:548:20)

0x01 回滚版本

碰到升级出错,第一反应就是回滚版本,已经是习惯了,但是忘记之前的版本号了,只能尝试着回滚版本,但是发现所有版本都有这个问题,开始陷入僵局(笔者忘了 node 从 4.x 被升级到了 6.x)。所以只能暂时先将数据库从 PostgreSQL 迁移到了 MySQL

0x02 分析问题

从上面的错误信息来看是由于密码认证出错的问题导致的,但是实际上密码是正确的,PostgreSQL 支持 md5 和 password 两种方式认证,password 就是明文发送,笔者使用的是 ssl 加密并且使用 md5 认证,于是乎上服务器查 PostgreSQL 日志,但是并没有发现认证出错的情况,照理来说这里已经可以排除是数据库服务器的问题了,但是笔者又多做了一步,导致后面走了弯路。笔者将密码认证方式改为 password 明文发送,就 OK 了。结果就开始怀疑 pg 库和数据库的问题了。但是摸索了半天并没有发现问题所在,然后 google 了也没有找到类似的情况,一般来说,遇到问题就 google,80% 的问题都能解决,google 不到就去 github issue 上找,如果还找不到就只有两种情况,低级 bug 或者前无古人的 bug。

0x03 线索

过了一段时间,偶然去了 npm 上面找了一下 pg 依赖包,发现这个版本已经到了 6.1.2 版本了,而 Ghost 官方依赖的版本是 4.1.1,就开始怀疑是不是 pg 版本过低的原因了。与此同时,在 Ghost 官方博客上面也找到了一条声明,表示由于大家都用 MySQL,PostgreSQL 没人能提交意见,因此准备将 PostgreSQL 降级为“二等公民”。所以就前往 pg 库的 issue 上找了一下,还真找到了两条用户关于升级 node 后的问题。升级到最新版本的 pg 依赖就解决了问题。

0x04 源码

由于是升级 node6.x 带来的问题,应该是 API 上做了改动,最终发现是 Buffer 上出现的问题,源代码如下

//password request handling
con.on('authenticationMD5Password', checkPgPass(function(msg) {
  var inner = Client.md5(self.password + self.user);
  var outer = Client.md5(inner + msg.salt.toString('binary'));
  var md5password = "md5" + outer;
  con.password(md5password);
}));

改成如下代码就成功了

//password request handling
con.on('authenticationMD5Password', checkPgPass(function(msg) {
  var inner = Client.md5(self.password + self.user);
  var outer = Client.md5(Buffer.concat([new Buffer(inner), msg.salt]));
  var md5password = "md5" + outer;
  con.password(md5password);
}));

实际上也不算是 Buffer 的问题,而是 crypto 模块现在默认认为字符串为 utf8,而不是 binary,如果没有指定 encoding,就会出现这个问题。

0x05 解决方案

既然上游代码都已经修复了这个 bug,直接升级版本就行了,然后提一个 PR 到官方就 ok。


山河永寂
2.4k 声望159 粉丝