Script para teclear la contraseña en Bankia

Tengo algunas cuentas en Bankia. A la hora de identificarme en la web para operar por Internet pide, como suele ser habitual, un usuario y una contraseña: login_bankia El problema es que, a la hora de introducir la contraseña, no permite teclear los dígitos de la misma. Para mayor seguridad han creado un pequeño teclado virtual que aparece al acceder al campo de la clave y sobre el que hay que pinchar para marcar la misma. También se puede teclear directamente pero cambian los dígitos por letras de forma aleatoria. Por ejemplo, en este caso para marcar el “1” habría que pulsar la “U”, para marcar el “5” la “B”, etc: teclado_virtual_bankia A mi esto me resulta incómodo, así que desde hace tiempo tenía ganas de programar un script greasemonkey para poder saltarme la restricción del teclado virtual y escribir los números directamente. Me daba un poco de pereza porque el código javascript que utiliza la web es Prototype en vez de JQuery que es al que estoy más habituado, pero ayer por fin me animé y me puse con ello. La idea era insertar un manejador de eventos en el campo de la clave para que al pulsar una tecla la sustituyese por el código de tecla del número correspondiente. Para eso, lo primero era obtener el campo correspondiente a la clave. Esto no es tan sencillo ya que el ID del campo de la clave es diferente en cada carga de la página, así que decidí echar un vistazo al código para ver si encontraba una forma sencilla y sobre todo robusta de hacerlo. Lo primero fue obtener y desminifizar el código. Lo que necesitaba estaba en core.js. Buscando un poco encontré la clase OI.Security.Keyboard que es donde se maneja casi todo lo referente al teclado virtual. La clase tiene una función que es la encargada de buscar el campo clave, junto con otros datos:
// Esta función se llama en el onload de la página.
OI.Security.Keyboard.registerKeyboards = function () {
    var a = OI.Security.Keyboard.prefijoInputCheck;

    $$("label.forClave").each(function (c) {
	// Esto lo que hace es buscar el campo de texto donde se introduce la clave
	// Busca las etiquetas con clase "forClave" y saca el id del campo del atributo "for"
        if (c.attributes["for"] && $(c.attributes["for"])) {
            // ¿"d" es una funcion de validación?
            var d = OI.Security.Keyboard.buscaIdCampo(c, a);
            var b = new OI.Security.Keyboard(c.attributes["for"].value, d);
            OI.Security.Keyboard.postRegisterKeyboard(b, c)
        }
    })
};
Con esto pude averiguar que el campo de la contraseña se puede encontrar buscando en el <label> con estilo forClave, y que el id se puede encontrar en el atributo for de dicho campo. La función buscaIdCampo en principio no me interesaba así que no investigué más sobre ella: parece que obtiene el nombre de una función de validación que está codificada como un estilo css aplicado al label anterior. Con esto ya pude escribir lo primero que necesitaba, una función para obtener el campo de la contraseña:
function getPasswordField(){
    var campo;
    $$("label.forClave").each(
        function (c) {
            if (c.attributes["for"] && $(c.attributes["for"])) {
                campo = $(c.attributes["for"].value)
            }
        })
    return campo;
}
Lo siguiente era encontrar la forma de encontrar a qué número pertenecía cada letra. Podría haberlo hecho examinando el DOM de la página para encontrar los elementos del teclado virtual, pero la clase OI.Security.Keyboard me lo puso más fácil todavía: en su inicialización crea también un objeto, keyboardData, que contiene la relación entre letras y números. Es más, ese objeto tiene dos funciones, valueOfLetter y valueOfNumber, que permiten hacer esa traslación directamente. Los objetos manejadores del teclado virtual se almacenan en el array OI.Security.Keyboard.keyboards (por lo que he visto en el código, pueden haber varios teclados seguros en la página de forma simultánea). El único truco fue buscar el objeto OI.Security.keyBoard en cada pulsación de teclado ya que cuando se ejecuta el script Greasemonkey todavía no está disponible (seguro que hay alguna forma más elegante de solucionar esto pero no me dediqué a investigarlo) También tuve que ver qué pasaba al pulsar una de las letras del teclado virtual, para reproducirlo en el código. Los manejadores de eventos se instalan en la función bindBehaviours de la clase. Los eventos que me interesaban llamaban a una función que a su vez terminaba llamando a la función addLetter. Esta última añade la letra correspondiente y llamaba también a la función chequeaValor(), así que esto es lo que tuve que incluir en el manejador. La última pieza que faltaba era detectar la pulsación de un número. Es posible que ya exista una función que lo haga pero perdía más tiempo buscándola que haciéndolo yo, así que programé una directamente: numberFromKeycode. Con todo esto, pude crear una primera versión del código que, instalado en el manejador del campo clave, producía los resultados esperados:
// Event handler. This function does the work.
function translateNumbers(e){
    var number = numberFromKeycode(e.keyCode);
    if (number == -1) return;

    // We must search these objects here because, if we
    // do it earlier, they are not avaliable
    var security = OI.Security.Keyboard.keyboards[0];
        // Security keyboard object
    var keyboard = security.keyboardData;
        // Virtual keyboard data
        
    passwordField.value += keyboard.valueOfNumber(number);
    security.chequeaValor();
}

// Y la instalación del manejador:
var passwordField = getPasswordField(); //Esta es la función comentada más arriba
// Add event handler to password field
Event.observe(passwordField, "keyup", translateNumbers);
Tras cargar la página de Bankia… voilà!: el teclado de login se comportaba como yo quería, admitiendo los números, y conseguí entrar por primera vez sin tener que pulsar en el dichoso teclado. Estaba tan contento con el código… hasta que se me ocurrió hacer una operación de verdad, donde la página te pide una firma digital con el mismo sistema que el del login. Ahí, el script falló y tuve que volver a poner los números “a mano”. Tras un poco de investigación descubrí que el problema es que el teclado virtual se creaba de forma dinámica, así que cuando se ejecutaba mi script (en el load de la página) no encontraba el campo clave y por tanto no instalaba el manejador. La forma más rápida de solucionar esto (que no la más elegante) fue instalar otro manejador de eventos, esta vez en la página, para que cada vez que teclease un número mirase a ver si se había creado un campo password y le instalase el manejador. Esta solución es un poco bestia pero en realida no suelo teclear muchos números en la página de Bankia así que la sobrecarga no sería excesiva:
// Search for password field every time a number is pressed.
// It could have been created dinamically, therefore it wouldn't had been installed when
// this script was loaded
function findDynamicallyCreatedPasswordField(e) {
    var number = numberFromKeycode(e.keyCode);
    if (number == -1) return;
    
    if(!passwordField) {
        // Do we have a password field now?
        passwordField = getPasswordField();
        if(!passwordField) return;
        
        // Remove document keyboard handler, it is not necessary anymore
        Event.stopObserving(document, "keyup", findDynamicallyCreatedPasswordField)
        
        // Install password field keyboard handler        
        Event.observe(passwordField, "keyup", translateNumbers);
        // Relaunch this event, or this first number would be lost
        translateNumbers(e)
    }
}

// Y la instalación del manejador:

// If password field already exists...
if(passwordField) {
    // Add event handler to password field
    Event.observe(passwordField, "keyup", translateNumbers);
} else {
    // Add a document handler to search a password field every number pressed
    Event.observe(document, "keyup", findDynamicallyCreatedPasswordField);
}
Con esto último conseguí que todo funcionara bien: ya nunca más tendré que pulsar con el ratón las letras del tecladito de las narices. Prueba superada. Tenéis el código publicado como un script de greasemonkey en userscripts. Este código es peligroso, así que antes de publicarlo le añadí una pequeña “protección” para asegurarme de que quien se lo instale sepa lo que está haciendo: no me gustaría que alguien con el ordenador lleno de troyanos se lo instalase y le pudiesen dejar pelado con un keylogger. Si habéis llegado hasta aquí seguro que no tenéis problema para editar el código y hacer lo que corresponda. Espero que hayáis disfrutado con este pequeño hackeo para hacer la vida un poco más fácil.

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *