|
问题的提出
网上一位网友提出了如下的问题:
[code:1]ssh [email protected] mysqldump abcd emp > emptoeee -u root -p [email protected] password:
Enter password:[/code:1]
第一个系统密码是看不见的,而第二个mysql的密码竟明文显示了...
第一个密码显然是ssh密码验证所需要的密码,第二个密码应该是远程mysqldump所需要的密码,但是为什么第二个密码就显示明文的了呢?
问题的分析
在Unix编程中,获取密码一般使用getpass(3)函数。他的定义是这样的:
[code:1]
#include <unistd.h>
char *getpass( const char * prompt );[/code:1]
其中prompt就是输入密码前面的提示语如“PassWord:”之类类的短语,在getpass的手册册页中说:
[code:1]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.[/code:1]
也就是说,通常情况下getpass的都是使用stdin来获取密码的,而且《Advanced Unix Programming》一书中我们知道,一般stdin,stdout和stderr是有TTY信息的。在这种情况下,glibc的getpass使用如下的方法:
[code:1]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);
}[/code:1]
在stdin具有终端属性的时候,这段代码会成功的屏蔽掉终端的回显功能。我们的输入就不会被显示出来。
但是,当我们使用netcat等工具,使用管道从网络另一端转发输入输出信息时,远端程序一般会将程序的stdin和stdout,stderr重定向到网络。此时,stdin和stdout等的文件描述符已经被替换:
[code:1]/* 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);[/code:1]
上面是netcat的代码,此时的stdin等的描述符已经是一个套接字。getpass的终端操作无法进行,输入的密码会直接的显示在远程的终端上。对于一次性执行的远程ssh命令,同样仅仅是stdin等被赋值为网络套接字。而且,从ssh的输出来看:
[code:1][gnap@osiris ~]$ ssh ftp bash -i
[gnap@ftp ~]$ su
standard in must be a tty
[gnap@ftp ~]$ exit exit
[gnap@osiris ~]$[/code:1]
远程的bash没有提示"no job control",估计stderr没有被重定向。所以,建议不要使用这种方式执行需要操作终端的程序。
解决方法:
出于安全的考虑,建议尽量避免使用glibc的getpass来获取密码。建议自己实现密码的获取,并且在无法获取tty是提示并退出程序(su)或者编写callback程序向X索取输入密码(ssh)。另外,作为安全的建议,任何调用getpass的程序都建议放到拥有终端的环境中去执行,以避免密码明文显示的安全隐患。 |
|