¿Cómo sabe el navegador cuándo pedirle al usuario que guarde la contraseña?

82

Esto está relacionado con la pregunta que hice aquí: ¿Cómo puedo hacer que el navegador solicite guardar la contraseña?

Este es el problema: NO PUEDO hacer que mi navegador me solicite que guarde la contraseña del sitio que estoy desarrollando. (Me refiero a la barra que aparece a veces cuando envías un formulario en Firefox, que dice "¿Recuerdas la contraseña de tu sitio.com? Sí / Ahora no / Nunca")

Esto es muy frustrante porque esta característica de Firefox (y la mayoría de los otros navegadores modernos, que espero que funcionen de manera similar) parece ser un misterio. Es como un truco de magia que hace el navegador, donde mira su código, o lo que envía, o algo, y si "parece" un formulario de inicio de sesión con un campo de nombre de usuario (o dirección de correo electrónico) y un campo de contraseña, ofrece ahorrar.

Excepto en este caso, donde no les ofrece a mis usuarios esa opción después de que usan mi formulario de inicio de sesión, y me está volviendo loco. :-)

(Revisé mi configuración de Firefox, NO le he dicho al navegador "nunca" para este sitio. Debería aparecer un mensaje).

Mi pregunta

¿Cuáles son las heurísticas que utiliza Firefox para saber cuándo debe pedirle al usuario que guarde? Esto no debería ser demasiado difícil de responder, ya que está allí mismo en la fuente de Mozilla (no sé dónde buscar o de lo contrario trataría de desenterrarlo yo mismo). Tampoco tuve suerte al encontrar una publicación de blog o alguna otra nota similar de los desarrolladores de Mozilla sobre esto.

(Estaría bien con esta pregunta respondida para Safari o IE; me imagino que todos los navegadores utilizan reglas muy similares, por lo que si puedo hacer que funcione en uno de ellos, funcionará en los demás).

(* Tenga en cuenta que si su respuesta tiene algo que ver con las cookies, el cifrado o cualquier otra cosa que tenga que ver con cómo estoy almacenando las contraseñas en mi base de datos local, es muy probable que haya entendido mal mi pregunta. :-)

Eric
fuente
1
No se. ¿Es su formulario un formulario POST con un campo de tipo contraseña?
Zneak
2
Sí, envuelto en etiquetas <form>, y los campos se denominan 'nombre de usuario' y 'contraseña'. Lo cargo como una capa separada con AJAX, pero también lo hace disqus.com (solo para lanzar un ejemplo) y funciona muy bien para ellos. Es por eso que, en lugar de (continuar) retocando las cosas al azar para ver si de alguna manera ayuda, quiero saber exactamente cómo está pensando el navegador.
Eric

Respuestas:

55

Según lo que he leído, creo que Firefox detecta las contraseñas form.elements[n].type == "password"(iterando a través de todos los elementos del formulario) y luego detecta el campo del nombre de usuario buscando hacia atrás a través de los elementos del formulario para el campo de texto inmediatamente antes del campo de la contraseña (más información aquí ). Puede probar algo similar en Javascript y ver si puede detectar su campo de contraseña.

Por lo que puedo decir, su formulario de inicio de sesión debe ser parte de un <form>o Firefox no lo detectará. La configuración id="password"de su campo de contraseña probablemente tampoco haría daño.

Si esto todavía le está dando muchos problemas, le recomendaría que pregunte en una de las listas de correo de desarrolladores del proyecto Mozilla (incluso puede obtener una respuesta del desarrollador que diseñó la función).

bta
fuente
2
Eso es maravilloso. Gracias. Eso es lo que estoy buscando.
Eric
1
Esto me ayudó a que funcionara en Firefox, pero no estoy teniendo suerte con Chrome. Este es el formulario que estoy usando: <form id = "myForm" action = "#"> <input id = "username"> <input id = "password" type = "password"> </form>
1.21 gigavatios
3
@ 1.21gigawatts- No hay una forma "estándar" de hacer esto (que yo sepa), por lo que diferentes navegadores pueden hacerlo de manera ligeramente diferente. No sé en qué se diferencia el proceso de Chrome del de Firefox, pero es posible que puedas averiguarlo si miras el código fuente de Chrome. La única forma en que supe cómo lo hizo Firefox fue leyendo su código. Este es el tipo de cosas que alguien necesita documentar en un gráfico grande, enumerando cómo lo hace cada navegador ...
bta
3
Un autor del código del administrador de contraseñas lo guía a través de las cosas en una publicación de blog de 2013; vale la pena leerlo: blog.mozilla.org/dolske/2013/08/20/on-firefoxs-password-manager
dat
2
Enlace actualizado a las publicaciones del blog del administrador de contraseñas: dolske.wordpress.com/2013/08
Vsevolod Golovanov
17

Tuve el mismo problema y encontré una solución:

  1. Para que el navegador solicite almacenar la contraseña, las casillas de nombre de usuario y contraseña deben estar en un formulario y ese formulario debe ser enviado. El botón de envío podría devolver falso desde el controlador onclick (por lo que el envío no ocurre realmente).

  2. para que el navegador restaure la contraseña almacenada previamente, los cuadros de entrada deben existir en el formulario HTML principal y no se deben crear a través de javascript dinámicamente. El formulario se puede crear con display: none.

Es necesario tener en cuenta que la contraseña se completa inmediatamente después de que se carga la página y está presente allí durante toda la sesión, por lo que se puede leer mediante javascript inyectado: empeora estos ataques. Para evitar esto, es razonable reenviarlo a una página separada solo para iniciar sesión, y resuelve todos los problemas para los que comenzó a leer este tema :). Como una solución parcial, borro los campos al enviar el formulario; si el usuario cierra la sesión y quiere iniciar sesión nuevamente, el navegador no completa la contraseña, pero eso es menor para mí.

Viliam


usuario324356
fuente
Funciona en Firefox 3.5 e IE8, no funciona en Chrome (el punto 1 no funciona). Quizás el envío tiene que ser real ...
user324356
user324356: configuré el formulario en "#", luego llamé a myForm.submit () y eso funcionó en Firefox pero no en Chrome.
1,21 gigavatios
1
fyi: cuando usa return falseo event.preventDefault()esto no funcionará en Chrome debido a este error: code.google.com/p/chromium/issues/detail?id=282488
mkurz
Esto es definitivamente importante con el algoritmo actual utilizado por Firefox - no es lo único que "busca" el administrador de contraseñas, pero el administrador de contraseñas de Firefox no se activará sin un envío real.
dat
Actualización: Chrome 46 solucionó su comportamiento incorrecto; todo debería funcionar bien ahora. Ver stackoverflow.com/a/33113374/810109
mkurz
10

Debería consultar la página de depuración de Mozilla Password Manager y los documentos de nsILoginManager para los escritores de extensiones (solo para conocer los detalles técnicos esenciales de cómo Firefox maneja la administración de contraseñas). Puede profundizar en las respuestas allí y en otras páginas vinculadas allí para averiguar más de lo que probablemente haya querido saber cómo interactúa el administrador de contraseñas con sitios y extensiones.

(Específicamente, como se indica en el documento de depuración del administrador de contraseñas, asegúrese de que no tiene la función de autocompletar desactivada en su html, ya que eso suprimirá el mensaje para guardar el nombre de usuario y la contraseña)

Nick Bastin
fuente
7

Esto parece funcionar para Firefox, Chrome y Safari en Mac. No probado en Windows.

<form id="bridgeForm" action="#" target="loginframe" autocomplete="on">
    <input type="text" name="username" id="username" />
    <input type="password" name="password" id="password"/>
</form>

<iframe id="loginframe" name="loginframe" src="anyblankpage.html"></iframe>

Esto debe agregarse a la página. No se puede agregar de forma dinámica. El formulario y el iframe se pueden configurar para mostrar: ninguno. Si no configura el src del iframe, el mensaje no se mostrará hasta que haya enviado el formulario al menos una vez.

Luego llame al formulario submit ():

bridgeForm.submit();

La acción puede ser opcional y el autocompletado puede ser opcional. No lo he probado.

Nota : En algunos navegadores, el formulario debe ejecutarse en un servidor (no en el host local ni en el sistema de archivos) antes de que el navegador responda.

Así que esto:

http://www.mysite.com/myPage.html

no esta:

http://126.0.0.1/myPage.html
http://localhost/myPage.html
file://directory/myPage.html

1,21 gigavatios
fuente
@ErikAronesty, ¿se te ha ocurrido una solución? Noté que en algunos navegadores (safari) la página debe estar en un servidor y no en un host local o en el sistema de archivos.
1,21 gigavatios
1
Chrome 46 corrigió su comportamiento incorrecto, ya no se iframenecesitan soluciones. Ver stackoverflow.com/a/33113374/810109
mkurz
6

Me funciona con angular, chrome, firefox: (he buscado y probado durante horas; para Chrome, el parámetro de acción de formulario ( #) fue la respuesta. @ 1.21 gigavatios , ¡gracias! Tu respuesta fue invaluable).

formar

firefox 30.0: no necesita un iframe oculto y un botón de envío (como se muestra a continuación), pero necesita la directiva "login-form-autofill-fix" para reconocer las credenciales autocompletadas, de la siguiente manera:

<form name="loginForm" login-form-autofill-fix action="#" target="emptyPageForLogin" method="post" ng-submit="login({loginName:grpEmail,password:grpPassword})">
<input type="text" name=username" id="username" ng-model="grpEmail"/>
<input type="password" name="password" id="password" ng-model="grpPassword"/>
<button type="submit">Login</button>
</form>

iframe oculto

chrome 35.0: no necesita la directiva anterior, pero necesita un iframe oculto y un botón de envío en el formulario real. El iframe oculto parece

<iframe src="emptyPageForLogin.html" id="emptyPageForLogin" name="emptyPageForLogin" style="display:none"></iframe>

directiva angular (usando jqLite)

Esto funciona con angular 1.2.18

module.directive('loginFormAutofillFix', function() { 
            return function(scope, elem, attrs) {
        if(!attrs.ngSubmit) {
            return;
        }
        setTimeout(function() {
            elem.unbind("submit").bind("submit", function(e) {
                //DO NOT PREVENT!  e.preventDefault(); 
                elem.find("input").triggerHandler("input");
                scope.$apply(attrs.ngSubmit);
            });
        }, 0);
});

enmienda

  • Después de algunas pruebas, me di cuenta de que Chrome necesita un poco de tiempo de espera mediante el método de inicio de sesión angular (200 ms); parece que la redirección a veces es demasiado rápida para el administrador de contraseñas.
  • mejor búsqueda de búsqueda clara ... con cada cambio
110maor
fuente
dado que el Chrome más nuevo, pw-manager completa el formulario solo a veces. también aparece el administrador de pw a veces, a veces no ... parece ser una función aleatoria ahora.
110maor
el nuevo firefox ignora el campo de contraseña autocompletado. el triggerHandler ("input") no funciona. esto se puede resolver con un evento nativo (document.createEvent) - envíelo un incendio.
110maor
1
Chrome 46 corrigió su comportamiento incorrecto, ya no se iframenecesitan soluciones. Ver stackoverflow.com/a/33113374/810109
mkurz
@ 110maor Me tomó mucho tiempo entender tu publicación. Espero que mis ediciones sean fieles a tu intención.
jpaugh
3

Bueno, en nuestro sitio , un campo de formulario con el nombre "nombre de usuario", el tipo "texto" seguido inmediatamente por un campo con el nombre "contraseña" y el tipo "contraseña" parece funcionar.

gastador
fuente
¡Demasiado rápido para mí! Mis pensamientos exactamente ... (estoy eliminando mi respuesta)
Ganesh Shankar
Si. Lo intenté. Sin suerte. :-( Mi teoría es que tengo algo más en la página que al navegador no le gusta / hace pensar que la página no tiene un formulario de inicio de sesión ...
Eric
3

Si está utilizando el inicio de sesión AJAX, eche un vistazo a ese código fuente: https://gist.github.com/968927

Consiste en enviar un formulario de inicio de sesión a un iframe oculto, para que IE y Chrome puedan detectar el inicio de sesión real, sin tener que volver a cargar la página.

Adrien Joly
fuente
Chrome 46 corrigió su comportamiento incorrecto, ya no se iframenecesitan soluciones. Ver stackoverflow.com/a/33113374/810109
mkurz
3

Las heurísticas son bastante simples aquí: detecta campos con ciertos nombres en cierto orden. Cuáles, no puedo decir, pero esto funcionó bien para mí en Chrome e IE:

Username field: name "login", type "text";
Password field: name "password", type "password"
Submit button: element "input", type "submit". 
Element "button" type "submit" did not work.
user19878
fuente
Tuve que reemplazar <button type="submit">por <input type="submit">para que Dashlane funcionara. Aunque no puedo creer que esto sea siquiera una cosa ...
Bram Verstraten
3

También noté que Chrome no ofrecerá recordar una contraseña si el formulario de inicio de sesión todavía está presente después de la solicitud de inicio de sesión, incluso si está oculto en la página.

Supongo que cree que la acción de inicio de sesión falló y, por lo tanto, se niega a almacenar credenciales no válidas.

Visto en Chrome 34.0.

ZeWaren
fuente
1
Chrome 46 corrigió su comportamiento incorrecto, ocultar el formulario después de una solicitud ajax debería ser suficiente. Ver stackoverflow.com/a/33113374/810109
mkurz
0

Además de mucho de lo que ya se ha dicho, me di cuenta de que los campos de entrada no deben estar "deshabilitados". Tuvimos un inicio de sesión de varios pasos que primero solicita el nombre de usuario y luego, en la siguiente pantalla, la contraseña. En esa segunda pantalla repetimos el correo electrónico pero lo deshabilitamos y eso impidió que Chrome et. Alabama. de reconocer como un campo válido para el nombre de usuario.

Como realmente queríamos mantener ese campo de entrada deshabilitado, terminamos con esta solución hacky:

<input type="text" name="display-username" id="display-username" value="ENTERED_USERNAME" placeholder="Username" disabled="disabled">
<input type="text" name="username" id="username" value="ENTERED_USERNAME" placeholder="Username" style="display: none;">
<input type="password" name="password" id="password" value="" autofocus="autofocus" autocomplete="on" placeholder="Password" required="required">

No iría tan lejos para recomendar esto, pero tal vez le indique a alguien algo en su propio código:

  1. El primer elemento muestra el nombre de usuario en un campo de entrada deshabilitado. Disabled no se envía como parte del formulario y el navegador no lo reconoce.
  2. El segundo elemento es un campo de entrada adecuado, con type = "text" y name / id = "username". Esto está siendo reconocido por el navegador. Para evitar que el usuario lo edite, lo ocultamos con CSS (display: none).
Hendrikbeck
fuente
Para situaciones en las que desea una entrada cuyo valor se envía pero que el usuario no puede cambiar, es posible que desee probar "solo lectura" en lugar de "deshabilitado".
David Brown
0

Si tiene, por ejemplo, dos tipo = texto en su formulario antes de la entrada tipo = contraseña, el navegador detectará el tipo de entrada más cercano = texto para su nombre de usuario.

Esto no importa que otra entrada tenga es = nombre de usuario y nombre = nombre de usuario

para resolver este problema, debe poner la entrada de su nombre de usuario que desea guardar exactamente antes de su contraseña de entrada

Buena suerte :-)

keivan kashani
fuente
-2

La palabra clave principal está aquí,

   <input type="password">
Jeba Moisés
fuente
1
Intente explicar su respuesta con algunos detalles más, ya que será útil para otros.
Ashish Duklan