Hoy un amigo nos ha enviado un mensaje al grupo de la Promoción para ver que opinábamos al respecto de la siguiente imagen
y yo que la he visto, instantáneamente me he acordado del cálculo necesario para rellenar esta tabla.
Lo primero de todo se debe calcular la entropía. La entropía de una contraseña es calculo matemático que representa un indicador de como se encuentra esta contraseña sobre una población, si incluimos en nuestra contraseña mayúsculas, minúsculas, números y símbolos, la entropía de nuestra contraseña será mayor puesto que la población de caracteres usados es mucho mayor, en este caso, unos 94 (26 minúsculas, 26 mayúsculas, 10 números y 32 símbolos). La fórmula es la siguiente:
o lo que es lo mismo, es el logaritmo en base de 2 de la cantidad de elementos de la población por la longitud de la cadena. Si efectuamos el cálculo inverso, 2 elevado a la entropía debe ser igual a la población elevada al número de caracteres de nuestra contraseña, que esto serían el número máximo de operaciones necesarias para intentar reventar nuestra password por fuerza bruta.
Si nuestra contraseña usa solo las minúsculas, su «pool» sería de 26 y por tanto la contraseña tendría una entropía baja y fácil de descubrir.
Si hay alguien que quiera comprobar la fuerza de su contraseña online puede hacerlo desde Kaspersky
Bien, pero lo que a mi me importa es como sabemos, con código. En la clase le he incluido una penalización si nuestra contraseña se encuentra entre las más conocidas.
En C#
/* ************************************************************************************************************************************************ * © JOAQUIN MARTINEZ RUS 2020 * Archivo: PasswordEntropy.cs * Descripción: Clase que calcula la entropia y el tiempo en descifrar por fuerza bruta de de una contraseña * Historial: * 1. Joaquin Martínez Rus - 02 sep 2020. Creación * * Comentarios: * * ****************************************************************/ public class PasswordEntropy { public PasswordEntropy(string password) { Password = password; } public string Password { get; set; } public double Entropy => GetEntropy(); public string EntropyToString => GetTimeByEntropy(Entropy); private double GetEntropy() { //Pools double pool = 0; // Numbers pool += Password.Any(k => char.IsDigit(k)) ? 10 : 0; // Lower 26 pool += Password.Any(k => char.IsLower(k)) ? 26 : 0; // Upper 26 pool += Password.Any(k => char.IsUpper(k)) ? 26 : 0; // Simbols 30 pool += Password.Any(k => (char.IsSymbol(k) || char.IsPunctuation(k))) ? 32 : 0; if (pool == 0) return pool; // entropy = Lenght * log pool / log2 var p = Math.Log(pool, 2); var entropy = Password.Length * Math.Log(pool, 2); // el uso de una contraseña famosa, indica una falta de seguridad absoluta // por lo que no se incluye como deducción de entropía, sino que se incluye en el // mismo cálculo de la entropía if (IsFamous(Password)) { entropy = (int)Entropies.NoSecurity - 1; // Inexistencia de seguridad } return entropy; } private const double OPERATIONPERSECOND = 4*10E12; private bool IsFamous(string password) { var list = new List() { "1234", "2000", "12345", "111111", "123123", "123456", "654321", "696969", "1234567", "12345678", "abc123", "alejandra", "america", "baseball", "bonita", "daniel", "dragon", "estrella", "football", "harley", "iloveyou", "jennifer", "jesus", "jordan", "letmein", "mariposa", "master", "michael", "monkey", "mustang", "password", "pussy", "qwerty", "roberto", "sebastian", "shadow", "superman", "Tequiero" }; return list.Contains(password); } public enum Entropies { Blank = 0, NoSecurity =10, TooWeak=20, VeryWeak=28, Weak=35, Medium=55, Strong=75, Stronger=127, OverTheTopStrong = 190 } private string GetTimeByEntropy(double entropy) { if (entropy == 0) return string.Empty; string output = ""; double value = Math.Pow(2, entropy) / OPERATIONPERSECOND; double ms = 0; double seconds = value; double minutes = seconds / 60; double hours = minutes / 60; double days = hours / 24; double months = days / 30; double years = months / 12; double centuries = years / 100; double millennium = centuries / 10; seconds = value; if (value == 0) return ""; if (seconds > 0 && seconds 59 && hours > 24 && days 11 && years < 100 && centuries < 1000000) { output = "Tu contraseña puede ser descifrada en más de un millón de años!"; } else { millennium = Math.Round(millennium, 1); output = $"Tu contraseña puede ser descifrada en {millennium}, {(millennium == 1 ? "milenio" : "milenios")}"; } return output; } } static void Main(string[] args) { var password = "x#Pr0m0cio_N"; for (int i = 1; i <= password.Length; i++) { var pwd = new PasswordEntropy(password.Substring(0,i)); Console.WriteLine($"Password: {pwd.Password}\n{pwd.EntropyToString}"); Console.WriteLine("Press any key"); } Console.ReadKey(); }
el resultado iterando por la contraseña que he creado
Password: x
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#P
Tu contraseña puede ser descifrada en 0, milisegundos
Press any key
Password: x#Pr
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0m
Tu contraseña puede ser descifrada en 17 milisegundos
Press any key
Password: x#Pr0m0
Tu contraseña puede ser descifrada en 2 segundos
Press any key
Password: x#Pr0m0c
Tu contraseña puede ser descifrada en 3 minutos
Press any key
Password: x#Pr0m0ci
Tu contraseña puede ser descifrada en 4 horas
Press any key
Password: x#Pr0m0cio
Tu contraseña puede ser descifrada en 15,6 dias
Press any key
Password: x#Pr0m0cio_
Tu contraseña puede ser descifrada en 4,1 años
Press any key
Password: x#Pr0m0cio_N
Tu contraseña puede ser descifrada en 3,8 siglos
Press any key
y en Kotlin
class PasswordEntropy(val password: String) { init { } fun entropy(): Double { //Pools var pool = 0.0 // Numbers pool += if (password.any { c -> c.isDigit() }) 10.0 else 0.0 // Lower 26 pool += if (password.any { c -> c.isLowerCase() }) 26.0 else 0.0 // Upper 26 pool += if (password.any { c -> c.isUpperCase() }) 26.0 else 0.0 val (noSymbols, symbols) = password.partition { it.isLetter() || it.isDigit() } // Simbols 32 pool += if (symbols.count()>0) 32.0 else 0.0 if (pool == 0.0) return pool // entropy = Lenght * log pool / log2 var entropy = password.length * log2(pool) // el uso de una contraseña famosa, indica una falta de seguridad absoluta // por lo que no se incluye como deducción de entropía, sino que se incluye en el // mismo cálculo de la entropía if (isFamous()) { entropy = 9.0 // Inexistencia de seguridad } return entropy } private fun isFamous(): Boolean{ return listOf( "1234", "2000", "12345", "111111", "123123", "123456", "654321", "696969", "1234567", "12345678", "abc123", "alejandra", "america", "baseball", "bonita", "daniel", "dragon", "estrella", "football", "harley", "iloveyou", "jennifer", "jesus", "jordan", "letmein", "mariposa", "master", "michael", "monkey", "mustang", "password", "pussy", "qwerty", "roberto", "sebastian", "shadow", "superman", "Tequiero").contains(password) } private val OPERATION_PER_SECOND: Double = 4*10E12 fun entropyToString(): String { val entropy = entropy() if (entropy == 0.0) return "" val value = 2.0.pow(entropy) / OPERATION_PER_SECOND var ms = 0 var seconds = value val minutes = seconds / 60.0 val hours = minutes / 60.0 val days = hours / 24.0 val months = days / 30.0 val years = months / 12.0 val centuries = years / 100.0 val millennium = centuries / 10.0 seconds = value if (value == 0.0) return "" return "Contraseña: $password\n${if (ifa) "Es una contraseña conocida!\n" else ""}${when { ms in 1..999 ->"Tu contraseña puede ser descifrada inmediatamente, yo no lo llamaría contraseña" seconds > 0 && seconds "Tu contraseña puede ser descifrada en ${seconds.toInt()} ${if (seconds.toInt() == 1) "segundo" else "segundos"}" seconds > 59 && minutes "Tu contraseña puede ser descifrada en ${minutes.toInt()} ${if (minutes.toInt() == 1) "minuto" else "minutos"}" minutes > 59 && hours "Tu contraseña puede ser descifrada en ${hours.toInt()} ${if (hours.toInt() == 1) "hora" else "horas"}" hours > 24 && days "Tu contraseña puede ser descifrada en ${days.toInt()} ${if (days.toInt() == 1) "dia" else "dias"}" days > 30 && months "Tu contraseña puede ser descifrada en ${months.toInt()} ${if (months.toInt() == 1) "mes" else "meses"}" months > 11 && years "Tu contraseña puede ser descifrada en ${years.toInt()} ${if (years.toInt() == 1) "año" else "años"}" years > 99 && centuries "Tu contraseña puede ser descifrada en ${"%.1f".format(centuries)} ${if (centuries == 1.0) "siglo" else "siglos"}" millennium > 1000000 -> "Tu contraseña puede ser descifrada en más de un millón de años!" else -> "Tu contraseña puede ser descifrada en ${millennium.toInt()} ${if (millennium.toInt() == 1) "milenio" else "milenios"}"}}" } fun main(){ val password = "x#Pr0m0cio_N"; for (i in (1..password.length)) { val pwd = PasswordEntropy(password.substring(0,i)) println("Password: ${pwd.password}\n${pwd.entropyToString()}\n") } }
el resultado para Kotlin
Password: x
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#P
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0m
Tu contraseña puede ser descifrada en 17 milisegundos
Press any key
Password: x#Pr0m0
Tu contraseña puede ser descifrada en 2 segundos
Press any key
Password: x#Pr0m0c
Tu contraseña puede ser descifrada en 3 minutos
Press any key
Password: x#Pr0m0ci
Tu contraseña puede ser descifrada en 4 horas
Press any key
Password: x#Pr0m0cio
Tu contraseña puede ser descifrada en 15,6 dias
Press any key
Password: x#Pr0m0cio_
Tu contraseña puede ser descifrada en 4,1 años
Press any key
Password: x#Pr0m0cio_N
Tu contraseña puede ser descifrada en 3,8 siglos
Press any key
El archivo jar podéis descargarlo desde aquí
Archivo ZIP
SHA256 9314879A535675E813EAC13CEC9B056CAADC4C080E5E033E239E2D88A60F55E3
Archivo jar
SHA256 CA942FB971E34CB775E14F12C5B25E5E456044F02DD54447F5690FA05469C3C8
Saludos a la X Promoción del IPE 2!!!
Esta obra está bajo una Licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional