There are various things you could attempt, but none of them will meaningfully do anything. For example, you can intercept each typed character, store it in a JS variable, and then replace it in the input with something menaingless. Reading the value of the input won't get the attacker anything, but they can just read the JS variable instead. You can hide the variable by putting it in a private closure with no read functions - only ones to update the password as it's typed, and one to send it to the server - but that's not going to hide it from somebody who does the same kind of thing, monitoring keystrokes or changes to the input in real time. You could try messing with the property accessors in the DOM. Specifically, overriding
value on the input element so that it doesn't return the real value is possible, and probably what BoA did. It doesn't actually add any real security because there's so many other ways to either monitor keystrokes, etc. or to extract the full password anyhow. Some ways: get the
outerHTML of the input or any containing element, or
innerHTML of any containing element, or if it uses an HTML form edit the action (destination) to point to your own site, or use DOM manipulation to remove or overlay the not-JS-readable password input with one that the JS can read (before the user starts to type), or...
One option might be to use a sandboxed iframe for the actual login form. Something like
<iframe sandbox="allow-forms allow-top-navigation-by-user-activation"... > which prevents script execution and prevents the frame contents from being read by the parent. Put the login form, and all dynamically-generated content (that might be subject to XSS), on the page that loads inside the iframe. Add a script that immediately navigates the user away if scripts are allowed at all (they won't be, inside the sandbox), and use
X-Frame-Options and/or CSP's
frame-ancestors to prevent third-party pages from loading the login page in an iframe. Inside the iframe, you can have a standard HTML form, and give it a
target="_top" attribute to make it load the login result in the top-level context. Any injected script (which is what XSS generally is) won't run because of the sandbox. (Of course, no other script will either, for the same reason, so your login form will have to cope with that). Don't put any dynamic content in the parent page (where scripts work), because even though the password input isn't in a scripts-work zone, XSS in the parent page could replace or edit the entire iframe with one more amenable to stealing your data.
Or you could just do the reasonable thing and stop chasing this fringe benefit of protecting a specific input field from XSS (most XSS happens after authentication, after all). Keep your login page simple. Don't use external scripts (or any scripts, unless they directly relate to the basic functionality of logging in). Don't use any user-supplied input if you can help it; if you can't help it for some reason, make sure it's output encoded and maybe input validated for good measure. Don't do any DOM modification that could enable DOM-based XSS. Add highly restrictive CSP (which should be EASY for such a clean page) for defense-in-depth. Voila: your password input is protected from XSS.