天有不测风云, 今晚(20240919,上海)台风Pulasan就要来了. 难免有忘记mysql密码的时候. 解决办法网上也是一找一大堆的. 这里做个小小的介绍. (不考虑history里面能查询到的情况, 况且那也不属于忘记密码的范畴)
如果是忘记普通账号的密码, 可以在业务端配置文件找(能找到的话, 估计也看不到这了.). 更多的情况是都找不到了. 只能重置密码. 命令参考
alter user username@'%' identified with mysql_native_password by '123456';
flush privileges;
注: 最新版mysql不在支持mysql_native_password插件了.
多数情况, 我们都是忘记root的密码了. 不然都可以使用第一种方法来解决. mysql也预料到了这种情况. 于是整了个参数skip_grant_tables , 该参数为True
时,即不做权限/密码认证, 任何账号都能直接登录, 这很危险, 于是又整了个参数skip_networking 来限制TCP/IP连接, 这样我们修改密码的时候,还可以通过socket来连接, 但是其它账号就不能通过网络连接了. 算是安全了.(如果其它人都能登录OS了的话, 也是可以通过socket连接的, 但这属于OS那边的'锅')
| Command-Line Format | --skip-grant-tables[={OFF|ON}]
|
| ------------------- | -------------------------------- |
| Type | Boolean |
| Default Value | OFF
|
| Command-Line Format | --skip-networking[={OFF|ON}]
|
| ---------------------------------------- | ------------------------------ |
| System Variable | skip_networking
|
| Scope | Global |
| Dynamic | No |
| SET_VAR
Hint Applies | No |
| Type | Boolean |
| Default Value | OFF
|
这两参数都需要重启数据库. 修改方法可参考如下:
# 停库
systemctl stop mysqld
# 编辑配置文件, 增加那两参数
vim my.cnf
skip_grant_tables
skip_networking # 可选
# 启库
systemctl start mysqld
# 登录数据库,并修改密码
mysql -S /tmp/mysql.sock
flush privileges;
alter user root@'localhost' identified with mysql_native_password by '123456';
flush privileges;
# 再把参数修改回去
vim my.cnf
# 再重启
systemctl restart mysqld
# 使用新密码登录数据库确认
mysql -S /tmp/mysql.sock -p123456
第二种方法有丢丢麻烦, 于是官方还有个参数init_file 也能实现修改密码. 该参数表示数据库启动的时候执行的SQL语句, 是不需要验证权限的. 所以我们可以让它直接我们的修改密码的语句就能达到修改密码的要求.
Command-Line Format |
|
---|---|
System Variable |
|
Scope | Global |
Dynamic | No |
| No |
Type | File name |
官方重置root密码的教程也是使用的这个参数.
# 编辑sql文件
vim conf/init.sql
alter user root@'localhost' identified with mysql_native_password by '123456';
# 编辑配置文件
vim conf/my.cnf
init_file='xxx/conf/init.sql'
# 重启数据库
systemctl restart mysqld
# 验证密码
mysql -S /tmp/mysql.sock -p123456
# 把参数文件修改回去(可选), 不改的话, 每次重启密码都被重置为这个密码了.
这个方法相对于上面的的skip_grant_tables要简单很多, 但也要重启.
前面几种都是比较常见的方法, 除了第一种都要重启. 生产环境重启不是一件容易的事. 那么有没有不需要重启的方法呢? 当然有了, 那就是爆破(不是团队竞技). 如果每次都建立tcp连接的话, 效率是非常低的, 而且还可能影响业务的正常使用. 我们知道mysql_native_password的加密方法是做2次hash, 也就是我们可以比较hash之后的值是否一致来判断密码是否正确. mysql.user
表 保存的也是加密之后的密码, 所以我们可以查看该表来获取加密后的密码, 但现在不是登录不了数据库么, 怎么查看呢?
诶, 我们昨天刚解析了mysql.ibd文件, 里面就有mysql.user
表的数据啊. 使用方法如下: (我这里加了个if,只解析mysql表的数据)
python3 get_mysql_ibd.py /data/mysql_dev/data/mysql.ibd
简单点的方法,我们可以拿该密码去一些hash网站查询.(但可能有的会收费, 但也有免费的)
如果密码很复杂的话, 可能在线查询不到. 而且在线查询还不安全, 这里就可以自己来爆破了. python版爆破方法参考:
import binascii
import hashlib
NEW_PASSWORD = '1234567'
# 这里是二进制的. 需要的话, 可以转为str
PASSWORD = b'*'+binascii.hexlify(hashlib.sha1(hashlib.sha1(NEW_PASSWORD.encode()).digest()).digest())
有种特殊情况是, 忘记了mysql的密码, 但是之前配置了免登(mysql_config_editor),还可以登录数据库, 那么就可以直接修改密码(同时重新配置免登文件). 还有种更优雅的方法, 就是解析~/.mylogin.cnf
文件内容. 好巧不巧我们之前就写过这种方法, 这里就不再介绍了, 感兴趣的自己看: https://www.modb.pro/db/1765168815701299200 相关的脚本可以直接在墨天轮下载
这种场景虽然少, 但今天恰好就用到了(文章写得断断续续的原因.....). 从库配置了免密, 但主库没有配. 这时候就可以去从库解析,然后在主库上面配置了.
前面那些都还算普通操作, 这里开始我们就要来点骚操作了.
既然我们已经能解析ibd文件了, 那么我们就能修改mysql.ibd文件里面root的密码信息, 改为我们要的密码不就行了么.
首先我们得找到密码的位置(PAGENO和OFFSET), 欸, 我们的ibd2sql就有这个功能(--debug).
mysql.user是host,user作为主键的, localhost是字母通常在%后面, 所以基本上都是最后一条, 很快就能找到的.
# 编辑脚本, 打开debug功能
vim get_mysql_ibd.py
ddcw.DEBUG = True
# 解析解析
python3 get_mysql_ibd.py /data/mysql_dev/data/mysql.ibd
这里我们就得到了 root@localhost的PAGENO=757, 往后面走, 找到密码位置.
于是我们得到了OFFSET=2513
然后我们适用如下脚本修改密码并生成一个新的mysql.ibd文件(最近修改ibd文件的脚本基本上都差不多, 注意crc32校验别忘了哟)
#!/usr/bin/env python3
# write by ddcw @https://github.com/ddcw/ibd2sql
# 修改Mysql密码的脚本
import binascii
import hashlib
import struct,os,sys
PAGENO = 757
OFFSET = 2513
filename = '/data/mysql_dev/data/mysql.ibd'
filename2 = '/tmp/mysql.ibd'
NEW_PASSWORD = '123456'
PAGE_SIZE = 16384
NEW_PASSWORD = b'*'+binascii.hexlify(hashlib.sha1(hashlib.sha1(NEW_PASSWORD.encode()).digest()).digest())
def create_crc32c_table():
poly = 0x82f63b78
table = []
for i in range(256):
crc = i
for _ in range(8):
if crc & 1:
crc = (crc >> 1) ^ poly
else:
crc >>= 1
table.append(crc)
return table
crc32_slice_table = create_crc32c_table()
def calculate_crc32c(data):
crc = 0xFFFFFFFF
for byte in bytearray(data): # for PY2
crc = crc32_slice_table[(crc ^ byte) & 0xFF] ^ (crc >> 8)
return crc ^ 0xFFFFFFFF
def replace_crc32(data):
c1 = calculate_crc32c(data[4:26])
c2 = calculate_crc32c(data[38:PAGE_SIZE-8])
cb = struct.pack('>L',(c1^c2)&(2**32-1))
data = cb + data[4:PAGE_SIZE-8] + cb + data[PAGE_SIZE-4:]
return data
f2 = open(filename2,'wb')
with open(filename,'rb') as f:
current_pageno = -1
while True:
data = f.read(PAGE_SIZE)
if data == b'':
break
current_pageno += 1
if current_pageno == PAGENO:
data = data[:OFFSET] + NEW_PASSWORD + data[OFFSET+len(NEW_PASSWORD):]
data = replace_crc32(data)
f2.write(data)
f2.close()
直接执行该脚本, 然后替换掉系统的mysql.ibd文件(建议先备份)
python3 modify_password.py
cp -ra /data/mysql_dev/data/mysql.ibd /data/mysql_dev/data/mysql.ibd.bak20240919
chown mysql:mysql /tmp/mysql.ibd
mv /tmp/mysql.ibd /data/mysql_dev/data/mysql.ibd
欸, 这就好了么. 我们先解析这个文件验证下呢(把debug关了, 不然信息太多,看起来乱七八糟的,到处都是 by Neeko)
mysqld.ibd文件内容确实是我们修改之后的密码了. 那么我们登录数据库试下呢
居然不行
flush privileges和flush tables也试了均不行.
那我们重启试下吧.
重启之后确实可以. 但还是要重启, 而且步骤感觉也不少. 还不如init_file那个
以前在某平台学习了一个适用c++修改cs1.6金币的方法, 就是直接修改内存(数字比较多,多修改几次,看看变化的是哪些, 修改那部分的值即可). 那理论上也可以修改mysqld进程的内存数据,把密码修改为我们实际的那个. 看了下内存dump文件, 可能存在多个密码相同的账号, 修改的难度较大. (能力有限, 就不卖弄了)
使用gdb接管mysqld进程, 待到连接过来的时候, 直接放行即可. 这操作更骚, 且难度不小. 未验证过此方法的可行性.
通常建议前3种方法, 后面几种不太推荐
方法 | 难度 | 优点 | 缺点/限制 |
---|---|---|---|
ALTER | 0星 | 简单,不需要重启 | 只能修改普通账号的密码, 需要高权限的账号登录, 业务得同步相关信息 |
skip_grant_tables | 1星 | 简单? | 过程比较啰嗦, 需要多次重启数据库 |
init_file | 2星 | 简单,官方提供的方法 | 需要重启 |
爆破 | 3星 | 不需要重启 | 成本高, 不优雅 |
mysql_config_editor.sh | 3星 | 简单 | 适用场景太TM有限了 |
修改mysql.ibd文件 | 4星 | 骚 | 还是TM要重启 |
修改内存 | 5星 | 不需要重启 | 危险 |
gdb跳过认证 | 离谱 | 不需要重启 | 离了个大谱 |
参考:
https://dev.mysql.com/doc/refman/8.0/en/server-options.html#option_mysqld_skip-grant-tables
https://dev.mysql.com/doc/refman/8.0/en/resetting-permissions.html
题外话
甲方要求工时饱和度, 所以后面加班会多点(DBA加班能干啥? dba事情越少越好啊, dba事情多的话就有大问题咯. 但作为外包,只能:好的), 写文章的时间好像还多了???
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。