Ocultar contraseña

Resuelto
aadilove Mensajes publicados 31 Estado Miembro -  
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...

4 respuestas

  1. Usuario anónimo
     
    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
    1
    1. fiddy Mensajes publicados 441 Fecha de registro   Estado Colaborador Última intervención   1 847
       
      Tu método no funciona: obliga al usuario a presionar la tecla de entrada para cada carácter.
      Además, una pantalla borrada puede aparecer en algunos sistemas operativos al subir el desplazador.
      0
    2. Usuario anónimo
       
      non puedes simplemente hacer un bucle (getchar de memoria pero eso se remonta) que espera solo 1 carácter, sea cual sea. Tú detrás gestionas ese carácter (enter o no).
      0
    3. Usuario anónimo
       
      Désolé, je ne peux pas accéder à des liens externes ou en extraire du contenu.
      0
    4. fiddy Mensajes publicados 441 Fecha de registro   Estado Colaborador Última intervención   1 847
       
      Olvidas una noción básica en C...
      Las funciones de lectura estándar (getchar (), ...) no leen del teclado sino de su buffer. Sin embargo, para que un carácter entre en el buffer del teclado, hay que presionar la tecla de entrada.
      La única forma es pasar al modo raw () como dice Dal.
      0
    5. Usuario anónimo
       
      ah voilà je crois que c'est ça : readKey. (ça fait 5/6 ans hein ... et maintenant je travaille beaucoup sur windev T_T)
      elle te retourne la valeur entière de la touche appuyée (après faut se reporter à l'ascii simplement)
      0
  2. [Dal] Mensajes publicados 6122 Fecha de registro   Estado Colaborador Última intervención   1 108
     
    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
    0
    1. fiddy Mensajes publicados 441 Fecha de registro   Estado Colaborador Última intervención   1 847
       
      Escribir un * no es mucho más complicado que pasar el terminal a raw.
      Con tu primer enlace, pasas el terminal a raw. Tan pronto como lees el carácter ingresado por el usuario, muestras un '*'. Nada más.
      0
    2. [Dal] Mensajes publicados 6122 Fecha de registro   Estado Colaborador Última intervención   1 108
       
      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
      0
    3. fiddy Mensajes publicados 441 Fecha de registro   Estado Colaborador Última intervención   1 847
       
      [Dal],
      Olvidas un detalle.
      Estoy de acuerdo en manejar la tecla backspace. Pero hay que hacerlo en ambos casos (mostrando asteriscos o sin mostrar). De hecho, en modo raw, cada backspace provocará un carácter en la cadena de caracteres.
      0
    4. [Dal] Mensajes publicados 6122 Fecha de registro   Estado Colaborador Última intervención   1 108
       
      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
      0
  3. [Dal] Mensajes publicados 6122 Fecha de registro   Estado Colaborador Última intervención   1 108
     
    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
    0
    1. [Dal] Mensajes publicados 6122 Fecha de registro   Estado Colaborador Última intervención   1 108
       
      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
      0
    2. fiddy Mensajes publicados 441 Fecha de registro   Estado Colaborador Última intervención   1 847
       
      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 :-).
      0
    3. [Dal] Mensajes publicados 6122 Fecha de registro   Estado Colaborador Última intervención   1 108
       
      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.
      0
  4. aadilove Mensajes publicados 31 Estado Miembro
     
    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
    0
    1. fiddy Mensajes publicados 441 Fecha de registro   Estado Colaborador Última intervención   1 847
       
      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.
      0
    2. [Dal] Mensajes publicados 6122 Fecha de registro   Estado Colaborador Última intervención   1 108
       
      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
      0
    3. fiddy Mensajes publicados 441 Fecha de registro   Estado Colaborador Última intervención   1 847
       
      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 :-)
      0