WN

WN (https://www.wn.se/forum/index.php)
-   Serversidans teknologier (https://www.wn.se/forum/forumdisplay.php?f=4)
-   -   Min lösenordslösning (https://www.wn.se/forum/showthread.php?t=27862)

NevYn 2008-03-11 14:18

Hej, har efter mycket om och men kommit fram till följande lösning för att hasha användares lösenord:

Kod:

$pwd = 'användarens lösenord';
$dynsalt1 = 'dynamiskt salt';
$dynsalt2 = 'annat dynamiskt salt';
$pass = base64_encode(hash_hmac,'sha512',$pwd,hash_hmac('sha512',$dynsalt1,$dynsalt2,true),true));

Jag tycker det borde vara en ganska bra lösning, men eftersom jag inte är allvetande kanske det finns någon annan där ute som har några synpunkter?

talthoff 2008-03-11 15:08

http://www.sitic.se/publikationer/na...-losenord-ratt

NevYn 2008-03-11 15:20

Citat:

Originally posted by talthoff@Mar 11 2008, 16:08
http://www.sitic.se/publikationer/na...-losenord-ratt
jo, den har jag läst, och har alltid använt sha1(dymaniskt salt + pwd + statiskt salt) tidigare, men har nu kommit fram till den här lösningen..

länkar:
http://en.wikipedia.org/wiki/HMAC
http://se2.php.net/hash_hmac
http://en.wikipedia.org/wiki/SHA_has...ions#SHA_sizes

efter att ha läst den infon så har jag fått för mig att den här lösningen är säkrare än den förstnämnda? men som sagt, jag är inte allvetande..

eliasson 2008-03-11 16:15

Kod:

md5(salt-på-100-random-chars + användarnamn + lösenord + salt-på-100-random-chars)
Fungerar också jättebra ;-)

gsoc 2008-03-11 17:20

Att sedan skriva ut hur man gör är ju inte så bra om man skall vara petig...

Kristoffer G 2008-03-11 17:25

Här har du en variant på ett login+registrerings system som jag gjort...ett väldigt väldigt simpelt sådan...


register.php
Kod:

<?php
//Kontrollerar om du försöker registrera dig
if(isset($_POST['skicka'])){
include 'inc/connect.php';
include 'inc/functions.php';
 
$in_user * *= $_POST['user'];
$in_pass * *= $_POST['pass'];
$in_check * = $_POST['check'];
 
//Anropar tre funktioner som kontrollerar de inmatade värdena
$check1 = langd($in_user, $in_pass);
$check2 = same($in_pass, $in_check);
$check3 = exists($in_user);
 
//Om allt är ok
if($check1 == "ok"){
if($check2 == "ok"){
if($check3 == "ok"){
 
//Genererar ett slumpat tal
$x = mt_rand(strlen($in_user), 101);
//Anropar frunktioner för att generera hash+salt+multiple runs
$p_hash = password($in_pass, $x);
$salt * = salt();
$fused *= fuse($p_hash, $salt, $x);
 
//Skickar in värdena i databasen
$count = $conn->exec("INSERT INTO Uregister(user, password, salt, x)VALUES('$in_user', '$fused', '$salt', '$x')");
$conn = null;
 
header ("location: register.php?done");
}
}
}
//Skriver ut eventuella felmeddelanden
if($check1 !== "ok"){ echo "$check1<br />";}
if($check2 !== "ok"){ echo "$check2<br />";}
if($check3 !== "ok"){ echo "$check3<br />";}
}
if(isset($_GET['done'])){
echo "Registrering lyckad";
}
?>
<form action="register.php" method="post">
Användarnamn: *<br />
<input type="text" name="user" /> <em>Minst 4 tecken</em><br />
Lösenord: <br />
<input type="password" name="pass" /> <em>Minst 4 tecken</em><br />
Upprepa Lösenord: <br />
<input type="password" name="check" /><br />
<input type="submit" name="skicka" value="Registrera" />
</form>

Login.php
Kod:

<?php
//If-sats som kontrollerar om man försöker logga in
if(isset($_POST['login'])){
include 'inc/connect.php';
include 'inc/functions.php';
 
$in_user = $_POST['user'];
$in_pass = $_POST['pass'];
 
//Hämtar värden från databasen, baserat på användarnamnet
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$do = $conn->prepare("SELECT password, salt, x FROM Uregister WHERE user = :user LIMIT 1");
$do->bindParam(':user', $in_user, PDO::PARAM_STR);
$do->execute();
$obj = $do->fetch(PDO::FETCH_OBJ);
 
$db_pass = $obj->password;
$db_salt = $obj->salt;
$db_x * *= $obj->x;
 
//Anropar funktionerna som behövs föra tt återskapa lösenordet
$gen_pass = password($in_pass, $db_x);
$gen_fuse = fuse($gen_pass, $db_salt, $db_x);
$conn = null;
 
//Kontrollerar om lösenorden matchar
if($gen_fuse == $db_pass){
//Skapar en session med alla uppgifter
session_start();
$_SESSION['user'] * = $in_user;
$_SESSION['pass'] * = $db_pass;
$_SESSION['ip'] * * = $_SERVER['REMOTE_ADDR'];
 
echo "Du är nu inloggad som {$_SESSION['user']}";
}
else{ echo "Fel användarnamn eller lösenord"; }
}
?>
<form action="login.php" method="post">
Användarnamn: *<br />
<input type="text" name="user" /> <em>Minst 4 tecken</em><br />
Lösenord: <br />
<input type="password" name="pass" /> <em>Minst 4 tecken</em><br />
<input type="submit" name="login" value="Login" />
</form>

Funktionerna
Kod:

<?php
//Kontrollerar längden på användarnamn och lösenord
function langd ($var1, $var2){
 * *if(strlen($var1)<4 || strlen($var2) <4){
 * *$error = "*Användarnamnet eller lösenordet är för kort";
 * *}
 * *else {$error = "ok"; }
 * *return $error;
}
 
//Kontrollerar om lösenordet matchar det upprepade lösenordet
function same($var1, $var2){
 * *if($var1 !== $var2){ $error = "*Lösenorden matchar inte"; }
 * *else { $error = "ok"; }
 * *return $error;
}
 
//Kontrollerar om användarnamnet redan existerar
function exists ($var1){
 * *include 'connect.php';
 * *
 * *$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 * *$do= $conn->prepare ("SELECT COUNT(id) as antal FROM Uregister WHERE user = :user ");
 * *$do->bindParam(':user', $var1, PDO::PARAM_INT);
 * *$do->execute();
 * *$obj = $do->fetch(PDO::FETCH_OBJ);
 * *$db_count = $obj->antal;
 * *
 * *if($db_count <1){ $error = "ok"; }
 * *else { $error = "*Det angivna användarnamnet finns redan"; }
 * *$conn = null;
 * *return $error;
}
 
//Hashar lösenordet
function password ($password, $x){
 * *$password = md5($password);
 * *for($i=0; $i <= $runs; $i++){
 * *$password = sha1($password);
 * *}
 * *return $password;
}
 
//Skapar ett salt
function salt (){
 * *$chars = "8!b9yea@of,g5ic¤j2d3k4_l7mhrn%#ps&t6uqvx-.1";
 * *$length = strlen($chars);
 
 * *for($i=0; $i <= $length; $i++){
 * *$runs = mt_rand(0,$length);
 * *$salt .= substr($chars, $runs, 1);
 * *}
 * *return sha1($salt);
}
//Bakar ihop lösenordet och saltet i en loop
function fuse ($password, $salt, $x){
 * *for($i=0; $i <= $runs; $i++){
 * *$fused = sha1("$password @:_:@ $salt");
 * *}
 * *return $fused;
}
?>

Du kan kolla i funktionerna osv hur jag har gjort. Fungerar väldigt bra... men tänk på att salt och x helst inte ska paras tilslammans med lösenordet i databasen.


EDIT*
Såg att jag glömt fixa så att INPUT kör genom prepared... men det ska det.

eliasson 2008-03-11 18:02

Citat:

Originally posted by gsoc@Mar 11 2008, 18:20
Att sedan skriva ut hur man gör är ju inte så bra om man skall vara petig...

Exakt, men dock så publicerar man inte sina salt's som totalt är ~200 garbage-chars ;-)

NevYn 2008-03-11 20:17

Citat:

Originally posted by gsoc@Mar 11 2008, 18:20
Att sedan skriva ut hur man gör är ju inte så bra om man skall vara petig...
Lösningen är något mer komplex än det jag skrivit ut, men det här utgör en del i den.
Mest hashnings-förfarandet jag va ute att få kommentarer på, då jag inte sett många som använt något liknande.

Jonas 2008-03-11 21:58

Varför ens köra Base64 ??? :S

Base64 kännetecknas enkelt på att det OFTAST är 2st likamed tecken på slutet.
Vilket gör det relativt enkelt för hackern när han får tag i dina lösenord och därmed mer eller mindre har lösenordet i klartext.

EN utav nackdelen med base64 är helt enkelt att varje tecken "väger mer".
Citat:

Base64-encoded data takes about 33% more space than the original data.
#EDIT: Notera även att Base64 inte är en hash. Base64 används främst i mailprotokollet för att skicka bilagor.

gsoc 2008-03-11 22:13

Vet inte om det bara är jag men det ser ut som din applikation är väldigt sårbar för sql injections...

register.php
Kod:

$in_user
Login.php
Kod:

$in_user
Sen kan man nog ta $var1 i funktionen exists, men det blir ungefär samma bara det att det är på databas anslutningar man kommer åt...

NevYn 2008-03-11 22:23

Citat:

Originally posted by Jonas@Mar 11 2008, 22:58
Varför ens köra Base64 ??? :S

Base64 kännetecknas enkelt på att det OFTAST är 2st likamed tecken på slutet.
Vilket gör det relativt enkelt för hackern när han får tag i dina lösenord och därmed mer eller mindre har lösenordet i klartext.

EN utav nackdelen med base64 är helt enkelt att varje tecken "väger mer".
Citat:

Base64-encoded data takes about 33% more space than the original data.
#EDIT: Notera även att Base64 inte är en hash. Base64 används främst i mailprotokollet för att skicka bilagor.

ehm, läste du hela raden, eller stannade du bara vid base64_encode?
anledningen till att jag kör base64_encode på det är för att få det i ett format som går att använda vettigt oberoende av vilket charset jag får för mig att använda i databasen..

Kod:

$pwd = 'hej';
$dynsalt1 = 'hej';
$dynsalt2 = 'hej;
$pass = base64_encode(hash_hmac('sha512',$pwd,hash_hmac('sha512',$dynsalt1,$dynsalt2,true),true));
echo $pass . "<br><br>" . base64_decode($pass);

ger:

bp7TOwYAEtNUXV0jTcoJxRqQ0gBhflcqOABRP2t8VnPxwEmRuG H+Dl1BmEX2VwX6bT2I0rzHSXYv1HcajDT9Rw==

nžÓ;ÓT]]#MÊ Å?Òa~W*8Q?k|VsñÀI‘¸aþ]A˜EöWúm=ˆÒ¼ÇIv/ÔwŒ4ýG

så att de skulle få mitt lösenord i klartext genom att jag använder base64_encode ställer jag mig ytterst tveksam till...

NOTERA: fortfarande inte hela systemet, utan bara hash-lösningen som jag kör med... tänker inte skriva ut hela heller :P

Kristoffer G 2008-03-11 22:32

[/QUOTE]
Citat:

Originally posted by gsoc@Mar 11 2008, 23:13
Vet inte om det bara är jag men det ser ut som din applikation är väldigt sårbar för sql injections...

register.php
Kod:

$in_user
Login.php
Kod:

$in_user
Sen kan man nog ta $var1 i funktionen exists, men det blir ungefär samma bara det att det är på databas anslutningar man kommer åt...

Använder PDO med prepared statements på alal databaskopplingar. Anses vara det säkraste idag. Innan jobbade man mycket med mysql_real_escape_string(), addslashes() osv...

Som jag skrev i edit så har jag glömt uppdatera SQL inputen till prepared.

Man kan sen utköka de script jag skickade med mer säkerhet... men det va inte det som diskutterades här.

Läs mer här (php.net)

Citat:


Prepared statements and stored procedures
Many of the more mature databases support the concept of prepared statements. What are they? You can think of them as a kind of compiled template for the SQL that you want to run, that can be customized using variable parameters. Prepared statements offer two major benefits:

The query only needs to be parsed (or prepared) once, but can be executed multiple times with the same or different parameters. When the query is prepared, the database will analyze, compile and optimize it's plan for executing the query. For complex queries this process can take up enough time that it will noticeably slow down your application if you need to repeat the same query many times with different parameters. By using a prepared statement you avoid repeating the analyze/compile/optimize cycle. In short, prepared statements use fewer resources and thus run faster.
The parameters to prepared statements don't need to be quoted; the driver handles it for you. If your application exclusively uses prepared statements, you can be sure that no SQL injection will occur. (However, if you're still building up other parts of the query based on untrusted input, you're still at risk).
Prepared statements are so useful that they are the only feature that PDO will emulate for drivers that don't support them. This ensures that you will be able to use the same data access paradigm regardless of the capabilities of the database.


Jonas 2008-03-11 22:32

Citat:

Ursprungligen postat av NevYn
ehm, läste du hela raden, eller stannade du bara vid base64_encode?

MMM jag läste hela raden.

Kod:

$pass = base64_encode(hash_hmac,'sha512',$pwd,hash_hmac('sha512',$dynsalt1,$dynsalt2,true),true));
Där du har en konstant, klartext, variabel, function,konstant.

Citat:

Ursprungligen postat av NevYn
anledningen till att jag kör base64_encode på det är för att få det i ett format som går att använda vettigt oberoende av vilket charset jag får för mig att använda i databasen..

Kvittar vilken charset du kör i MySQL, det handlar mer om vilket charset du skriver ut sidan i och vilket charset du får inputen i.
MySQL lagrar datat i det format du väljer att spara det som.

Du kan lagra UTF-8 i en Latin1 tabell utan problem, problemet dyker upp först när du skall läsa datat i någon mysql klient, men skriver du ut det på en websida så har du inga problem sålänge sidan visas i UTF-8.

Mao base64 är bara något värdelöst, räcker att datat i mysql är samma som datat du får ifrån användaren så fungerar det.

NevYn 2008-03-11 22:41

Citat:

Ursprungligen postat av Jonas
Citat:

Originally posted by -NevYn@Mar 11 2008, 23:23
ehm, läste du hela raden, eller stannade du bara vid base64_encode?


MMM jag läste hela raden.

Kod:

$pass = base64_encode(hash_hmac,'sha512',$pwd,hash_hmac('sha512',$dynsalt1,$dynsalt2,true),true));
Där du har en konstant, klartext, variabel, function,konstant.

Citat:

Ursprungligen postat av NevYn
anledningen till att jag kör base64_encode på det är för att få det i ett format som går att använda vettigt oberoende av vilket charset jag får för mig att använda i databasen..

Kvittar vilken charset du kör i MySQL, det handlar mer om vilket charset du skriver ut sidan i och vilket charset du får inputen i.
MySQL lagrar datat i det format du väljer att spara det som.

Du kan lagra UTF-8 i en Latin1 tabell utan problem, problemet dyker upp först när du skall läsa datat i någon mysql klient, men skriver du ut det på en websida så har du inga problem sålänge sidan visas i UTF-8.

Mao base64 är bara något värdelöst, räcker att datat i mysql är samma som datat du får ifrån användaren så fungerar det.

oj, såg nu att jag hade skrivit fel..
Kod:

$pass = base64_encode(hash_hmac,'sha512',$pwd,hash_hmac('sha512',$dynsalt1,$dynsalt2,true),true));
ska vara
Kod:

$pass = base64_encode(hash_hmac('sha512',$pwd,hash_hmac('sha512',$dynsalt1,$dynsalt2,true),true));
hash_hmac — Generate a keyed hash value using the HMAC method
string hash_hmac ( string $algo , string $data , string $key [, bool $raw_output ] )

'sha512' => algoritmen som används
$pwd = användarens lösenord
$dynsalt1 och $dynsalt2 är helt skilda från varandra, och unika för varje användare.

men men.. du menar alltså att base64_encode() skulle sabba hela funktionen?

Jonas 2008-03-11 22:52

Citat:

Originally posted by NevYn@Mar 11 2008, 23:41
men men.. du menar alltså att base64_encode() skulle sabba hela funktionen?
Jag anser personligen att du slösar bara utrymme på detta i din databas, indexet blir större och databasen blir slöare.

Har du en specifik längd (md5 = 32tecken, sha1 = 40tecken osv) så hade jag satsar på en varbinary (som varchar fast för binär data) eller någon blob istället. Båda 2 är casesensitiv vilket jag misstänker är kritiskt för dig samt har inget med charset att göra.

NevYn 2008-03-11 23:03

Citat:

Originally posted by Jonas@Mar 11 2008, 23:52
Jag anser personligen att du slösar bara utrymme på detta i din databas, indexet blir större och databasen blir slöare.

Har du en specifik längd (md5 = 32tecken, sha1 = 40tecken osv) så hade jag satsar på en varbinary (som varchar fast för binär data) eller någon blob istället. Båda 2 är casesensitiv vilket jag misstänker är kritiskt för dig samt har inget med charset att göra.

Ah, tack för tipset! Varbinary hade jag i min iver inte alls tänkt på :P

Förutom min min blunder gällande det, vad tycker du om själva sättet att hasha på?

SimonP 2008-03-11 23:30

Citat:

Originally posted by Jonas
Jag anser personligen att du slösar bara utrymme på detta i din databas, indexet blir större och databasen blir slöare.

Nej, nu är du fel ute :)

Att Base64-encoda en hash tar mindre plats att lagra i en databas än hex-encodad hash.

Exempel:
raw MD5 = 16 tecken
base64 MD5 = ca 25 tecken
hexadecimal MD5 = 32 tecken

NevYn 2008-03-11 23:40

Citat:

Originally posted by SimonP@Mar 12 2008, 00:30
Nej, nu är du fel ute :)

Att Base64-encoda en hash tar mindre plats att lagra i en databas än hex-encodad hash.

Exempel:
raw MD5 = 16 tecken
base64 MD5 = ca 25 tecken
hexadecimal MD5 = 32 tecken

i det här fallet sparar jag på att köra det binärt istället för att köra på base64..
base64 = 88 tecken
binärt = 64 tecken

SimonP 2008-03-11 23:45

Citat:

Originally posted by NevYn@Mar 11 2008, 23:40
i det här fallet sparar jag på att köra det binärt istället för att köra på base64..
base64 = 88 tecken
binärt = 64 tecken

Självklart tar binärt minst lagringsutymme.

Jonas 2008-03-12 10:39

Citat:

Originally posted by SimonP@Mar 12 2008, 00:30
Nej, nu är du fel ute
Att Base64-encoda en hash tar mindre plats att lagra i en databas än hex-encodad hash.
Exempel:
raw MD5 = 16 tecken
base64 MD5 = ca 25 tecken
hexadecimal MD5 = 32 tecken

Nu var det så att jag syftade på att köra base64 överhuvud taget och tog upp längden på det hex-encodade som exempel.

eg0master 2008-03-12 15:20

Citat:

Originally posted by gsoc@Mar 11 2008, 18:20
Att sedan skriva ut hur man gör är ju inte så bra om man skall vara petig...
Fel. varje form av säkerhetslösning (liksom kryptering) som bygger på att algoritmen är hemlig är i sig dålig eftersom algoritmen förr eller senare kommer bli känd. Om inte lösningen är säker nog trots att algoritmen är känd så är det en dålig lösning. Det är sunt att publicera sin algoritm eftersom man då får den validerad ur ett säkerhetsperspektiv.

Det gäller algoritmen. Om man sedan har en privat nyckel (tänk RSA) eller ett hemligt salt så må det vara hemligt, givet att man tror sig kunna hålla den informationen hemlig. Men algoritmen som används bör vara öppen.

edit: Och jag skulle kanske ha en åsikt om lösningen. Att hasha lösenordet med SHA och lite salt är ävl at anse som säkert i dagsläget. MD5 är generellt sett det inte eftersom det finns en del som lyckats återskapa MD5-värden genom att bara se på resultatet.

Robert 2008-03-12 16:21

Citat:

Originally posted by eliasson@Mar 11 2008, 17:15
Kod:

md5(salt-på-100-random-chars + användarnamn + lösenord + salt-på-100-random-chars)
Fungerar också jättebra ;-)

Man rekomenderar dock inte längre folk att använda varken MD5 eller SHA1 utan gå upp SHA386 alt SHA512.

Lumax 2008-03-12 16:26

Citat:

Ursprungligen postat av Robert
Citat:

Ursprungligen postat av eliasson
Kod:

md5(salt-på-100-random-chars + användarnamn + lösenord + salt-på-100-random-chars)
Fungerar också jättebra ;-)

Man rekomenderar dock inte längre folk att använda varken MD5 eller SHA1 utan gå upp SHA386 alt SHA512.

Vilka man? :)

Robert 2008-03-14 00:30

Citat:

Ursprungligen postat av Fredrik S
Citat:

Originally posted by -Robert@Mar 12 2008, 17:21
Citat:

Ursprungligen postat av eliasson
Kod:

md5(salt-på-100-random-chars + användarnamn + lösenord + salt-på-100-random-chars)
Fungerar också jättebra ;-)

Man rekomenderar dock inte längre folk att använda varken MD5 eller SHA1 utan gå upp SHA386 alt SHA512.


Vilka man? :)

...de som vet att det finns brister i md5/sha1. :)

SimonP 2008-03-14 10:17

Citat:

Originally posted by Robert@Mar 14 2008, 00:30
...de som vet att det finns brister i md5/sha1. :)
...och de som förstår bristerna vet att dom inte påverkar hashing i lösenordsammanhang.

Vi har diskuterat det förut:
http://www.webmasternetwork.se/index.php?a...hl=blixten&s=wn

Robert 2008-03-14 16:26

Ja, jag vet inte hur det är i eran php-värld, men för mig är det iaf en fråga om att skriva siffran "512" istället för "1" efter order SHA när jag ska generera en hash. Jag köper att det är minimal skillnad för ett vangligt svenskt svensson system som aldrig under sin livstid kommer att ha data tillräckligt intressant för NSA eller vilka som nu skulle kunna lägga resurser på att knäcka sin egen algorithm (SHA1) ;) Men då disk är billigt så sparar jag hellre en hash med 64tecken än en med 32. :)
Ska ett system leva i några år så vill man ju inte låta alla användare skapa nya passwords en vacker dag om det behövs bytas.

"Attacks always get better; they never get worse"

Adam N 2008-03-16 02:22

När jag läser inläggen i denna tråd så får jag en känsla av att lösenorden ofta skickas i klartext från
webbläsare till webbserver. Redan här bör man väl använda hash och kombinera med nonce vid inloggning?

eg0master 2008-03-16 09:28

Citat:

Originally posted by Adam N@Mar 16 2008, 03:22
När jag läser inläggen i denna tråd så får jag en känsla av att lösenorden ofta skickas i klartext från
webbläsare till webbserver. Redan här bör man väl använda hash och kombinera med nonce vid inloggning?

Frågan är ju hur sannolikt det är att någon kommer över databasen med de hashade lösenorden eller sniffar trafik för att fånga enskilda lösenord i klartext. Är man rädd för det senare finns det en enkel lösning... SSL.


Alla tider är GMT +2. Klockan är nu 03:50.

Programvara från: vBulletin® Version 3.8.2
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Svensk översättning av: Anders Pettersson