问题
In my web-application, there are two pages that deal with the user's account, the user account create/edit page and the login page. These pages do pretty much as expected, except I want to:
Prevent the browser's usernames/password list (aka, list) from being displayed on the user account create/edit page,
Conditionally display the list on the login page,
Conditionally display the browser's save username/password dialog (aka, save-dialog) after submitting the user account create/edit page, and
Prevent showing the save-dialog after submitting the login page.
The user account creation/edit page has a 'My Device' checkbox, that may be checked/unchecked to control some features in my web-app and allow the browser to show/hide its save-dialog and list on this page and on the login page.
While on the account creation/edit page, the list should never be displayed so a hacker can't easily see the usernames that have already been saved to the browser's usernames/passwords manager. However, after the user has logged-in, they can change their own username and/or password with this page, then submit the changes, and if the 'My Device' checkbox is checked, allow the browser to display the save-dialog.
The login page also uses the 'My Device' checkbox setting to allow the list to be seen or not when checked or not checked, respectively. Irrespective of the 'My Device' setting, the save-list should never be shown on this page, because this would cause the user's password on the server and the one in the browser to be out of sync, and prevent the owner from 'auto-logging' into my web-application successfully.
The below page doesn't show the list, nor should it, but it also doesn't always shows the save-dialog when a new/unsaved username/password combination or a changed set has been entered.
Here is a simplified account user create/edit page example that in Google Chrome, Microsoft Edge, and Firefox properly hides the list and many times shows the save-dialog when the "My device" checkbox is checked. However, in Microsoft Internet Explorer, the save-dialog is never displayed :
<!DOCTYPE html>
<html lang="en">
<!--
How to turn off form autocompletion
https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion#Preventing_autofilling_with_autocompletenew-password
The HTML autocomplete attribute
https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
-->
<head>
<meta charset="UTF-8">
<title>Username-Password Test Edit html</title>
<!-- Dot-Font used to simulate the password type input element -->
<link type="text/css" rel="stylesheet"
href="https://cdn.rawgit.com/noppa/text-security/master/dist/text-security-disc.css">
<script src="https://cdn.jsdelivr.net/npm/ua-parser-js@0/dist/ua-parser.min.js"></script>
<style>
* { font-size: x-large; font-family: Arial; }
/* Show placeholder in normal text */
.password_empty { font-family: inherit; }
/* Show value as dots */
.password_not_empty { font-family: 'text-security-disc'; }
.column1 { position: absolute; left: 30px; }
.column2 { position: absolute; left: 190px; top: 0; }
textarea,
input[type="email"],
input[type="password"],
input[type="text"] { height: 22px; width: 250px; padding-left: 2px; }
</style>
</head>
<body onload="pageLoad();">
<p id="message" style="opacity: 0;">
Please use the Submit button to save your information.
</p>
<form action="Username_Password_Test.php" method="post" autocomplete="off"
onsubmit="return submitIt();">
<!--
Hidden Username and Password input 'honey-pot' elements for the browser to notice,
so that it doesn't show the usernames/passwords list.
-->
<div id="username_password_div" style="display: none;">
<input type="text" id="user_name" name="user_name"
placeholder="username" tabIndex=-1
autocomplete="on" />
<input type="password" id="pass_word" name="pas_word"
placeholder="password" tabIndex=-1
autocomplete="new-password" />
</div>
<br />
<label class="column1">
Full Name:
<input type="text" id="fullname" name="fullname" tabIndex=1
autocomplete="off"
class="column2"
placeholder="Enter your full name"
value="Test Me" />
</label><br />
<br />
<label class="column1">
Email:
<input type="email" id="email" name="email" class="column2" tabIndex=2
autocomplete="off"
placeholder="Enter your email"
value="test.me@email.web" />
</label><br />
<br />
<label class="column1" title="Check if this is your personal device.">
Your device:
<span class="column2"
style="white-space: nowrap;">
<input type="checkbox" id="your_device" name="your_device" tabIndex=-1
autocomplete="off"
style="margin: 3px 0;"
value="yes" />
(Leave unchecked for strict account protections.)
</span>
</label><br />
<br />
<label class="column1">
Username:
<input type="text" id="username" name="username" tabIndex=3
autocomplete="off"
class="column2"
placeholder="Enter a username"
value="" />
</label><br />
<br />
<label class="column1">
Password:
<input type="text" id="password" name="password" tabIndex=4
autocomplete="off"
class="column2 password_empty"
placeholder="Enter a password"
oninput="set_password_ClassName();"
value="" />
</label><br />
<label class="column1" style="margin-top: 4px;">
Show password?
<input type="checkbox" id="view_hide_password" tabIndex=-1
class="column2" style="margin: 7px 0 0 0;"
onclick="set_password_ClassName(); password.focus();" />
</label><br />
<br />
<button id="submitForm" class="column1" >Submit</button><br /><br />
</form>
<script>
function pageLoad() {
var labels = document.querySelectorAll( 'label' );
var inputs, input;
// Create global variables ...
window.activeElement =
window.last_activeElement = null;
window.username = document.getElementById( 'username' );
window.password = document.getElementById( 'password' );
window.view_hide_password = document.getElementById( 'view_hide_password' );
window.submit = document.getElementById( 'submit' );
//
// Track which elements get the focus in the window.activeElement - used for
// IE/Edge to tell what element has the focus when the form is submitted,
// which sets the document.activeElement to the form when the form is submitted,
// thus preventing the form submit submitIt function to check which element
// actually has the focus.
//
document.addEventListener( 'focus', setFocus, true );
document.addEventListener( 'focusout', unsetFocus, true );
set_password_ClassName();
setFocus( username );
} // End of pageLoad() function.
function setFocus( target ) {
var target_wtx_context, activeElement_wtx_context;
//
// Is the target parameter missing or it can't be focused on, then use the
// event's target as the element to be focused on ...
//
target = ( ( ( target !== undefined ) && ( target.autofocus !== undefined ) )
? target
: ( ( event !== undefined ) &&
( event.target !== undefined ) &&
( event.target.autofocus !== undefined )
? event.target
: null ) );
//
// Get the target's wtx-context, unless the target is the submit button because
// they don't have them.
//
if( ( target.nodeName !== 'BUTTON' ) || ( target.id !== 'submitForm' ) ) {
target_wtx_context = getWTXContext( target );
//
// If the target has a wtx-context, then set the activeElement's
// wtx-context.
//
if( target_wtx_context ) {
activeElement_wtx_context = ( ( activeElement )
? activeElement.getAttribute( 'wtx-context' )
: null );
} // End of if( target_wtx_context ) ...
} // End of if( ( target.nodeName !== 'BUTTON' ) ||
// ( target.id !== 'submitForm' ) ) ...
//
// If the submit button or the current target and the activeElement aren't the
// same, then set the activeElememt to the current target ...
//
if( ( ( target.nodeName === 'BUTTON' ) &&
( target.id === 'submitForm' ) ) ||
( ( target_wtx_context !== activeElement_wtx_context ) &&
( isFocusable( target ) ) ) ) {
activeElement = target;
// Focus on the current target, if it isn't the submit button ...
if( ( target.nodeName !== 'BUTTON' ) ||
( target.id !== 'submitForm' ) ) {
target.focus();
}
else {
submitIt();
} // End of if( ( target.nodeName !== 'BUTTON' ) ||
// ( target.id !== 'submitForm' ) ) ...
// else ...
}
else if( last_activeElement ) {
// The current element and the activeElement are the same.
activeElement = last_activeElement;
}
else {
activeElement = document.active_Element;
} // End of if( ( ( target.nodeName === 'BUTTON' ) &&
// ( target.id === 'submitForm' ) ) ||
// ( ( target_wtx_context !== activeElement_wtx_context ) &&
// ( isFocusable( target ) ) ) ) ...
// else if( last_activeElement ) ... else ...
} // End of setFocus( target ) function.
function getWTXContext( target ) {
//
// The wtx-context attribute that the Google Chrome browser adds to many
// elements is useful for comparing elements rather than the
// 'fields/values' in element objects, but not all browsers add the wtx-content
// attribute, so this function is used to create a unique value when the
// getAttribute( 'wtx-content' ) function-call returns a null value.
//
var wtx_context = ( ( target !== null )
? target.getAttribute( 'wtx-context' )
: null );
return ( wtx_context
? wtx_context
: ( ++getWTXContext.counter ).toString() + '-wtx' );
} // End of getWTXContext( target ) function.
getWTXContext.counter = 1;
function unsetFocus() {
var target = event.target;
if( isFocusable( target ) ) {
last_activeElement = target;
} // End of if( isFocusable( target ) ) ...
} // End of unsetFocus() function.
function isFocusable( target ) {
return !( ( target.nodeName == 'BODY' ) ||
( target.nodeName == 'DIV' ) ||
( target.nodeName == 'SPAN' ) )
} // End of isFocusable( target ) function.
function set_password_ClassName() {
//
// Toggle between the normal text class and the dot-font in the pseudo-
// password input "password" type text element. This allows the user to
// view/hide the password value that they type into the input field.
// Additionally, the password-empty allows the placeholder to be readable
// when the pseudo- password input field doesn't contain a value.
//
var message = document.getElementById( 'message' );
var normal_text = 'password_empty';
// Remove the password_{not_}empty class ...
if( password.classList.contains( 'password_empty' ) ) {
password.classList.remove( 'password_empty' );
}
else if( password.classList.contains( 'password_not_empty' ) ) {
password.classList.remove( 'password_not_empty' );
} // End of if( password.classList.contains( 'password_empty' ) ) ...
// else if( password.classList.contains( 'password_not_empty' ) ) ...
if( view_hide_password !== null ) {
if( !view_hide_password.checked ) {
password.classList.add( ( password.value.trim().length > 0 )
? 'password_not_empty'
: 'password_empty' );
}
else {
// Show regular text in the password field ...
password.classList.add( normal_text );
password.type = 'text';
} // End of if( !view_hide_password.checked ) ... else ...
} // End of if( ( view_hide_password !== null ) ...
} // End of set_password_ClassName() function.
function submitIt() {
var pass_word, user_name;
if( activeElement &&
( activeElement.nodeName === 'BUTTON' ) &&
( activeElement.id === 'submitForm' ) ) {
if( document.getElementById( 'your_device' ).checked ) {
//
// Make the pseudo-password input type text element a real password field
// so that the browser will display the save-dialog when the username and
// password values haven't been saved before or have been changed.
//
password.type = 'password';
} // End of if( document.getElementById( 'your_device' ).checked ) ...
// The following errors, saying that submit() isn't a function.
// activeElement.form.submit();
// This works and submits the form ...
( Object.getPrototypeOf( activeElement.form ).submit ).
call( activeElement.form );
}
else {
// The form is being submitted, but the activeEement isn't the submit button.
//
// Display a message telling the user to use the submit button to submit the
// form.
//
if( message.timer ) clearInterval( message.timer );
message.style.opacity = 1; // Full opacity.
message.duration = 10; // Start from 10, subtract 1 every 1/8 second.
message.timer = setInterval(
function() {
var currentOp = getComputedStyle( message ).
getPropertyValue( 'opacity' );
var duration = parseFloat( message.duration );
if( duration > 0 )
message.duration = ( duration - 1 ).toString();
else {
if( currentOp > 0 )
currentOp -= 0.1;
else {
currentOp = 0;
clearInterval( message.timer );
} // End of if( currentOp > 0 ) ...
// else ...
message.style.opacity = currentOp.toString();
} // End of if( duration = > 0 ) ...
// else ...
},
125 );
} // End of if( activeElement &&
// ( activeElement.nodeName === 'BUTTON' ) &&
// ( activeElement.id === 'submitForm' ) ) ...
return false;
} // End of submitIt() function.
</script>
</body>
</html>
Thank you
来源:https://stackoverflow.com/questions/58811233/in-ie-how-can-displaying-the-browsers-list-of-saved-usernames-passwords-and-sa