Construya un Control de Seguridad de Contraseñas Simple
() translation by (you can also view the original English article)
Proporcionar retroalimentación instantánea es lo que está de moda en el momento. ¿Por qué limitarse a verificar nombres de usuario y direcciones de correo electrónico? ¿Por qué no ampliar esto para proporcionar comentarios visuales rápidos acerca de la fortaleza de la contraseña que el usuario ha ingresado? Hoy, veremos cómo crear un control simple de seguridad de contraseñas usando la biblioteca jQuery, expresiones regulares y un algoritmo simple.
Una Palabra del Autor
Como la mayoría de los expertos en seguridad le dirán, el usuario siempre es el enlace más débil. Los sistemas más seguros son vulnerables cuando un usuario elige una contraseña desacertada. Teniendo esto en cuenta, la tendencia reciente parece ser proporcionar retroalimentación rápida al usuario sobre la fortaleza de la contraseña para que el usuario pueda extender o modificar la contraseña para hacerla más segura.



Hoy, vamos a usar la biblioteca jQuery, un grupo de expresiones regulares y un algoritmo muy simple para crear un control de seguridad de contraseña básico. ¿Interesado? ¡Comencemos de inmediato! Aquí hay una demostración de lo que estamos tratando de construir hoy:

Objetivos de Diseño
Nuestros objetivos de diseño para esta funcionalidad específica son relativamente pequeños.
- Proporcionar retroalimentación visual al usuario sobre la fortaleza de su contraseña.
- La respuesta debe ser instantánea. Esto significa no hacer clic en un botón para probar la seguridad.
- El evento desencadenante puede ser cualquiera de los eventos del teclado. Elegí keyup porque es el más apropiado para nuestra necesidad específica.
- Para la retroalimentación visual, la modificación del texto solo, aunque útil, es muy escasa. Elegí cambiar los colores de fondo para llamar la atención del usuario sobre esto.
- Proporcionar comentarios cuantificables adicionales para que el usuario sepa en qué departamentos la contraseña carece de fuerza y cómo se puede mejorar.
Ahora que hemos resuelto adecuadamente nuestras necesidades, podemos pasar a la siguiente etapa.
Plan de Acción
Ahora decidiremos el orden de los pasos individuales que deben realizarse.
- Conecte el controlador de eventos al evento keyup del cuadro de entrada.
- Deje que el controlador de eventos verifique la entrada, pero delegue todo lo demás en métodos de ayuda individuales.
- Los métodos de ayuda deberían encargarse de analizar la entrada y analizarla, calcular la complejidad e imprimir los resultados.
- Asegúrese de que el controlador de eventos desactive los métodos de ayuda solo si la longitud de la entrada es mayor que el mínimo esperado para no perder ciclos de CPU en entradas no válidas.
- Regrese el control al controlador de eventos en caso de que sea necesario hacer algo más.
El Algoritmo



Con el interés de mantener este escrito breve y accesible, he decidido usar un algoritmo muy básico. El algoritmo analiza la cadena de texto, otorga bonificaciones por longitud adicional, presencia de números, símbolos y letras mayúsculas y penalizaciones por entradas de solo letras o números. No vamos a ver los patrones comunes que coinciden o a verificar la entrada con un diccionario, ya que esto está fuera del alcance del artículo. Si el interés alcanza su punto máximo, puedo hacer un artículo sobre esto en el futuro.
Primero, verificamos la longitud de la cadena de entrada. Si es mayor que la longitud mínima, déle una puntuación base de 50. De lo contrario, conviértalo en 0. Luego, repita cada carácter de la cadena de texto y verifique si se trata de un símbolo, un número o una letra mayúscula. Si es así, tome nota de ello.
Luego, verifique cuántos caracteres adicionales tiene la cadena, por encima del mínimo recomendado y otorgue una bonificación por cada caracter. También otorgue una bonificación si la cadena contiene una combinación de letras mayúsculas, números y símbolos o los tres. Concede una bonificación por la presencia de cada uno también.
Compruebe si la cadena solo contiene letras minúsculas o números y, de ser así, penalice.
Sume todos los números y decida la fuerza de la contraseña en consecuencia.
Ese es el largo y corto del algoritmo. No está yendo excesivamente suave, pero atrapa muchas contraseñas malas. Lo entenderá mejor una vez que lo veamos en el código.
Markup Principal
El marcado HTML de la página de demostración se ve así:
1 |
|
2 |
<!DOCTYPE html>
|
3 |
<html lang="en-GB"> |
4 |
<head>
|
5 |
<title>Simple Password Strength Checker - by Siddharth for NetTuts</title> |
6 |
<link type="text/css" href="css/style.css" rel="stylesheet" /> |
7 |
<script type="text/javascript" src="js/jquery.js"></script> |
8 |
<script type="text/javascript" src="js/mocha.js"></script> |
9 |
</head>
|
10 |
<body>
|
11 |
|
12 |
<div id="container"> |
13 |
|
14 |
<h1>Create a simple password strength checker</h1> |
15 |
|
16 |
<h2 class="bolded">by Siddharth for the lovely folks at Net Tuts</h2> |
17 |
|
18 |
<p>Type in your password to get visual feedback regarding the strength of your password.</p> |
19 |
<p>I assure you, I am not stealing your passwords. The form doesn't not submit. You can look through the source if you are suspicious. :)</p> |
20 |
|
21 |
<div class="block"> |
22 |
<input id="inputPassword"/> |
23 |
<div id="complexity" class="default">Enter a random value</div> |
24 |
</div>
|
25 |
|
26 |
<div class="block"> |
27 |
<div id="results" class="default">Breakdown of points</div> |
28 |
<div id="details"></div> |
29 |
</div>
|
30 |
|
31 |
</div>
|
32 |
</body>
|
33 |
</html>
|
Haga caso omiso del marcado habitual. Observe el elemento input con una ID de inputPassword, el elemento div con una ID de complexity que muestra la complejidad de la contraseña y el elemento div con una ID de details que muestra el desglose de puntos.
También hemos incluido la biblioteca jQuery y nuestro propio archivo de script. Puntos extra si aprecia el nombre de nuestro archivo de script.
Estilo CSS
1 |
|
2 |
body{ |
3 |
font-family: "Lucida Grande", "Verdana", sans-serif; |
4 |
}
|
5 |
|
6 |
h1{ |
7 |
font-size: 30px; |
8 |
padding: 0; |
9 |
margin: 0; |
10 |
}
|
11 |
|
12 |
h2{ |
13 |
font-size: 18px; |
14 |
padding: 0; |
15 |
margin: 0 5px 30px 0; |
16 |
}
|
17 |
|
18 |
input{ |
19 |
width: 288px; |
20 |
height: 30px; |
21 |
margin: 50px 0 0 0; |
22 |
padding: 3px 5px; |
23 |
font-size: 22px; |
24 |
font-family: "Lucida Grande", "Verdana", sans-serif; |
25 |
}
|
26 |
|
27 |
#container{ |
28 |
width: 820px; |
29 |
margin-left: auto; |
30 |
margin-right: auto; |
31 |
padding: 50px 0 0 0; |
32 |
}
|
33 |
|
34 |
.block{ |
35 |
width: 300px; |
36 |
margin: 0 auto 0 auto; |
37 |
}
|
38 |
|
39 |
#complexity, #results{ |
40 |
width: 300px; |
41 |
padding: 3px 0; |
42 |
height: 20px; |
43 |
color: #000; |
44 |
font-size: 14px; |
45 |
text-align: center; |
46 |
}
|
47 |
|
48 |
#results{ |
49 |
margin: 30px 0 20px 0; |
50 |
}
|
51 |
|
52 |
.default{background-color: #CCC;} |
53 |
.weak{background-color: #FF5353;} |
54 |
.strong{background-color: #FAD054;} |
55 |
.stronger{background-color: #93C9F4; } |
56 |
.strongest{background-color: #B6FF6C;} |
57 |
|
58 |
span.value{ |
59 |
font-weight:bold; |
60 |
float: right; |
61 |
}
|
Solo CSS base para disposición y tipografía. Tenemos un montón de clases en la parte inferior para cada calificación de fuerza individual. Los agregaremos a los elementos cuando sea necesario.
Implementación de JavaScript
Ahora que tenemos un marco sólido y un estilo básico en su lugar, podemos comenzar a codificar la funcionalidad requerida. Tenga en cuenta que hacemos un uso extenso de jQuery. Siéntase libre de enlazar al CDN de Google si es necesario.
Variables y Manejo de Eventos
Dado que se va a hacer malabares con una gran cantidad de números, necesitamos un conjunto de variables para contener los valores. Ya que es una demostración y no un código de producción, decidí declarar las variables como globales y acceder a ellas a través de los métodos de ayuda en lugar de declararlas internamente y luego pasarlas a las funciones.
1 |
|
2 |
var strPassword; |
3 |
var charPassword; |
4 |
var complexity = $("#complexity"); |
5 |
var minPasswordLength = 8; |
6 |
var baseScore = 0, score = 0; |
7 |
|
8 |
var num = {}; |
9 |
num.Excess = 0; |
10 |
num.Upper = 0; |
11 |
num.Numbers = 0; |
12 |
num.Symbols = 0; |
13 |
|
14 |
var bonus = {}; |
15 |
bonus.Excess = 3; |
16 |
bonus.Upper = 4; |
17 |
bonus.Numbers = 5; |
18 |
bonus.Symbols = 5; |
19 |
bonus.Combo = 0; |
20 |
bonus.FlatLower = 0; |
21 |
bonus.FlatNumber = 0; |
Los nombres de las variables son bastante estándar pero de todos modos daré una explicación. strPassword contiene el valor del cuadro de entrada, charPassword es un array que contiene cada carácter de la cadena, complexity contiene una referencia al elemento div. También definimos la longitud mínima de la contraseña, el puntaje y el puntaje base.
Creamos un hash rápido para contener el número de caracteres adicionales, mayúsculas, números y símbolos. Hacemos lo mismo por las bonificaciones. El num hash contiene el número de caracteres, mientras que el bonus hash contiene los multiplicadores de bonificación. Usted puede simplemente crear variables individuales, pero creo que esto se ve más limpio.
No olvide conectar el controlador de eventos al evento.
1 |
|
2 |
$("#inputPassword").bind("keyup", checkVal); |
checkVal es el controlador de eventos que crearemos en solo un momento.
El Controlador de Eventos
1 |
|
2 |
function checkVal() |
3 |
{
|
4 |
if (charPassword.length >= minPasswordLength) |
5 |
{
|
6 |
baseScore = 50; |
7 |
analyzeString(); |
8 |
calcComplexity(); |
9 |
}
|
10 |
else
|
11 |
{
|
12 |
baseScore = 0; |
13 |
}
|
14 |
|
15 |
outputResult(); |
16 |
}
|
Primero verificamos la longitud de la cadena de entrada. Si es mayor o igual a la longitud mínima especificada, podemos proceder. Establecemos el puntaje base en 50 y llamamos a los métodos de ayuda que se encargan de analizar la cadena y calcular su complejidad.
Si es menor que la longitud esperada, simplemente establecemos el puntaje base a 0.
Luego llamamos a la función outputResult que se encarga de dar sentido a los cálculos computados. Veremos cómo funciona más adelante.
Analizando la Entrada
1 |
|
2 |
function analyzeString () |
3 |
{
|
4 |
for (i=0; i<charPassword.length;i++) |
5 |
{
|
6 |
if (charPassword[i].match(/[A-Z]/g)) {num.Upper++;} |
7 |
if (charPassword[i].match(/[0-9]/g)) {num.Numbers++;} |
8 |
if (charPassword[i].match(/(.*[!,@,#,$,%,^,&,*,?,_,~])/)) {num.Symbols++;} |
9 |
}
|
10 |
|
11 |
num.Excess = charPassword.length - minPasswordLength; |
12 |
|
13 |
if (num.Upper && num.Numbers && num.Symbols) |
14 |
{
|
15 |
bonus.Combo = 25; |
16 |
}
|
17 |
|
18 |
else if ((num.Upper && num.Numbers) || (num.Upper && num.Symbols) || (num.Numbers && num.Symbols)) |
19 |
{
|
20 |
bonus.Combo = 15; |
21 |
}
|
22 |
|
23 |
if (strPassword.match(/^[\sa-z]+$/)) |
24 |
{
|
25 |
bonus.FlatLower = -15; |
26 |
}
|
27 |
|
28 |
if (strPassword.match(/^[\s0-9]+$/)) |
29 |
{
|
30 |
bonus.FlatNumber = -35; |
31 |
}
|
32 |
}
|
Esto puede parecer un poco complicado, pero se lo prometo, es solo por las expresiones regulares. Repasemos el código parte por parte.
Primero, necesitamos descubrir la composición de la cadena en cuestión. De igual manera, tenemos que averiguar si la cadena contiene letras mayúsculas, números o símbolos y de ser así, cuántos de ellos están presentes. Con esto en mente, iteramos a través del array de caracteres y verificamos cada carácter para ver su tipo. El método match nos permite hacer coincidir una cadena con una expresión regular. Si usted es nuevo en expresiones regulares, le sugiero que lea el gran artículo de Vasili aquí.
A continuación, determinamos la diferencia entre la longitud de la cadena de entrada y la longitud mínima especificada de la contraseña. Esto nos da el exceso de caracteres para jugar.
Luego verificamos si la cadena tiene mayúsculas, números y símbolos. Si es así, otorga una bonificación. También verificamos si tiene combinaciones de dos de ellas y otorgamos una bonificación más pequeña si es así.
Finalmente, verificamos si una cadena es plana: si contiene solo letras minúsculas o solo números. Comprobamos esto con una expresión regular y, de ser así, penalizamos la contraseña para esta práctica.
Calcule la Complejidad
1 |
|
2 |
function calcComplexity() |
3 |
{
|
4 |
score = baseScore + (num.Excess*bonus.Excess) + (num.Upper*bonus.Upper) + (num.Numbers*bonus.Numbers) + |
5 |
(num.Symbols*bonus.Symbols) + bonus.Combo + bonus.FlatLower + bonus.FlatNumber; |
6 |
}
|
Solo una simple adición. Agregamos el puntaje base al producto de la cantidad de caracteres sobrantes y su multiplicador. Lo mismo para letras mayúsculas, números y símbolos. Luego agregamos una bonificación para las combinaciones, si está presente, y agregamos penalizaciones si la cadena es plana.
Actualizando la Interfaz de Usuario
Ahora que todo el cálculo está detrás de nosotros, podemos actualizar la interfaz de usuario para reflejar los cambios. Aquí está cada uno de los estados.




1 |
|
2 |
function outputResult() |
3 |
{
|
4 |
if ($("#inputPassword").val()== "") |
5 |
{
|
6 |
complexity.html("Enter a random value").addClass("default"); |
7 |
}
|
8 |
else if (charPassword.length < minPasswordLength) |
9 |
{
|
10 |
complexity.html("At least " + minPasswordLength+ " characters please!").addClass("weak"); |
11 |
}
|
12 |
else if (score<50) |
13 |
{
|
14 |
complexity.html("Weak!").addClass("weak"); |
15 |
}
|
16 |
else if (score>=50 && score<75) |
17 |
{
|
18 |
complexity.html("Average!").addClass("strong"); |
19 |
}
|
20 |
else if (score>=75 && score<100) |
21 |
{
|
22 |
complexity.html("Strong!").addClass("stronger"); |
23 |
}
|
24 |
else if (score>=100) |
25 |
{
|
26 |
complexity.html("Secure!").addClass("strongest"); |
27 |
}
|
28 |
}
|
No hay nada lujoso aquí, pero lo revisaremos línea por línea.
Primero verificamos si la entrada está vacía. De ser así, cambiamos el texto del resultado y agregamos una clase default para cambiar el color de fondo a su color original.
Si es menor que la longitud mínima especificada, cambiamos el texto en consecuencia y agregamos una clase weak para que su fondo sea rojo. Hacemos lo mismo si el puntaje total es menor a 50 pero cambia el texto a weak.
A medida que aumenta la puntuación, cambiamos el texto de acuerdo a eso y agregamos las clases necesarias. Siéntase libre de cambiar los puntajes de referencia para cada calificación. Acabo de poner valores no científicos para hacer funcionar la demostración.
Actualización del Desglose Detallado

Con el resultado principal actualizado, podemos ver la actualización de las estadísticas ahora.
1 |
|
2 |
function outputResult() |
3 |
{
|
4 |
// Previous Code
|
5 |
|
6 |
$("#details").html("Base Score :<span class=\"value\">" + baseScore + "</span>" |
7 |
+ "<br />Length Bonus :<span class=\"value\">" + (num.Excess*bonus.Excess) + " ["+num.Excess+"x"+bonus.Excess+"]</span> " |
8 |
+ "<br />Upper case bonus :<span class=\"value\">" + (num.Upper*bonus.Upper) + " ["+num.Upper+"x"+bonus.Upper+"]</span> " |
9 |
+ "<br />Number Bonus :<span class=\"value\"> " + (num.Numbers*bonus.Numbers) + " ["+num.Numbers+"x"+bonus.Numbers+"]</span>" |
10 |
+ "<br />Symbol Bonus :<span class=\"value\"> " + (num.Symbols*bonus.Symbols) + " ["+num.Symbols+"x"+bonus.Symbols+"]</span>" |
11 |
+ "<br />Combination Bonus :<span class=\"value\"> " + bonus.Combo + "</span>" |
12 |
+ "<br />Lower case only penalty :<span class=\"value\"> " + bonus.FlatLower + "</span>" |
13 |
+ "<br />Numbers only penalty :<span class=\"value\"> " + bonus.FlatNumber + "</span>" |
14 |
+ "<br />Total Score:<span class=\"value\"> " + score + "</span>" |
15 |
}
|
Esta parte no es tan confusa como parece. Dejame explicar.
En lugar de simplemente actualizar los valores individuales para los resultados detallados, he recurrido a simplemente actualizar el valor HTML completo del contenedor. Sé que va a ser lento cuando varios de estos cuadros se sumen, pero acceder a cada elemento individualmente y luego actualizar su valor para una pequeña demostración parece ser bastante contraproducente. Así que corra conmigo aquí.
Esto es como inyectar HTML en un elemento, excepto que hemos colocado un par de variables para permitir que los detalles se actualicen instantáneamente. Cada valor obtiene una clase value para que sea negrita. También mostramos la cantidad de caracteres especiales y su multiplicador para que el usuario pueda medir qué elementos pesan más.
Algunos Ajustes
En este punto del tiempo, hay 2 errores que aparecen.
- Si escribe una contraseña larga y luego borra el cuadro de texto, los colores de fondo no cambiarán.
- En el mismo escenario, los detalles de los puntos desglosados no se actualizan como deberían.
Los abordaremos uno por uno.
Para el primer error, la causa raíz es el hecho de que no eliminamos todas las otras clases. Esto no sería un problema si las clases agregadas más recientemente tienen prioridad sobre otras. Lamentablemente, no es así. Aquí hay una solución rápida.
1 |
|
2 |
function outputResult() |
3 |
{
|
4 |
if ($("#inputPassword").val()== "") |
5 |
{ complexity.html("Enter a random value").removeClass("weak strong stronger strongest").addClass("default");} |
6 |
else if (charPassword.length < minPasswordLength) |
7 |
{complexity.html("At least " + minPasswordLength+ " characters please!").removeClass("strong stronger strongest").addClass("weak");} |
8 |
else if (score<50) |
9 |
{complexity.html("Weak!").removeClass("strong stronger strongest").addClass("weak");} |
10 |
else if (score>=50 && score<75) |
11 |
{complexity.html("Average!").removeClass("stronger strongest").addClass("strong");} |
12 |
else if (score>=75 && score<100) |
13 |
{complexity.html("Strong!").removeClass("strongest").addClass("stronger");} |
14 |
else if (score>=100) |
15 |
{complexity.html("Secure!").addClass("strongest");} |
16 |
|
17 |
// Details updating code
|
18 |
}
|
Probablemente está preguntando por qué no eliminamos todas y cada una de las clases aquí. La respuesta es simple: aprovechamos uno de los principales atributos de CSS: la cascada. Si nota el orden de declaración de cada clase en el archivo CSS, notará que el valor predeterminado ocurre primero y el más fuerte es el último, lo que significa que si un elemento tiene la clase más fuerte anulará cualquier modificación realizada por cualquier clase por encima de ella. Entonces, solo tendremos que eliminar las clases que ocurren debajo de la clase relevante. Por ejemplo, para que un elemento tenga strong, tendremos que eliminar las clases stronger y strongest.
La razón por la que existe la segunda falla se debe al hecho de que las variables individuales no se restablecen cuando se produce un nuevo evento. También se llevan al próximo evento. Para solucionar esto, creamos una función rápida que reinicializa todas las variables relevantes y le agrega al controlador de eventos checkVal, por lo que se llama cada vez que se actualiza el texto del cuadro de entrada.
1 |
|
2 |
function init() |
3 |
{
|
4 |
strPassword= $("#inputPassword").val(); |
5 |
charPassword = strPassword.split(""); |
6 |
|
7 |
num.Excess = 0; |
8 |
num.Upper = 0; |
9 |
num.Numbers = 0; |
10 |
num.Symbols = 0; |
11 |
bonus.Combo = 0; |
12 |
bonus.FlatLower = 0; |
13 |
bonus.FlatNumber = 0; |
14 |
baseScore = 0; |
15 |
score =0; |
16 |
}
|
1 |
|
2 |
function checkVal() |
3 |
{
|
4 |
init(); |
5 |
|
6 |
// Other code
|
7 |
}
|
Limitaciones

Si has estado jugando un poco con la demo, notará que Pa$$W0rd$ aparece como una contraseña segura, mientras que de hecho se romperá muy pronto. Esto se debe a la simplicidad de nuestro algoritmo aquí. No verificamos los reemplazos de caracteres. O contraseñas o patrones comunes para ese asunto. Hacer tales cosas aumentaría la dificultad de este tutorial a la vez que reduciría su accesibilidad, y ambas cosas no las quería para este escrito en particular.
Esto está pensado como un control de seguridad de contraseña básico. Si necesita reforzarlo, probablemente podría agregar un par de expresiones regulares más para verificar los patrones y la repetición de caracteres y luego ajustar los resultados de acuerdo a eso.
Mirar la entrada en contra de un diccionario está realmente por fuera del alcance de este artículo y requeriría un enorme diccionario descargado al lado del cliente o conectarlo a un sistema del lado del servidor para hacer eso. De nuevo, realmente quería evitarlos a los dos esta vez.
Conclusión
Y ahí lo tiene: cómo agregar una funcionalidad amigable para el usuario, la capacidad de decirle al usuario la fortaleza de la contraseña que acaba de ingresar, para sus proyectos. Espero que haya encontrado este tutorial interesante y que le haya sido útil. Siéntase libre de reutilizar este código en cualquier lugar de sus proyectos y toque aquí el timbre si tiene dificultades.
¿Preguntas? ¿Comentarios positivos? ¿Criticas? Vaya a la sección de comentarios y déjeme un comentario. ¡Feliz codificación!
- Síganos en Twitter o suscríbase al feed RSS de Nettuts+ para obtener los mejores tutoriales de desarrollo web en la web.