En mi anterior post hablaba de c├│mo construir un programa "clon" de la utilidad de l├şnea de comandos Windows RunAs. En esta primera parte vimos c├│mo se lanzaba un proceso suplantando a un usuario, que era el objetivo principal de la aplicaci├│n, si bien es la parte m├ís f├ícil.

La tarea que nos quedó pendiente de ver es la más complicada y consiste en solicitar al usuario la clave de manera segura, sin que llegue a estar almacenada en claro en la memoria siquiera. Para ello usamos la clase SecureString, que apareció para estos menesteres en la versión 2.0 del framework.

Sin m├ís vueltas vamos a ver directamente c├│mo se solicita dicha clave y pasamos a explicar los puntos de inter├ęs:

        private static SecureString PideClave(string nomUsuario)
        {
            Console.WriteLine("Escriba la contrase├▒a para {0}:", nomUsuario);

            int top, left;
            ConsoleKeyInfo cki;
            SecureString clave = new SecureString();

            top = Console.CursorTop;
            left = Console.CursorLeft;

            bool ModoEntrada = true;
            while (ModoEntrada)
            {
                cki = Console.ReadKey(true);

                switch(cki.Key)
                {
                    //Si se pulsa ESC salimos del bucle
                    case ConsoleKey.Escape:
                        ModoEntrada = false;
                        clave = null;   //devolvemos un null para indicar que el programa debe terminar
                        break;

                    //Con ENTER terminamos la entrada de datos
                    case ConsoleKey.Enter:
                        ModoEntrada = false;
                        break;

                    //Si se pulsa la tecla de borrado hacia atrás hay que "currarse" el eliminar el caracter
                    //y que se vea en la pantalla eso
                    case ConsoleKey.Backspace:
                        if (clave.Length > 0)
                        {
                            Console.SetCursorPosition(left + clave.Length - 1, top);
                            Console.Write(" ");
                            Console.SetCursorPosition(left + clave.Length - 1, top);
                            clave.RemoveAt(clave.Length - 1);
                        }
                        break;
                    
                    //Cualquier otra tecla
                    default:
                        if (Char.IsLetterOrDigit(cki.KeyChar) || cki.KeyChar == '_')
                        {
                            clave.AppendChar(cki.KeyChar);
                            //Se sustituye por un asterisco
                            Console.SetCursorPosition(left + clave.Length - 1, top);
                            Console.Write('*');
                        }
                        break;
                }
            }
            Console.Write("\n");
            return clave;
        }

B├ísicamente lo que se hace es ir solicitando las letras de la clave una a una, capturando las pulsaciones de las teclas con el m├ętodo ReadKey de la clase Console. Este m├ętodo toma un par├ímetro opcional que indica si se debe mostrar el caracter pulsado por pantalla o no. En este caso le indicamos que debe interceptar la pulsaci├│n de la tecla y no mostrarla por pantalla (valor true en el par├ímetro del m├ętodo). Lo que hacemos posteriormente es escribir un asterisco. De este modo damos informaci├│n visual sobre cu├íntos caracteres se han introducido, pero no los mostramos ni permitimos que se almacenen en lado alguno, sino que los metemos dentro de la cadena segura con su m├ętodo AppendChar.

Tambi├ęn capturamos algunas teclas especiales que se pueden pulsar:

  • ESC: con esto devolvemos un nulo que se comprueba en el c├│digo llamante parando la ejecuci├│n del programa.
  • ENTER: finalizamos la introducci├│n de datos dando por v├ílida la contrase├▒a introducida hasta el momento.
  • BACKSPACE: la tecla de borrado (con la flechita a la izquierda), que sirve para corregir el ├║ltimo caracter.

Listo. con esto pedimos la clave al usuario sin comprometer la seguridad haci├ęndola pasar por una cadena normal. ├ëstas son objetos inmutables que permanecen en memoeria mientras no las reclame el recolector de basura y aunque las "sobrescribamos" (es un decir, por que lo que conseguir├şamos es s├│lo una nueva cadena, ya que no se destruyen al asignarle otro valor a la misma variable, como se comenta en ese post antiguo que referencio).

Exactamente el mismo principio se podr├şa aplicar para pedir de manera segura una clave en una aplicaci├│n Windows (capturando letra a letra y sin dejar que ├ęstas se reflejen luego en la interfaz de usuario o en alguna cadena).

Tambi├ęn os dejo, como promet├ş, el c├│digo completo del programa RunAsClon (20 KB) para que el que quiera le pueda echar un vistazo. He usado regiones condicionales para laznar el programa de manera c├│moda para depurar mientras estamos en modo de desarrollo (debug), pero es lo ├║nico a mayores que tiene el c├│digo. Lo he hecho con Visual Studio 2008 pero funcionar├í perfectamente con Visual Studio 2005 tambi├ęn si importas el .cs. He incluido el resultado de la compilaci├│n tanto en depuraci├│n como en "release" por si alguien lo quiere utilizar directamente sin tener que compilarlo.

Si alguien quiere crear su propio programa RunAs que se ejecute sin pedir la clave al usuario, sino incluy├ęndola como parte de la l├▒inea de comandos (no recomendable pero ├║til en muchos casos) puede tocar ligeramente este c├│digo para conseguirlo sin problema.

┬íEspero qu├ę te resulte ├║til!

💪🏻 ┬┐Este post te ha ayudado?, ┬┐has aprendido algo nuevo?
Pues NO te pido que me invites a un caf├ę... Te pido algo m├ís f├ícil y mucho mejor

Escrito por un humano, no por una IA