Ocultar contraseña
Resuelto
aadilove
Mensajes publicados
31
Estado
Miembro
-
fiddy Mensajes publicados 441 Fecha de registro Estado Colaborador Última intervención -
fiddy Mensajes publicados 441 Fecha de registro Estado Colaborador Última intervención -
Hola,
Mi objetivo es ocultar mi contraseña al escribirla.
Para ser más claro, cuando escribo mi contraseña en la consola (UNIX), me gustaría ver '*' en lugar de los caracteres que tecleo.
Gracias de antemano...
Mi objetivo es ocultar mi contraseña al escribirla.
Para ser más claro, cuando escribo mi contraseña en la consola (UNIX), me gustaría ver '*' en lugar de los caracteres que tecleo.
Gracias de antemano...
4 respuestas
-
yosh !
No sé si algo existe, pero puedes hacer:
a cada carácter diferente de ENTER: lo guardo, limpio la pantalla y muestro un '*' más
SI el carácter es enter => valido mis caracteres guardados -
Hola aadilove,
En Linux/Unix, debes suspender la visualización en pantalla de los caracteres tecleados.
Puedes hacerlo como se indica aquí:
https://stackoverflow.com/questions/1196418/getting-a-password-in-c-without-using-getpass-3
Si realmente quieres mostrar asteriscos (¿por qué?), es más complicado, y probablemente deberías usar ncurses.
Así:
http://www.cplusplus.com/articles/E6vU7k9E/#CURSES-e2
ncurses es una biblioteca en C, el código presentado en este enlace mezcla C con un estilo de programación en C++, pero debería darte una idea y es bastante simple de corregir. De lo contrario, deberías encontrar otros ejemplos programados de manera más ortodoxa en C.
Dal-
-
Hola fiddy,
Cuando se permite al usuario ingresar algo, también es bueno permitirle borrar su entrada :-), y por lo tanto manejar la tecla retroceso, para que borre el carácter (la estrella) previamente ingresado, hasta eventualmente la estrella que representa el primero, y manejar los datos de manera correspondiente... y manejar también la tecla enter, por supuesto.
Así que, si queremos manejar correctamente este tipo de interfaz de usuario, hay que ir un poco más allá.
fgets que se utiliza en el primer enlace no permite hacer eso, ya que solo maneja un flujo y espera ENTER (o EOF).
Creo que se podría usar getchar, con el buffering de línea desactivado (jugando con ICANON) además de echo, y enviar \b como se muestra en el segundo enlace para borrar la estrella mostrada.
En resumen, podemos prescindir de ncurses, pero de todos modos no es trivial (con ncurses o sin ella, de hecho), manejar "estrellas", en lugar de simplemente no devolver nada.
Dal -
-
No, si solo se suprime el eco, el terminal funciona sin eco, pero con el buffering de línea, por lo que la tecla de retroceso se gestiona (al igual que la tecla de entrada) sin que tengas que manejarla tú mismo (como se propone en el código de SO, aquí https://stackoverflow.com/questions/1196418/getting-a-password-in-c-without-using-getpass-3).
Solo si quieres "ver estrellas", debes eliminar el buffering de línea, desactivando además el modo canónico, como se propone en mi código adaptado aquí: https://forums.commentcamarche.net/forum/affich-27337373-masquer-mot-de-passe#13.
Dal
-
-
Hola aadilove, fiddy,
Aquí hay un ejemplo que he podido probar en una máquina FreeBSD compilada con gcc, a la que me conecto con un terminal emulado por Putty, adaptado tanto del primer enlace publicado en mi mensaje https://forums.commentcamarche.net/forum/affich-27337373-masquer-mot-de-passe#2 como del segundo, pero sin usar ncurses.
Debería funcionar bajo Linux también. Sin embargo, consulte las observaciones a continuación.
#include <stdio.h> #include <stdlib.h> #include <termios.h> #define ENTER_KEY 10 #define BACKSPACE_KEY 127 int main(void) { struct termios oflags, nflags; char password[11]; char ch = ' '; int n = 0; /* deshabilitando el buffering de línea al deshabilitar * el modo canónico, además de * deshabilitar el eco */ tcgetattr(fileno(stdin), &oflags); nflags = oflags; nflags.c_lflag &= (~ICANON & ~ECHO); nflags.c_lflag |= ECHONL; if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) { perror("tcsetattr"); return EXIT_FAILURE; } printf("contraseña (longitud máx %lu): ", sizeof(password) - 1); do { ch = getchar(); if ( (ch != BACKSPACE_KEY) && (ch != ENTER_KEY) && (n < sizeof(password) - 1) ) { printf("*"); password[n] = ch; password[n+1] = '\0'; n++; } else if ( (ch == BACKSPACE_KEY) && (n > 0) ) { printf("\b \b"); n--; password[n] = '\0'; } } while (ch != ENTER_KEY); printf("Has escrito: %s\n", password); /* restaura el terminal */ if (tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) { perror("tcsetattr"); return EXIT_FAILURE; } return 0; }
La desactivación del buffering de línea permite disponer de la tecla presionada con getchar() sin necesidad de presionar la tecla de entrada.
El código propuesto arriba también prohíbe la entrada más allá de la capacidad máxima de la cadena de contraseña, gestiona el retroceso para borrar los asteriscos y la entrada...
Ten en cuenta que el terminal puede ser configurado para enviar algo diferente a 127 (Ctrl-?) cuando se presiona retroceso. Así, mi Putty me permite configurar el terminal emulado para enviar Ctrl-H en su lugar.
Dal-
Sin embargo, es más simple limitarse a desactivar el eco en el terminal, para no tener que mostrar los asteriscos y gestionar todo lo que eso conlleva, y limitarse a hacer lo que se describe en el primer enlace publicado:
https://stackoverflow.com/questions/1196418/getting-a-password-in-c-without-using-getpass-3
También es más seguro, en teoría, porque una persona que vea la pantalla no podrá contar los asteriscos y deducir el número de caracteres que componen la contraseña.
Dal -
es más simple contentarse con anular el eco en el terminal, para no tener que mostrar las estrellas y gestionar todo lo que conlleva
En modo raw, el backspace muestra un carácter. Así que esto provocará la visualización de una estrella. Pero sigue siendo coherente con la entrada del carácter en la cadena de caracteres. Si gestionas el backspace, tendrás que hacerlo para las dos variantes. Pero admito que será un poco más simple en la variante sin visualización.
Por seguridad, estoy de acuerdo. No hay duda :-). -
Al pasar a la estructura termios los parámetros apropiados, puedes controlar finamente el comportamiento de la consola y definir en el campo c_lflag que ECHO sea retirado, manteniendo ICANON.
https://pubs.opengroup.org/onlinepubs/7908799/xsh/tcsetattr.html
https://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html
Este extracto de man 3 tcflow es más claro sobre el asunto:
Modo canónico y no canónico La configuración de la bandera canon ICANON en c_lflag determina si el terminal está operando en modo canónico (ICANON activado) o en modo no canónico (ICANON desactivado). Por defecto, ICANON está activado. En modo canónico: * La entrada se hace disponible línea por línea. Una línea de entrada está disponible cuando se teclea uno de los delimitadores de línea (NL, EOL, EOL2; o EOF al inicio de la línea). Excepto en el caso de EOF, el delimitador de línea está incluido en el búfer devuelto por read(2). * La edición de línea está habilitada (ERASE, KILL; y si la bandera IEXTEN está activada: WERASE, REPRINT, LNEXT). Una read(2) devuelve como máximo una línea de entrada; si la read(2) solicitó menos bytes de los disponibles en la línea actual de entrada, entonces solo se leen tantos bytes como se solicitaron, y los caracteres restantes estarán disponibles para una futura read(2). En modo no canónico, la entrada está disponible inmediatamente (sin que el usuario tenga que teclear un carácter delimitador de línea), y la edición de línea está deshabilitada.
Por lo tanto, al conservar el "modo canónico", retroceder, entrar, etc., se comportan normalmente, aun cuando el eco es suprimido, ya que el terminal sigue procesando líneas.
Sobre la seguridad, es más que pasar por el comando del sistema stty (que tiene el mismo grado de complejidad), pero puede ser objeto de trucos, como se mostró en mi prueba de concepto aquí https://forums.commentcamarche.net/forum/affich-27337373-masquer-mot-de-passe#24
Dal
... que encuentra que la lectura de este tema comienza a volverse complicada.
-
-
Gracias a todos por sus propuestas. Al final, procedí de la siguiente manera (sin mostrar los '*') :
printf(" Contraseña : ");
system("stty -echo"); // después de esta línea cualquier carácter escrito será oculto
fgets(passe, sizeof(passe), stdin);
system("stty echo"); // volvemos al modo normal-
Buena idea para no mostrar las estrellas.
Sin embargo, el uso de system("stty -echo"); debe ser prohibido ya que plantea problemas de seguridad y ni siquiera es POSIX.
Pero si estas limitaciones no te molestan, tienes algo aún más simple con la función getpass() que hará lo que quieres directamente ;-).
Y para tu información, ten cuidado con fgets(), ya que el '\n' se almacenará en tu cadena si hay espacio. -
Oui, como dice Fiddy, llamar a un comando externo no es lo mejor.
Aquí está tu código con la corrección sobre la cadena recuperada por fgets sugerida por Fiddy:
$ cat aadilove.c #include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char passe[11]; char *p; printf(" Contraseña: "); /* después de esta línea, todo carácter ingresado * debería estar oculto, a menos que un usuario * haya sustituido el comando stty */ system("stty -echo"); fgets(passe, sizeof(passe), stdin); if ((p = strchr(passe, '\n')) != NULL) *p = '\0'; /* regreso al modo normal */ system("stty echo"); printf("\nHas ingresado %s, pero nada " "debió mostrarse al ingresar\n", passe); return 0; }
Y aquí hay una ejecución "normal" de este código:
$ gcc -Wall aadilove.c -o aadilove $ ./aadilove Contraseña: Has ingresado toto, pero nada debió mostrarse al ingresar
Y aquí hay un ejemplo de lo que un usuario, incluso sin privilegios, puede hacer con este código (aquí, con el shell sh - Bourne Shell, adapta para otro shell la forma en que se manipulan las variables de entorno):
$ whereis stty stty: /bin/stty /usr/src/bin/stty $ echo $PATH /sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin:/home/ccm/bin $ pwd /usr/home/ccm/temp/testcommandsub $ PATH=/usr/home/ccm/temp/testcommandsub:$PATH $ export PATH $ echo "echo 'coucou'" > stty ; chmod +x stty $ ./aadilove coucou Contraseña: toto coucou Has ingresado toto, pero nada debió mostrarse al ingresar
Al menos deberías poner la ruta completa de stty ("/bin/stty" en mi caso) en tu comando system. No es práctico, porque se podría imaginar que un sistema lo coloca en otro lado. Además, no estás a salvo de que este comando sea reemplazado por otro que haga trucos (eso será más difícil), o de posibles vulnerabilidades de este comando.
Dal -
Exactamente la falla de seguridad en la que estaba pensando.
Sería necesario al menos poner la ruta completa de stty
Eso no cambia nada ^^. Solo son 6 caracteres más para escribir (sin contar la tecla de entrada ^^) para eludir la llamada a /bin/stty. Y se puede volver a jugar con el PATH. Aunque me parece que este truco ha sido corregido en los núcleos superiores a 2.4. Es mejor usar la familia exec().
O aún mejor, tu programa más arriba :-)
-