C命令行密码输入

13

我想允许用户通过命令行界面输入密码,但不想在屏幕上显示密码(或显示“****”)。

如何在C语言中做到这一点? 谢谢。

更新:

我只在Linux上工作。所以我实际上并不关心Win或其他系统。我尝试了Lucas的解决方案,它很好用。然而,我还有另一个问题:

  1. 如果这是一个单进程和单线程的应用程序,更改termios的设置会影响不同的终端吗?

  2. 对于单进程-多线程、多进程-多线程的情况呢?

非常感谢。


7
http://www.linux.com/learn/docs/man/3177-getpass3 - sambowry
你使用的控制台和操作系统是什么?在大多数情况下,用户输入将会被回显到命令行。这可以更改,但会受平台限制。 - Lucas
2
从标准输入流std::cin中读取密码。 - Jonathan Graehl
你所编写的代码将根据不同的平台有所不同,包括Windows、Linux、手机、微波炉或电梯界面等。你想要为哪一个平台编写代码呢? - pmg
并不是完全重复的问题,因为另一个问题明确允许使用C++构造和库。 - Stefan Kendall
请注意,传统上,getpass() 只支持 8 个字符的密码输入。较新版本取消了这一限制(在 Solaris 上,最多支持 256 个字符),而你更有可能遇到现代版本而非古老版本。但是,在 Unix 的某些版本中长度可能会成为问题(与支持 128 字节的 Linux 或 BSD 派生和 MacOS X 相对)。 - Jonathan Leffler
7个回答

18

如果您的系统支持它,getpass 是一种选择:

#include <unistd.h>
/* ... */
char *password = getpass("Password: ");

由于字符被输入时不会显示任何内容。


该函数现在已经过时(https://man7.org/linux/man-pages/man3/getpass.3.html)。手册现在建议使用termios()手动禁用ECHO。 - adrian

10
如果你正在使用UNIX环境,类似这样的命令可以关闭命令行的回显。
#include <stdio.h>
#include <termios.h>
#include <unistd.h>   

#define SIZE 100

void getPassword(char password[])
{
    static struct termios oldt, newt;
    int i = 0;
    int c;

    /*saving the old settings of STDIN_FILENO and copy settings for resetting*/
    tcgetattr( STDIN_FILENO, &oldt);
    newt = oldt;

    /*setting the approriate bit in the termios struct*/
    newt.c_lflag &= ~(ECHO);          

    /*setting the new bits*/
    tcsetattr( STDIN_FILENO, TCSANOW, &newt);

    /*reading the password from the console*/
    while ((c = getchar())!= '\n' && c != EOF && i < SIZE){
        password[i++] = c;
    }
    password[i] = '\0';

    /*resetting our old STDIN_FILENO*/ 
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt);

}


int main(void)
{
    char password[SIZE];
    printf("please enter password\n");
    getPassword(password);

    printf("Do something with the password <<%s>>\n", password);
    return 0;
}

6

函数getpass现在已过时。请使用termios

#include <termios.h>
#include <stdio.h>

void get_password(char *password)
{
    static struct termios old_terminal;
    static struct termios new_terminal;

    //get settings of the actual terminal
    tcgetattr(STDIN_FILENO, &old_terminal);

    // do not echo the characters
    new_terminal = old_terminal;
    new_terminal.c_lflag &= ~(ECHO);

    // set this as the new terminal options
    tcsetattr(STDIN_FILENO, TCSANOW, &new_terminal);

    // get the password
    // the user can add chars and delete if he puts it wrong
    // the input process is done when he hits the enter
    // the \n is stored, we replace it with \0
    if (fgets(password, BUFSIZ, stdin) == NULL)
        password[0] = '\0';
    else
        password[strlen(password)-1] = '\0';

    // go back to the old settings
    tcsetattr(STDIN_FILENO, TCSANOW, &old_terminal);
}

int main(void)
{
    char password[BUFSIZ];

    puts("Insert password:");
    get_password(password);
    puts(password);
}

2

对于C语言/命令行/Linux,请参考以下内容:

man getch
man noecho

请看getch中关于noecho的注释。我自己从未尝试过。

在bash中,如果你使用read -s,它不会在屏幕上回显:

> read -s x
<type something><enter>
> echo $x
<whatever you typed>

因为 RTFM 哲学和跨平台提示而点赞。 - conradkleinespel
不安全。密码随后存储在环境变量中,可以被其他进程读取。另一个基本要求是尽快清除密码,而这种方法无法满足此要求。 - Nino Filiu

2
为了以可移植的方式实现此功能,您需要使用标准化或事实标准库。
请参阅“man 3 termios”和“man 3 ncurses”。
以下是适用于Linux和其他Unix系统的程序...
#include <stdio.h>

int main(void) {
  int f = open("/dev/tty", 0);
  char s[100];

  system("stty -echo > /dev/tty");
  f >= 0 && read(f, s, sizeof s) > 0 &&  printf("password was %s", s);
  system("stty echo > /dev/tty");
  return 0;
}

许多改进是可能的。使用termios可能会更好,并避免了fork和system()可能存在的安全问题。使用标准I/O读取密码可能更好。(如所写,键入的换行符必须被删除。)在Linux和其他一些操作系统中有一个名为getpass()的函数,然而它被标记为“过时。不要使用它。”。处理SIGTSTP可能是个好主意。总的来说,我可能会寻找一个现有的解决方案来处理所有这些小问题...

1
如果您可以访问curses库,您可以使用noecho。如果您正在使用Microsoft的C编译器,则可以使用_getch。但据我所知,这两个都将您绑定到控制台。如果您需要读取stdin,无论它是来自控制台、文件还是管道命令,您需要告诉操作系统如何处理控制台

0
     #include<conio.h>
     #include<iostream>
     using namespace std;
     int main(){
     char *pass = new char[20];
     cout<<"Password :";
     int i=0;   
     while( ( pass[i]=getch() ) != '\n' && pass[i] != '\r' && i<19 )
     {putchar('*'); i++;}
     pass[i]='\0';
     cout<<endl;
     if(strcmp("123456789",pass)==0 ) // do stuff
     return 0;}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接