返回 登录
1

看各路神仙如何大战MySQL Insecure Warning报警有感

阅读4494

问题由来

MySQL在5.6.5上开始在命令行中直接填入用户密码会提示错误,例如:

$./mysql -h10.10.30.18 -uwoqutech -pwoqutech -ss -e 'select @@server_id'
Warning: Using a password on the command line interface can be insecure.
330618

如果你要写一个脚本调用mysql命令来获得server_id的值,这个Warning的信息绝对是你的噩梦。

提示这个安全原因本来无可厚非,但是坑爹的是,没有任何一个参数或者开关能关闭这个Warning。

2012年liu wei同学就提交了这个bug,到了2017年的今天,还是没有解决。期间有n多人开骂,其中Van Stokes骂的最经典:

How about this insane idea….
How about MySQL quits trying to save the world from itself and REMOVE THIS STUPID WARNING MESSAGE. It’s NONE OF YOUR BUSINESS if I want to use a password on the command line or not. So quit worrying about it. Remove the warning. We don’t need to be nannied to death by the likes of you.

翻译过来就是:

你们脑子秀逗了,赶紧把这个傻×报警信息清理掉,再不清理掉就麻烦大了!
骂归骂,也有很多人提供了对应的解决方案,甚至包括修改MySQL的汇编代码来解决。

人间大炮一级准备:脚本自己解决

最简单的,MySQL自己不解决,我们脚本里面可以过滤。拿到这两行信息以后,通过grep -v或者各自程序语言去过滤掉这个错误信息。

但是每个脚本都需要有额外的逻辑来处理这个信息,也是大家非常不爽的原因。

Karl Nicoletti就很不爽:

Does the MySQL development team have ANY idea how many man-hours of work they have inflicted on DBAs and developers relying on command line input responses that DO NOT return the word “Warning” or “Error” in the case of successful execution??? I alone will be spending at least 100 hours to upgrade and test all our in-house maintenance, and installation scripts to work around this idiotic warning. Maybe, just MAYBE you could use the word “NOTE:” instead of “Warning:”??? Better yet, provide an option, –no-cmd-line-warning that would shut the damn thing off?

人间大炮二级准备:MYSQL_PWDmysql_config_editor解决

还好,有一些其他的解决方案也可以解决这个问题。

mysql_config_editor

利用mysql_config_editor来保存用户名密码,避免输出Warning信息:

mysql_config_editor set --login-path=woqutech --host=10.10.30.18 --user=woqutech --password
mysql --login-path=woqutech  -e "select @@server_id"

这种方式必须手工输入密码,在脚本里面用也非常坑爹。

MYSQL_PWD

通过设置MYSQL_PWD变量来避免输出Warning信息:

$MYSQL_PWD='woqutech' ./mysql  -h10.10.30.18 -uwoqutech  -ss -e 'select @@server_id'
330618

这种方式相对接受度比较高。

人间大炮三级准备:源码解决

既然MySQL是开源的,那我们当然希望通过源码解决拉,源码其实很简单。

void print_cmdline_password_warning()
{
  static my_bool password_warning_announced= FALSE;
  if (!password_warning_announced)
  {
    fprintf(stderr, "Warning: Using a password on the command line "
            "interface can be insecure.\n");
    (void) fflush(stderr);
    password_warning_announced= TRUE;
  }
}

阿里的印风就提交了一个patch供大家参考。

人间大炮发射:汇编解决

源码解决方案的问题在于需要维护自己的版本,每次MySQL新的版本发布都需要重新打patch,并重新编译。

还有什么其他的办法列,Andrew McGill和Perry Harrington提供了在汇编层面解决这个问题的办法!

删除汇编中Warning信息

Andrew McGill使用的是perl,解决方案如下:

perl -p -i -e 's/(Warning: Using a password on the command line interface can be insecure..)/"\0"x(length($1))/es' /usr/local/mysql/bin/mysql

简单解释一下:

  • MySQL程序编译出来就是一个汇编的代码
  • \0代表是空字符,"\0"x(length($1)表示多个空字符,不会打印任何字符
  • Andrew McGill利用perl将Warning: Using a password on the command line interface can be insecure.的信息都替换为空,也就不会打印Warning信息了。

print_cmdline_password_warning函数逻辑修改

Perry Harrington提供了一种匪夷所思的汇编代码修改方式:

原版在测试环境下不可用:

printf '\x75' | dd of=./mysql bs=1 seek=$((16#$(objdump --disassemble -F mysql |grep password_warning|grep je|awk '{print $1}'|cut -d: -f1|sed -e 's/^.//g'))) count=1 conv=notrunc

可用版本:

printf '\xeb' | dd of=./mysql bs=1 seek=$((16#$(objdump --disassemble -F mysql |grep password_warning|grep jne|awk '{print $1}'|cut -d: -f1|sed -e 's/^.//g'))) count=1 conv=notrunc

这个需要解释一下:

print_cmdline_password_warning函数中会判断password_warning_announced是否为FALSE,由于password_warning_announced是静态变量,再次进入这个函数将不会打印错误信息,函数代码如下:

  /**
 * This function should be called to print a warning message
 * if password string is specified on the command line.
 */
void print_cmdline_password_warning()
{
  static my_bool password_warning_announced= FALSE;
  if (!password_warning_announced)
  {
    fprintf(stderr, "Warning: Using a password on the command line "
            "interface can be insecure.\n");
    (void) fflush(stderr);
    password_warning_announced= TRUE;
  }
}

汇编代码中只需要把判断password_warning_announced的逻辑修改成直接跳转,就可以不打印出来了。

怎么修改列,通过objdump可以查看到print_cmdline_password_warning函数的逻辑如下:

000000000044d980 <print_cmdline_password_warning> (File Offset: 0x4d980):
  44d980:       55                      push   %rbp
  44d981:       48 89 e5                mov    %rsp,%rbp
  44d984:       53                      push   %rbx
  44d985:       48 83 ec 08             sub    $0x8,%rsp
  44d989:       80 3d e0 aa 59 00 00    cmpb   $0x0,0x59aae0(%rip)        # 9e8470 <_ZZ30print_cmdline_password_warningE26password_warning_announced> (File Of
fset: 0x5e8470)
  44d990:       75 2f                   jne    44d9c1 <print_cmdline_password_warning+0x41> (File Offset: 0x4d9c1)
  44d992:       48 8b 1d 07 00 4c 00    mov    0x4c0007(%rip),%rbx        # 90d9a0 <_DYNAMIC+0xd50> (File Offset: 0x50d9a0)
  44d999:       48 8d 3d 40 c7 08 00    lea    0x8c740(%rip),%rdi        # 4da0e0 <special_opt_prefix_lengths+0x40> (File Offset: 0xda0e0)
  44d9a0:       ba 49 00 00 00          mov    $0x49,%edx
  44d9a5:       be 01 00 00 00          mov    $0x1,%esi
  44d9aa:       48 8b 0b                mov    (%rbx),%rcx
  44d9ad:       e8 26 df fb ff          callq  40b8d8 <fwrite@plt> (File Offset: 0xb8d8)
  44d9b2:       48 8b 3b                mov    (%rbx),%rdi
  44d9b5:       e8 ce e0 fb ff          callq  40ba88 <fflush@plt> (File Offset: 0xba88)
  44d9ba:       c6 05 af aa 59 00 01    movb   $0x1,0x59aaaf(%rip)        # 9e8470 <_ZZ30print_cmdline_password_warningE26password_warning_announced> (File Offset: 0x5e8470)
  44d9c1:       48 83 c4 08             add    $0x8,%rsp
  44d9c5:       5b                      pop    %rbx
  44d9c6:       c9                      leaveq
  44d9c7:       c3                      retq
  44d9c8:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  44d9cf:       00

我们看到44d990行,上一行比较了password_warning_announced的值,jne表示不等于就跳转到函数尾44d9c1(退出堆栈),我们把汇编指令修改成无论如何都跳转jmp不就解决了这个问题吗?

  44d990:       75 2f                   jne    44d9c1 <print_cmdline_password_warning+0x41> (File Offset: 0x4d9c1)

所以修改的策略就是把jne修改成jmp,对应的就是要把75修改为eb。汇编指令操作码可以查考此处

  • 75是jne
  • eb是jmp

所以最终的修改方式就是这么一个诡异的命令:

printf '\xeb' | dd of=./mysql bs=1 seek=$((16#$(objdump --disassemble -F mysql |grep password_warning|grep jne|awk '{print $1}'|cut -d: -f1|sed -e 's/^.//g'))) count=1 conv=notrunc
  • objdump –disassemble -F mysql |grep password_warning|grep jne|awk '{print $1}'|cut -d: -f1|sed -e 's/^.//g'获得要修改的MySQL汇编文件字节偏移位置
  • seek=$((16#$(objdump –disassemble -F mysql |grep -password_warning|grep jne|awk '{print $1}'|cut -d: -f1|sed -e 's/^.//g')))获得16进制“要修改的MySQL汇编文件字节偏移位置”
  • printf '\xeb' | dd of=./mysql bs=1 seek=$((16#$(objdump –disassemble -F mysql |grep password_warning|grep jne|awk '{print $1}'|cut -d: -f1|sed -e 's/^.//g'))) count=1 conv=notrunc将MySQL汇编文件中print_cmdline_password_warning函数中jne修改为jmp,直接跳转,不打印错误信息

总结

MySQL出于安全的考虑,建议大家不要在命令行中写password,但是只是在命令行中提示,并且还不能关闭,确实比较坑,我们要做一个企业级的产品也需要对每一个细节和功能考虑的更加仔细和细致,避免出现类似的问题。

虽然官方也不知道啥时候能解决这个Warning的问题,但是各路大神各出奇招来解决这个问题,也给我们提供了很多思路,很有借鉴意义。

来源:沃趣科技(woqutech)
作者:董红禹

评论