Přechod na bezpečnější bcrypt

(publikováno 28.02.2017) PHP, Bezpečnost, Tipy & triky

Jednoduchý postup, jak implementovat bezpečný bcrypt do systému, který stále ukládá hesla pomocí starých hashovacích funkcí jako je MD5, SHA1 ať už se saltem nebo bez.

Přechod na bezpečnější bcrypt

O tom, že funkce jako MD5, SHA1 a další již nejsou bezpečné, a je vhodné použít novější funkce, je článek Jak správně ukládat hesla. Tento článek trochu naváže. Bude popsán jednoduchý postup, jak implementovat password_hash i do již zaběhnutých systémů.

Na svém blogu Michal Špaček jde ještě trochu dále a zabezpečuje také hesla zahashována starou metodou. Jednoduše proto, ať se vám nestane to co Mallu.

Jak na to

Mnoho lidí mohlo odradit, že je potřeba upravit databázi, ukládat typ hashe, jaký je použit apod. Poté to vše ještě implementovat v kódu a je to moc práce. Nic z toho ale nutné není, a i úpravy kódu mohou být minimální.

Úprava databáze

Pokud se heslo nyní ukládá jako VARCHAR(32) (pro MD5) nebo VARCHAR(40) (pro SHA1) apod., stačí zvětšit pole na VARCHAR(255). Ke změně ani ke ztrátě dat nedojde, a pro bcrypt to bude stačit.

Úprava kódu

V článku o bcryptu a PHP funkcích password_* bylo popsáno, že výstup z funkcí password_hash() začíná vždy speciální sekvencí $xx$yy$..., a toho se dá využít. Výstup funkce SHA1 případně MD5 nikdy znakem $ nezačne.

Proto stačí password_* funkce zaobalit do vlastních funkcí, aby fungovaly i se starou verzí hashování. Funkce password_hash slouží pouze ke generování hashe, a ten se vždy bude generovat již bezpečně pomocí bcryptu. Proto stačí napsat vlastní funkce pouze pro password_verify() a password_needs_rehash(). Pokud je potřeba, dopsat funkci password_get_info() už snad nebude problém.

// Kontrola správnosti hashe pro dané heslo
// $saltedPass je volitelně použit jen pro starý způsob hashování
function backward_password_verify($pass, $hash, $saltedPass = null)
{
    if ($hash[0] == "$") {
        return password_verify($pass, $hash);
    }else{
        if (empty($saltedPass)) {
            $saltedPass = $pass;
        }
        // Zde vložit starý způsob hashování 
        $countedHash = md5($saltedPass);
        return $hash == $countedHash;
    }
}
// Kontrola, jestli je potřeba heslo přegenerovat
// Pro starý způsob hashování vždy vrátí true
function backward_password_needs_rehash($hash)
{
    if ($hash[0] == "$") {
        return password_needs_rehash ($hash, PASSWORD_DEFAULT);
    }else{
        return true;
    }
}

Použití

Použití se nyní neliší od použití v předchozím článku, stačí pouze použít nové funkce.

// Vytvoření hash pomocí defaultního algoritmu, aktuálně Bcrypt
// Hash nyní může být také MD5 nebo cokoli staršího, použitého
// v backward_password_* funkcích
$hash = password_hash("mojeTajnéHeslo", PASSWORD_DEFAULT);


// Kontrola shodnosti hashe a zadaného hesla
if(backward_password_verify ( "mojeTajnéHeslo" , $hash )){
    // heslo odpovídá

    // Kontrola, jestli lze vytvořit kvalitnější hash
    if(backward_password_needs_rehash ( $hash )){
        // Uložit do DB nový hash
        $hash = password_hash("mojeTajnéHeslo", PASSWORD_DEFAULT);
    }
}

Podpora pro starší PHP

Funkce password_* jsou až od PHP verze 5.5, ani to ale nemusí odradit od aplikace bezpečných hashovacích funkcí. Stačí použít password_compat knihovnu napsanou jedním z vývojářů PHP. Ta lze použít již od v5.3.7, takže by již nic nemělo bránit použití. Instalovat lze jak pomocí composeru tak pomocí jednoduchého include, jedná se pouze o jedinný soubor.


Tak už jste přešli na bezpečnější způsob ukládání hesel? Podělte se v komentářích

K tomuto článku již není možné přidávat další komentáře