Security

Password strength

How guessable a password is (zxcvbn): a score from 0 to 4, a warning and suggestions, and how long it would take to crack.

Version 1.0.0 · 2 functions · 1 crate

#passwords#auth#forms#sign-up

Functions

What use password_strength.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use password_check, password_score from password_strength (Quillo 0.6).

password_check(password: str, user_inputs: list) → any

The full judgement of password: score 0 (guessed at once) to 4 (very hard), warning and suggestions in English, guesses, and crack_time (an offline attack on a slow hash such as bcrypt) and crack_seconds. user_inputs are words a guesser would try first: the user's name, email, the site's name. An empty password is score 0.

password_score(password: str, user_inputs: list) → int

Only the score, 0 to 4: a form can ask for 3 or more.

Get it

$ quillo install password_strength               # password_strength.qwer into this folder$ quillo install password_strength --example     # and its example, password_strength.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/password_strength.qwer$ curl -O https://quillolang.org/plugins/files/password_strength.qwe

Then, in your program, all of it or only what you call:

main.qwe
use password_strength.qweruse password_check, password_score from password_strength      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

[dependencies]
zxcvbn = "3"

Example

password_strength.qwe
// A sign-up form that refuses weak passwords and says why.////   quillo run password_strength.qwe      then open http://localhost:8080/check?password=ada1815use "password_strength.qwer"route GET /check ? { password: str, email: str = "ada@example.com" } {    let verdict = password_check(password, [email, "quillo shop"])    { ...verdict, accepted: verdict.score >= 3 }}route GET /compare = {    weak: password_score("password1", []),    own_name: password_score("ada.lovelace", ["Ada Lovelace", "ada@example.com"]),    strong: password_score("correct horse battery staple violin", []),}
$ quillo run password_strength.qwe$ curl 'localhost:8080/check?password=password1'$ curl 'localhost:8080/compare'$ curl 'localhost:8080/check?password=ada1815'

Source

password_strength.qwer
//! How guessable a password is (zxcvbn): a score from 0 to 4, why, what to do about it, and how long it would take to crack, for sign-up forms.//! version: 1.0.0//!//! [dependencies]//! zxcvbn = "3"use serde_json::{json, Value};/// Passwords longer than this are judged on their first 100 characters: zxcvbn's work grows with the length.const MAX_LEN: usize = 100;fn cut(password: &str) -> String {    password.chars().take(MAX_LEN).collect()}fn inputs(user_inputs: &[String]) -> Vec<&str> {    user_inputs.iter().take(20).map(|s| s.as_str()).collect()}/// The full judgement of `password`: `score` 0 (guessed at once) to 4 (very hard), `warning` and `suggestions` in English, `guesses`,/// and `crack_time` (an offline attack on a slow hash such as bcrypt) and `crack_seconds`. `user_inputs` are words a guesser/// would try first: the user's name, email, the site's name. An empty password is score 0.pub fn password_check(password: &str, user_inputs: Vec<String>) -> Value {    let p = cut(password);    if p.is_empty() {        return json!({ "score": 0, "warning": "Enter a password.", "suggestions": [], "guesses": 0, "crack_time": "instant", "crack_seconds": 0.0 });    }    let e = zxcvbn::zxcvbn(&p, &inputs(&user_inputs));    let t = e.crack_times().offline_slow_hashing_1e4_per_second();    let (warning, suggestions) = match e.feedback() {        Some(f) => (f.warning().map(|w| w.to_string()), f.suggestions().iter().map(|s| s.to_string()).collect::<Vec<_>>()),        None => (None, vec![]),    };    json!({        "score": u8::from(e.score()),        "warning": warning,        "suggestions": suggestions,        "guesses": e.guesses().min(i64::MAX as u64) as i64,        "crack_time": t.to_string(),        "crack_seconds": match t { zxcvbn::time_estimates::CrackTimeSeconds::Integer(i) => i as f64, zxcvbn::time_estimates::CrackTimeSeconds::Float(f) => f },    })}/// Only the score, 0 to 4: a form can ask for 3 or more.pub fn password_score(password: &str, user_inputs: Vec<String>) -> i64 {    let p = cut(password);    if p.is_empty() {        return 0;    }    u8::from(zxcvbn::zxcvbn(&p, &inputs(&user_inputs)).score()) as i64}
Thread
68Useful? Like it, or add to its thread below.

Thread 0 posts

On Password strength: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.