备注:文中 Steem 指的是区块链,Steemit 指的是 <steemit.com> 网站,请注意区分。
前因
这一切的一切还要从最近帮朋友注册 Steemit 账号说起。之前我使用 Steem Python 来注册新用户,密码是用 https://v2.steemconnect.com/accounts/create 生成。前几天帮朋友注册的时候,发现 steem-python 注册用户有异常报错,于是就换成了 Vessel 注册新用户了,并且总结了一篇 教程。
由于使用 Vessel 注册用户的时候,我看到有一个 Generate new private_keys 的按钮,就在想这个是不是也能生成密码呢,就试了下,发现并不能,只能生成脑钱包密码和私钥。
于是想能不能随便拿个私钥来做密码,毕竟私钥也是个随机字符串,理论上讲当做密码也是没问题的,同时我也好奇 Steem 能不能不用任意字符串做密码,而不是那个 P 开头的字符串。
第二天为了试验 Steem Python 是否好用了的时候,就顺便拿了一个 Vessel 生成的 Owner Private Key 作为密码。注册成功了,但是在 Steemit.com 登陆的时候,却提示我密码错误。
这是怎么回事?!瞬间就慌了,以为3个币的注册费要白花。赶紧用 Steem Python 输出这个账户的 Posting Private Key 尝试登陆,成功了,又导出了 Active Private Key 试了试,也可以。
也就是说,我现在拥有除了改密码以外的所有功能。这让我很不爽。又试了几遍不行后,感觉自己也是认栽了,就把 Vessel 生成私钥的窗口给关了。注册用做密码的那个私钥,我是一个备份都没有留,太大意了!观察了一会,发现原来 Steemit 网站的登陆密码其实是一个私钥开头加上了一个 P,而这个时候刚才的 Vessel 窗口都让我关了,也没法试一试,是不是就是我用的那个私钥开头加了一个 P。真是太恨自己的手快了。。。
又过了一天,感觉这个事不能就这样结束了,于是又重新走了一遍这个流程,又用私钥注册了一个账户,分别用这个私钥和这个私钥加 P 登陆,结果还是登陆失败。
过程
我决定要从源码入手了,看看到底发生了什么?!
先看了下 Steem Python 的代码,在 https://github.com/steemit/steem-python/blob/master/steem/commit.py#L511 和 https://github.com/Netherdrake/steem-python/blob/master/steembase/account.py#L24 这两个位置找到了注册的时候私钥是如何生成的。
这里我把关键代码摘出来了
posting_key = PasswordKey(account_name, password, role="posting")
active_key = PasswordKey(account_name, password, role="active")
owner_key = PasswordKey(account_name, password, role="owner")
memo_key = PasswordKey(account_name, password, role="memo")
class PasswordKey(object):
def __init__(self, account, password, role="active"):
self.account = account
self.role = role
self.password = password
def get_private(self):
a = bytes(self.account +
self.role +
self.password, 'utf8')
s = hashlib.sha256(a).digest()
return PrivateKey(hexlify(s).decode('ascii'))
....省略后面的代码
原来我们设置的登陆密码只是一个Salt,所有的私钥是通过把 username,role,password 连接后哈希出来的。
如果按照这个逻辑,我用私钥字符串做密码并不应该出现密码错误的情况。
为了验证这个推断,我用 Steem Python 库中的方法进行了验证,如下:
通过库中的方法,我成功导出了 tinyfish 这个账号的 Owner Private Key,也就是说,Steemit 的密码其实可以设置为任意字符串的,这个密码只是一个 Salt ,只是为了方便我们记忆,因为在 Steem 链上只有公钥和私钥,而一个账户下最少拥有了3对公私钥,通过密码这种形式来简化记忆的难度。
那么既然 Steem Python 的代码没有问题,那么鬼一定就出在 Steemit 网站上了,难道因为我用的是私钥,触发了网站的某个逻辑,进而导致我进入了错误的登陆流程?
为了验证我的这个推断,继续去翻 condenser 的代码。最终在这里找到了答案,https://github.com/steemit/condenser/blob/master/src/app/redux/UserSaga.js#L198。
我摘出来了重要部分
try {
const private_key = PrivateKey.fromWif(password);
login_wif_owner_pubkey = private_key.toPublicKey().toString();
private_keys = fromJS({
posting_private: isRole('posting', () => private_key),
active_private: isRole('active', () => private_key),
memo_private: private_key,
});
} catch (e) {
// Password (non wif)
login_owner_pubkey = PrivateKey.fromSeed(username + 'owner' + password)
.toPublicKey()
.toString();
private_keys = fromJS({
posting_private: isRole('posting', () =>
PrivateKey.fromSeed(username + 'posting' + password)
),
active_private: isRole('active', () =>
PrivateKey.fromSeed(username + 'active' + password)
),
memo_private: PrivateKey.fromSeed(username + 'memo' + password),
});
}
看代码也就是只有通过 PrivateKey.fromWif 这个方法拿不到 private_key 的时候,应该会有异常抛出,然后才进入到 catch 的流程中,也就是我们用密码登陆的流程。
所以我用私钥做密码注册的小号登陆失败的原因,就是因为网站直接把我的密码当做私钥用了,那肯定是要报错了!!!
在知道原因后,我可以用 posting private key 登陆网站,然后修改密码的时候,用从 Steem Python 中读取出来的 Owner private key 完成密码修改。只是可惜了我第一个小号的密码没有保存,再次后悔若干分钟。可能唯一的解决方法就是暴力破解了。。。。
总结
- 请不要使用生成的私钥字符串作为你的登陆密码!
- 无论用什么生成器生成密码,请一定要先存好!
- 修改密码后,记得保存新生成的各个私钥!
感谢你的阅读,我是一个见证人,欢迎通过 SteemConnect 来给我投票,或者打开 https://steemit.com/~witnesses 页面,输入 ety001 进行投票。
Thank you for reading. I'm a witness. I would really appreciate your witness vote! You can vote by SteemConnect. Or open https://steemit.com/~witnesses page, input ety001 to vote.