how twitter detects password strength & weak password list
I was on the fence of whether to sign up a twitter account, one message caught my eyes during sign up: Too obvious, when I typed 1234567 in the password field. It made me wonder how twitter detects password strength.
Below are (stub) its client-side (in javascript) implementation detecting password strength,
In short, its password policies are:
- minimal length 6
- your typed password (after concerted to lower case) is not in their weak password list
- a simple, custom function (function D) to test your password strength, that looks up for a list of meta characters like #,$,%,^,&,*,?,_,~,
The most important interesting part is it pre-fetches a list of weak password, I am not sure where it is from, probably base upon their analysis of failed attempts. You can find this list the bottom of this page.
function I(K) {
var M = 0;
var L = B.minlength ? B.minlength : 6;
<!--
// If less than 6 characters, display too short -->
if (K.length < L) {
return {
score: K.length,
message: _("Too short"),
className: "password-invalid"
}
}
<!--
// convert password into lower case, and marks as invalid if it equals username -->
if (B.username) {
var N = (typeof(B.username) == "function") ? B.username() : B.username;
if (N && (K.toLowerCase() == N.toLowerCase())) {
return {
score: 0,
message: _("Too obvious"),
className: "password-invalid"
}
}
}
<!--
// Checks against its list of BANNED_PASSWORDS and marks input password as invalid if it matches any of the banned password -->
if ($.inArray(K.toLowerCase(), twttr.BANNED_PASSWORDS) != -1) {
return {
score: 0,
message: _("Too obvious"),
className: "password-invalid"
}
}
<!-- //enlarges length to 4 times larger, stick into a variable -->
M += K.length * 4;
<!-- //Removes duplicated chars -->
M += (D(1, K).length - K.length) * 1;
M += (D(2, K).length - K.length) * 1;
M += (D(3, K).length - K.length) * 1;
M += (D(4, K).length - K.length) * 1;
<!-- //start to detect password strength-->
<!-- number: increases 5 to the password strength -->
if (K.match(/(.*[0-9].*[0-9].*[0-9])/)) {
M += 5
}
<!-- //meta char: increases 5 to the password strength -->
if (K.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) {
M += 5
}
<!-- //lower, upper char: increases 5 to the password strength -->
if (K.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)) {
M += 10
}
if (K.match(/([a-zA-Z])/) && K.match(/([0-9])/)) {
M += 15
}
<!-- //Both meta char and number, add 15 -->
if (K.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && K.match(/([0-9])/)) {
M += 15
}
<!-- //meta char & alpha, add 15 -->
if (K.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && K.match(/([a-zA-Z])/)) {
M += 15
}
<!-- //alpha or digits only, minus 10 -->
if (K.match(/^\w+$/) || K.match(/^\d+$/)) {
M -= 10
}
<!-- //reset to 0 if strength less than 0 -->
if (M < 0) {
M = 0
}
<!-- //reset to 100 if strength great than 0 -->
if (M > 100) {
M = 100
}
<!-- //Assign different messages according to the strength level -->
if (M < 34) {
return {
score: M,
message: _("Weak"),
className: "password-weak"
}
}
if (M < 50) {
return {
score: M,
message: _("Good"),
className: "password-good"
}
}
if (M < 75) {
return {
score: M,
message: _("Strong"),
className: "password-strong"
}
}
return {
score: M,
message: _("Very Strong"),
className: "password-verystrong"
}
}
function D(L, O) {
var K = "";
for (var N = 0; N < O.length; N++) {
var P = true;
for (var M = 0; M < L && (M + N + L) < O.length; M++) {
P = P && (O.charAt(M + N) == O.charAt(M + N + L))
}
if (M < L) {
P = false
}
if (P) {
N += L - 1;
P = false
} else {
K += O.charAt(N)
}
}
return K
}
Here is its list of weak password, looks fairly interesting! From the development and performance perspective, it does't make any sense to that pre-loading this password list rather than trying similar approach like checking username availability, that makes an ajax call to the server. Anyway, I think it serve as a good list of weak passwords to security guys to add in their arsenal.
twttr.BANNED_PASSWORDS = ["111111", "11111111", "112233", "121212", "123123", "123456", "1234567", "12345678", "131313", "232323", "654321", "666666", "696969", "777777", "7777777", "8675309", "987654", "aaaaaa", "abc123", "abc123", "abcdef", "abgrtyu", "access", "access14", "action", "albert", "alexis", "amanda", "amateur", "andrea", "andrew", "angela", "angels", "animal", "anthony", "apollo", "apples", "arsenal", "arthur", "asdfgh", "asdfgh", "ashley", "asshole", "august", "austin", "badboy", "bailey", "banana", "barney", "baseball", "batman", "beaver", "beavis", "bigcock", "bigdaddy", "bigdick", "bigdog", "bigtits", "birdie", "bitches", "biteme", "blazer", "blonde", "blondes", "blowjob", "blowme", "bond007", "bonnie", "booboo", "booger", "boomer", "boston", "brandon", "brandy", "braves", "brazil", "bronco", "broncos", "bulldog", "buster", "butter", "butthead", "calvin", "camaro", "cameron", "canada", "captain", "carlos", "carter", "casper", "charles", "charlie", "cheese", "chelsea", "chester", "chicago", "chicken", "cocacola", "coffee", "college", "compaq", "computer", "cookie", "cooper", "corvette", "cowboy", "cowboys", "crystal", "cumming", "cumshot", "dakota", "dallas", "daniel", "danielle", "debbie", "dennis", "diablo", "diamond", "doctor", "doggie", "dolphin", "dolphins", "donald", "dragon", "dreams", "driver", "eagle1", "eagles", "edward", "einstein", "erotic", "extreme", "falcon", "fender", "ferrari", "firebird", "fishing", "florida", "flower", "flyers", "football", "forever", "freddy", "freedom", "fucked", "fucker", "fucking", "fuckme", "fuckyou", "gandalf", "gateway", "gators", "gemini", "george", "giants", "ginger", "golden", "golfer", "gordon", "gregory", "guitar", "gunner", "hammer", "hannah", "hardcore", "harley", "heather", "helpme", "hentai", "hockey", "hooters", "horney", "hotdog", "hunter", "hunting", "iceman", "iloveyou", "internet", "iwantu", "jackie", "jackson", "jaguar", "jasmine", "jasper", "jennifer", "jeremy", "jessica", "johnny", "johnson", "jordan", "joseph", "joshua", "junior", "justin", "killer", "knight", "ladies", "lakers", "lauren", "leather", "legend", "letmein", "letmein", "little", "london", "lovers", "maddog", "madison", "maggie", "magnum", "marine", "marlboro", "martin", "marvin", "master", "matrix", "matthew", "maverick", "maxwell", "melissa", "member", "mercedes", "merlin", "michael", "michelle", "mickey", "midnight", "miller", "mistress", "monica", "monkey", "monkey", "monster", "morgan", "mother", "mountain", "muffin", "murphy", "mustang", "naked", "nascar", "nathan", "naughty", "ncc1701", "newyork", "nicholas", "nicole", "nipple", "nipples", "oliver", "orange", "packers", "panther", "panties", "parker", "password", "password", "password1", "password12", "password123", "patrick", "peaches", "peanut", "pepper", "phantom", "phoenix", "player", "please", "pookie", "porsche", "prince", "princess", "private", "purple", "pussies", "qazwsx", "qwerty", "qwertyui", "rabbit", "rachel", "racing", "raiders", "rainbow", "ranger", "rangers", "rebecca", "redskins", "redsox", "redwings", "richard", "robert", "rocket", "rosebud", "runner", "rush2112", "russia", "samantha", "sammy", "samson", "sandra", "saturn", "scooby", "scooter", "scorpio", "scorpion", "secret", "sexsex", "shadow", "shannon", "shaved", "sierra", "silver", "skippy", "slayer", "smokey", "snoopy", "soccer", "sophie", "spanky", "sparky", "spider", "squirt", "srinivas", "startrek", "starwars", "steelers", "steven", "sticky", "stupid", "success", "suckit", "summer", "sunshine", "superman", "surfer", "swimming", "sydney", "taylor", "tennis", "teresa", "tester", "testing", "theman", "thomas", "thunder", "thx1138", "tiffany", "tigers", "tigger", "tomcat", "topgun", "toyota", "travis", "trouble", "trustno1", "tucker", "turtle", "twitter", "united", "vagina", "victor", "victoria", "viking", "voodoo", "voyager", "walter", "warrior", "welcome", "whatever", "william", "willie", "wilson", "winner", "winston", "winter", "wizard", "xavier", "xxxxxx", "xxxxxxxx", "yamaha", "yankee", "yankees", "yellow", "zxcvbn", "zxcvbnm", "zzzzzz"];
I also noticed there are some duplications in this list, which I don't have a clue of why. Let's see
<!-- From http://www.martienus.com/code/javascript-remove-duplicates-from-array.html --> function unique(a) { var r = new Array(); o: for (var i = 0, n = a.length; i < n; i++) { for (var x = 0, y = r.length; x < y; x++) { if (r[x] == a[i]) continue o; } r[r.length] = a[i]; } return r; } >>> unique(BANNED_PASSWORDS).length 381 >>> BANNED_PASSWORDS.length 386 >>> show_duplicated(BANNED_PASSWORDS) abc123 asdfgh letmein monkey password
So, I wonder, if someone write a brute force script by loading this list, how many of early twitter user accounts will be comprised?
- tag:






Comments
Interesting breakdown - very
Interesting breakdown - very useful too ;) However, am I the only one confused as to what function D is attempting to achieve? It certainly isn't looking for the characters you mention - that's done afterwards. It appears to be looking for the number of duplicated letters - is that right?
Thanks for dropping by.
Thanks for dropping by. Interesting question. function D looks like, what you said, attempts to locate duplicated letters. But I also don't have a clue where this function is called :(
It's called 4 times in lines
It's called 4 times in lines 29-32 for different values of L
@mrben: I finally got some
@mrben:
I finally got some time to play around this, here are my breakdown:
D(1, "aaabcdef") returns abcdef
D(2, "aaabcdef") returns aaabcdef
K = "aaabcdef";
D(1, K).length - K.length) * 1;
this will return abdef
The way these two functions work are it first do basic detection (length <= 6, whether your password equals user name, whether it is in the array). Then call D(), that it basically checks for duplications, four times to calculate the strength of M (0 - 100), it returns different messages (0-34, 35-50, 51 -75, 76 -100) per the level of strength, which is an interesting approach.
P.S: I added comments to the original code, let me know if I am mistaken any part of them.