En el post de hoy vamos a leer una cadena usando C (compatible con C++) pero no vamos a mostrarla, simplemente vamos a mostrar asteriscos o cualquier otro carácter mientras el usuario escribe.
Esto será hecho en C con MinGW en Windows pero lo he probado igual con Dev-C++. Al final tendremos un programa que va a solicitar una contraseña, no la va a mostrar y mostrará asteriscos en su lugar.
Pequeña nota de seguridad
Ya no deberíamos usar C para cosas de seguridad. Sí, sé que varias cosas están programadas con C (librerías, paquetes, núcleos de sistemas operativos) y que si lo usamos bien no habrá problema, pero ya no es recomendable.
Yo hago este tutorial por si tu profesor quiere enseñarte las bases y hacerte sufrir pensando en el algoritmo, pero ya no deberíamos manejar contraseñas con C al menos que sea para tareas o cosas divertidas.
Explicando el algoritmo
Necesitamos una forma de leer cada carácter o tecla presionada sin que el usuario presione Enter. En Windows existe getch
, que nos da la tecla presionada, misma que fácilmente puede ser convertida a char
.
Por cada carácter leído lo convertimos a cadena y lo concatenamos a la contraseña leída.
Estoy manejando 2 casos especiales: cuando se presiona la tecla de borrado y cuando se presiona Enter; en ese caso los códigos de las mismas son 8
y 13
respectivamente.
Cuando se presiona la tecla de borrado, eliminamos el último carácter de nuestra cadena. Y cuando se presiona Enter suponemos que el usuario terminó de escribir su contraseña.
Finalmente, cada que el usuario presiona una tecla limpiamos la pantalla y enmascaramos la cadena. Esto solo es necesario si enmascaras la cadena.
Nota: seguramente debe haber algunas teclas que no estoy tomando en cuenta, por ejemplo si el usuario quiere mover la posición del cursor con una tecla de dirección, o cosas de esas.
Leer contraseña en C
Vamos a la función. He encerrado todo el comportamiento en un método para poder invocarlo cuantas veces sea necesario.
Por cierto, esta función no regresa la cadena, simplemente coloca la contraseña en la cadena de destino que tú le indiques, por lo que debes declarar la string antes y enviar el apuntador a la misma.
void leerContrasenaSinMostrarla(char *palabraSecretaDestino)
{
int caracterLeido = 0;
char temporal[2];
while (caracterLeido != TECLA_ENTER)
{
limpiarPantalla();
printf("Ingresa tu palabra secreta: ");
enmascaraCadena(palabraSecretaDestino, strlen(palabraSecretaDestino), MASCARA);
caracterLeido = getch();
// https://parzibyte.me/blog/2019/11/02/c-char-a-string/
temporal[0] = caracterLeido;
temporal[1] = '\0';
if (caracterLeido == TECLA_BORRAR)
{
if (strlen(palabraSecretaDestino) > 0)
{
// https://parzibyte.me/blog/2022/03/23/eliminar-ultimo-caracter-cadena-c/
palabraSecretaDestino[strlen(palabraSecretaDestino) - 1] = '\0';
}
}
else if (caracterLeido != TECLA_ENTER)
{
strncat(palabraSecretaDestino, temporal, 1);
}
}
}
Hacemos un ciclo que se va a detener cuando el usuario presione Enter. Dentro del ciclo vemos si el usuario presionó el backspace para eliminar el último char, y en caso de que no, vemos si presionó algo distinto al Enter para concatenar ese carácter a la contraseña.
Fíjate en que tenemos dos funciones extras: la de enmascarar la cadena (que hace tiempo también hice con Arduino) y la que limpia la pantalla.
Enmascarar cadena
Para mostrar asteriscos en lugar de la cadena real, simplemente imprimimos un carácter desde 0 hasta la longitud de la cadena menos 1. Así:
void enmascaraCadena(char *cadena, int longitud, char mascara)
{
int i = 0;
for (i = 0; i < longitud; i++)
{
printf("%c", mascara);
}
}
Limpiar pantalla
Existen varias formas de limpiar la pantalla, y también depende de la plataforma, terminal, sistema operativo y creo que también del estado de ánimo del programador.
Yo he usado la siguiente función:
void limpiarPantalla()
{
// system("cls"); // Comentar en Linux y descomentar en Windows
printf("\e[1;1H\e[2J"); // Comentar en Windows y descomentar en Linux
}
Por cierto este es un buen lugar para documentar que en ocasiones la limpieza de pantalla modificaba la cadena y no permitía eliminar el carácter. Todo muy raro, pero al final ya no me pasó.
Poniendo todo junto
Finalmente, para leer una cadena sin mostrarla, y en su lugar mostrar asteriscos ya sea en C o C++ tenemos el siguiente código junto con su implementación en el main
:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
#define TECLA_BORRAR 8
#define TECLA_ENTER 13
#define MAXIMA_LONGITUD_CADENA 1000
#define MASCARA '*'
/*
https://parzibyte.me/blog
*/
void limpiarPantalla()
{
// system("cls"); // Comentar en Linux y descomentar en Windows
printf("\e[1;1H\e[2J"); // Comentar en Windows y descomentar en Linux
}
void enmascaraCadena(char *cadena, int longitud, char mascara)
{
int i = 0;
for (i = 0; i < longitud; i++)
{
printf("%c", mascara);
}
}
void leerContrasenaSinMostrarla(char *palabraSecretaDestino)
{
int caracterLeido = 0;
char temporal[2];
while (caracterLeido != TECLA_ENTER)
{
limpiarPantalla();
printf("Ingresa tu palabra secreta: ");
enmascaraCadena(palabraSecretaDestino, strlen(palabraSecretaDestino), MASCARA);
caracterLeido = getch();
// https://parzibyte.me/blog/2019/11/02/c-char-a-string/
temporal[0] = caracterLeido;
temporal[1] = '\0';
if (caracterLeido == TECLA_BORRAR)
{
if (strlen(palabraSecretaDestino) > 0)
{
// https://parzibyte.me/blog/2022/03/23/eliminar-ultimo-caracter-cadena-c/
palabraSecretaDestino[strlen(palabraSecretaDestino) - 1] = '\0';
}
}
else if (caracterLeido != TECLA_ENTER)
{
strncat(palabraSecretaDestino, temporal, 1);
}
}
}
int main()
{
char palabraSecreta[MAXIMA_LONGITUD_CADENA] = "";
leerContrasenaSinMostrarla(palabraSecreta);
// Nota: obviamente si no quieres mostrarla no la muestres, de todos modos la contraseña ya estará en palabraSecreta
printf("\nLo que escribiste es: '%s'", palabraSecreta);
return 0;
}
En el main defino mi cadena (línea 61) y la envío a la función leerContrasenaSinMostrarla
. Cuando el usuario presione Enter, la función terminará y yo tendré la contraseña leída desde C.
Recuerda que tú decides qué hacer con la contraseña. Yo la muestro por motivos de prueba, pero tú puedes hacer cualquier otra cosa.
Para terminar te dejo con más tutoriales de C en mi blog.