Last active
November 8, 2024 11:55
-
-
Save jroper/5cc5f7359ccc89a2a5b9691c33114b9d to your computer and use it in GitHub Desktop.
Allow password managers to work with ING Australia's login
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name ING Australia Password Manager Fix | |
// @namespace https://jazzy.id.au/ | |
// @version 0.1 | |
// @description Allows the industry best practice of using a password manager for storing passwords for ING Australia's login screen. | |
// @author James Roper | |
// @match https://www.ing.com.au/securebanking/ | |
// @grant none | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
console.log("Running script"); | |
// First, give the client number field a name. This ensures that it can be filled by a password manager. | |
document.getElementById("cifField").setAttribute("name", "username"); | |
// The ING password field is protected by a home grown "security feature" (lesson number one of security, don't use | |
// your own home grown security mechanism), where they use images of digits presented in a random order, and which | |
// image is for which digit is only known by the server, and of course anyone that can read the digit from the | |
// image. The actual binary data of the image changes each time, so we can't just do a mapping of binary data to | |
// digit. However, it's trivial to recognise the number by looking at the image itself, for example, the number | |
// zero has an orange pixel at the centre of the image, while the number 8 has a white pixel. By inspecting a few | |
// specially chosen pixels, we can define a heuristic to map an image to a digit. So that's what this does. I don't | |
// know why they have this "security feature", it's not a security feature at all, it's as dumb as ROT13. The | |
// connection is already encrypted, so eavesdroppers can't discover the password anyway, this only gets in the way | |
// of scripts running locally (such as a password manager) from accessing the password, but as this script shows, | |
// it's trivial to work around it. I guess the person that created this thinks they are very clever, more clever | |
// than all the rest of the IT security industry who recommends strongly against these types of practices. *sigh* | |
function identify(img) { | |
let myImg = new Image(); | |
myImg.src = img; | |
let context = document.createElement("canvas").getContext("2d"); | |
context.drawImage(myImg, 0, 0); | |
function isOrange(x, y) { | |
return context.getImageData(x, y, 1, 1).data[2] === 0; | |
} | |
function isWhite(x, y) { | |
return context.getImageData(x, y, 1, 1).data[2] === 255; | |
} | |
if (isOrange(90, 55)) { | |
if (isOrange(85, 57)) return 5; | |
else if (isWhite(96, 47)) return 0; | |
else if (isWhite(89, 59)) return 4; | |
else return 6; | |
} else if (isOrange(95, 62)) { | |
if (isOrange(96, 67)) return 7; | |
else if (isWhite(97, 47)) return 2; | |
else return 1; | |
} else { | |
if (isWhite(84, 61)) return 8; | |
else if (isWhite(84, 47)) return 9; | |
else return 3; | |
} | |
return -1; | |
} | |
// A map of keypad digits to the image for that digit. | |
let keypadMap = new Array(10); | |
// Populate the keypad map by identifying which digit is in each image. | |
function populateMap() { | |
let keys = document.querySelectorAll("img.ing-keypad"); | |
for (let i = 0; i < keys.length; i++) { | |
// ignore delete and cancel keys | |
if (i != 9 && i != 11) { | |
let digit = identify(keys[i].src); | |
console.log("Identified key " + i + " as " + digit); | |
keypadMap[digit] = keys[i]; | |
} | |
} | |
} | |
// Reads the password field, and clicks the corresponding number images to fill it out. | |
function enterPassword() { | |
let pwField = document.getElementById("password"); | |
let pw = pwField.value; | |
if (pw.length > 0) { | |
for (let i = 0; i < pw.length; i++) { | |
let digit = parseInt(pw.charAt(i)); | |
keypadMap[digit].click(); | |
} | |
pwField.onchange = function() {}; | |
return true; | |
} | |
return false; | |
} | |
// It takes time for the keypad to initialize, so we give it a second. | |
window.setTimeout(function() { | |
populateMap(); | |
// Try and fill out the password immediately, hoping that a password manager has filled it, | |
// but if it hasn't, then set an onchange event handler. | |
if (!enterPassword()) { | |
document.getElementById("password").onchange = enterPassword; | |
} | |
}, 1000); | |
})(); |
dont know if this still works for you man, but i can't get it to work with Lastpass
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This works for me with lastpass - though you do need to configure lastpass with custom form fields so that it fills the client number into the "username" field and your access code into the "password" field.