|
|
问题的提出
网上一位网友提出了如下的问题:
- ssh root@a.b.c.cn mysqldump abcd emp > emptoeee -u root -p
- root@a.b.c.cn password:
- Enter password:
复制代码
第一个系统密码是看不见的,而第二个mysql的密码竟明文显示了...
第一个密码显然是ssh密码验证所需要的密码,第二个密码应该是远程mysqldump所需要的密码,但是为什么第二个密码就显示明文的了呢?
问题的分析
在Unix编程中,获取密码一般使用getpass(3)函数。他的定义是这样的:
- #include <unistd.h>
- char *getpass( const char * prompt );
复制代码
其中prompt就是输入密码前面的提示语如“PassWord:”之类类的短语,在getpass的手册册页中说:
- For glibc2, if /dev/tty cannot be opened, the prompt is written to
- stderr and the password is read from stdin. There is no limit on the
- length of the password. Line editing is not disabled.
复制代码
也就是说,通常情况下getpass的都是使用stdin来获取密码的,而且《Advanced Unix Programming》一书中我们知道,一般stdin,stdout和stderr是有TTY信息的。在这种情况下,glibc的getpass使用如下的方法:
- if (__tcgetattr (fileno (in), &t) == 0)
- {
- /* Save the old one. */
- s = t;
- /* Tricky, tricky. */
- t.c_lflag &= ~(ECHO|ISIG);
- tty_changed = (tcsetattr (fileno (in), TCSAFLUSH|TCSASOFT, &t) == 0);
- }
复制代码
在stdin具有终端属性的时候,这段代码会成功的屏蔽掉终端的回显功能。我们的输入就不会被显示出来。
但是,当我们使用netcat等工具,使用管道从网络另一端转发输入输出信息时,远端程序一般会将程序的stdin和stdout,stderr重定向到网络。此时,stdin和stdout等的文件描述符已经被替换:
- /* duplicate the socket for the child program */
- dup2(ncsock->fd, STDIN_FILENO);
- close(ncsock->fd);
- dup2(STDIN_FILENO, STDOUT_FILENO);
- dup2(STDIN_FILENO, STDERR_FILENO);
复制代码
上面是netcat的代码,此时的stdin等的描述符已经是一个套接字。getpass的终端操作无法进行,输入的密码会直接的显示在远程的终端上。对于一次性执行的远程ssh命令,同样仅仅是stdin等被赋值为网络套接字。而且,从ssh的输出来看:
- [gnap@osiris ~]$ ssh ftp bash -i
- [gnap@ftp ~]$ su
- standard in must be a tty
- [gnap@ftp ~]$ exit
- exit
- [gnap@osiris ~]$
复制代码
远程的bash没有提示"no job control",估计stderr没有被重定向。所以,建议不要使用这种方式执行需要操作终端的程序。
解决方法:
出于安全的考虑,建议尽量避免使用glibc的getpass来获取密码。建议自己实现密码的获取,并且在无法获取tty是提示并退出程序(su)或者编写callback程序向X索取输入密码(ssh)。另外,作为安全的建议,任何调用getpass的程序都建议放到拥有终端的环境中去执行,以避免密码明文显示的安全隐患。 |
|