Inloggningsskript och lösenordshash, säkerhet? [PHP/MySQL]

Permalänk
Medlem

Inloggningsskript och lösenordshash, säkerhet? [PHP/MySQL]

Hej!
Jag håller på med ett inloggningsskript, och är väl färdigt... Men är lite osäker på hur säkert det är, då jag lärt mig allt medan jag gjort det. Hade blivit väldigt tacksam ifall någon med större koll på säkerhet skulle vilja ta en titt på det här
Inloggning:

session_start(); include 'db_conn.php'; function clean($str) { $str = @trim($str); if(get_magic_quotes_gpc()) { $str = stripslashes($str); } return mysql_real_escape_string($str); } $usrname = clean($_POST['usrname']); $pwd = clean($_POST['pwd']); $salt = sha1(md5($pwd)); $pwd = md5($pwd.$salt); $mysql_qry = "SELECT member_id FROM members WHERE usrname='$usrname' AND pwd='$pwd'"; $result = mysql_query($mysql_qry); if($result) { if(mysql_num_rows($result) > 0) { session_regenerate_id(); $member = mysql_fetch_assoc($result); $_SESSION['SESS_MEMBER_ID'] = $member['member_id']; session_write_close(); header("location: index.php?s=member.php"); exit(); }else { echo "Login failed!"; exit(); } }else { die("Query failed"); }

Autentiseringen:

session_start(); if(!isset($_SESSION['SESS_MEMBER_ID']) || (trim($_SESSION['SESS_MEMBER_ID'])=='')) { header("location: index.php"); exit(); }

Har jag missat något?

Tack på förhand!
//Johannes

Visa signatur

Desktop|i5 3570k(@4,4GHz)|Asus P8Z77-V|AMD 6950|12GB RAM|Crucial BX500 480GB|Manjaro|
Laptop|Lenovo T440s|i7|8GB RAM|Debian Jessie|
Server|Fujitsu Primergy TX1310|G1820|8GB RAM|15TB|Unraid|
Ring, lånad mail

Permalänk
Inaktiv

Jag skulle använt SHA256 istället om jag var du.

Permalänk
Medlem

Förutom att byta till SHA256 kan du även sätta en gräns på misslyckade inloggningsförsök.

Visa signatur

Intel i7 8700 | ASUS Prime Z370-P | Corsair 16GB 3000MHz | ASUS GTX 1080 | Fractal Design Define S | Corsair RM750x | Hyper 212 EVO

Permalänk

Du kan även ha två salt. Ett (slumpat) för usern som du sparar i databasen och ett generellt för alla konton som du sparar som en config-variabel i en php-fil. Sen lägger på dem båda på när du hashar lösenordet. Lägg på dem efter lösenordet.

Även bra tips vid inloggning är att inte visa om det är användarnamn eller lösenord som är fel. Skriv bara "användarnamn eller lösenord fel".

Visa signatur

/Mvh Stefan

Permalänk
Medlem

Du bör verifiera att sessionen är giltig. Kontrollera att användaren verkligen loggade in från samma ip-adress och spara senaste aktivitet så du kan logga ut användaren efter ett tag.

if (isset($_SESSION['remote_addr']) AND isset($_SESSION['last_activity']) AND $_SESSION['remote_addr'] == $_SERVER['REMOTE_ADDR'] AND $_SESSION['last_activity'] > strtotime('-30 minutes')) { $_SESSION['last_activity'] = time(); } else die('Du har blivit utloggad');

Sen kan du även använda fler tekniker för att skydda dina lösenord mot en rainbowtable-attack.
Målet är att det ska kräva datorkraft för att få fram ett enda lösenord.

  • Slumpad salt (det gör du princip redan)

  • Olika mycket kryptering på lösenordet, något liknande

    $min = 15; $max = 100; $password = "querty"; for ($i = 0; $i < rand($min, $max); $i++) { $password = sha1($password); }

  • Ändra hashet genom att byta ut vissa bokstäver. Tex alla 'a' till 'B' och alla 'B' till 'a'.

Några bra länkar att läsa igenom
http://codahale.com/how-to-safely-store-a-password/
http://juliusbeckmann.de/blog/easy-to-use-and-secure-php-hash... (färdig klass)
http://net.tutsplus.com/tutorials/php/understanding-hash-func...

Permalänk

Kör "Portable PHP password hashing framework" om du vill ha mer av ett ordentligt skydd.
Läs första svaret här: http://stackoverflow.com/questions/1581610/help-me-make-my-pa...
Officiella sidan här: http://www.openwall.com/phpass/

Väldigt enkelt att använda.

Visa signatur

"Knowledge amplification. What he learns, we all learn. What he knows, we all benefit from."

Permalänk
Skrivet av Xburk:

Hej!
Jag håller på med ett inloggningsskript, och är väl färdigt... Men är lite osäker på hur säkert det är, då jag lärt mig allt medan jag gjort det. Hade blivit väldigt tacksam ifall någon med större koll på säkerhet skulle vilja ta en titt på det här
Inloggning:

session_start(); include 'db_conn.php'; function clean($str) { $str = @trim($str); if(get_magic_quotes_gpc()) { $str = stripslashes($str); } return mysql_real_escape_string($str); } $usrname = clean($_POST['usrname']); $pwd = clean($_POST['pwd']); $salt = sha1(md5($pwd)); $pwd = md5($pwd.$salt); $mysql_qry = "SELECT member_id FROM members WHERE usrname='$usrname' AND pwd='$pwd'"; $result = mysql_query($mysql_qry); if($result) { if(mysql_num_rows($result) > 0) { session_regenerate_id(); $member = mysql_fetch_assoc($result); $_SESSION['SESS_MEMBER_ID'] = $member['member_id']; session_write_close(); header("location: index.php?s=member.php"); exit(); }else { echo "Login failed!"; exit(); } }else { die("Query failed"); }

Autentiseringen:

session_start(); if(!isset($_SESSION['SESS_MEMBER_ID']) || (trim($_SESSION['SESS_MEMBER_ID'])=='')) { header("location: index.php"); exit(); }

Har jag missat något?

Tack på förhand!
//Johannes

Hej hej
Du genererar salt:en fel. Problemet du har är helt enkelt att det du lagrar i databasen är en deterministisk funktion av lösenorden! Detta gör att en hacker kan
1. Se om två personer har samma lösenord.
2. Cracka alla lösenorden i databasen samtidigt istället för ett i taget.

Istället borde du generera och lagra ett helt slumpmässigt salt när du först sparar lösenordet i databasen och sen när folk ska logga in, hämta och använda det salt:et.

Sen att du använder md5 är lite konstigt eftersom det finns en del attacker mot det. Du borde köra antingen sha1, sha256 eller blowfish istället.

Permalänk
Medlem

Tack för alla svar! Nu har jag ändrat koden lite...

// auth.php: session_start(); if(!isset($_SESSION['SESS_MEMBER_ID']) || (trim($_SESSION['SESS_MEMBER_ID'])=='')) { header("location: index.php"); exit(); } if (isset($_SESSION['remote_addr']) AND isset($_SESSION['last_activity']) AND $_SESSION['remote_addr'] == $_SERVER['REMOTE_ADDR'] AND $_SESSION['last_activity'] > strtotime('-30 minutes')) { $_SESSION['last_activity'] = time(); } else die('Du har blivit utloggad'); //login-exec.php: session_start(); include 'db_conn.php'; require 'PasswordHash.php'; function clean($str) { $str = @trim($str); if(get_magic_quotes_gpc()) { $str = stripslashes($str); } return mysql_real_escape_string($str); } $usrname = clean($_POST['usrname']); $pwd = clean($_POST['pwd']); $pwdHasher = new PasswordHash(8, FALSE); $mysql_qry = mysql_query("SELECT pwd, member_id FROM members WHERE usrname='$usrname'"); $result = mysql_fetch_assoc($mysql_qry); $hash_db = $result['pwd']; $member_id = $result['member_id']; $checked = $pwdHasher->CheckPassword($pwd, $hash_db); if ($checked) { session_regenerate_id(); $_SESSION['SESS_MEMBER_ID'] = $member_id; $_SESSION['remote_addr'] = $_SERVER['REMOTE_ADDR']; $_SESSION['last_activity'] = time(); session_write_close(); header("location: index.php?s=member"); exit(); } else { echo "Login failed!"; exit(); }

Skrivet av Xyntica:

Du bör verifiera att sessionen är giltig. Kontrollera att användaren verkligen loggade in från samma ip-adress och spara senaste aktivitet så du kan logga ut användaren efter ett tag.

if (isset($_SESSION['remote_addr']) AND isset($_SESSION['last_activity']) AND $_SESSION['remote_addr'] == $_SERVER['REMOTE_ADDR'] AND $_SESSION['last_activity'] > strtotime('-30 minutes')) { $_SESSION['last_activity'] = time(); } else die('Du har blivit utloggad');

Tror att jag har lyckats med det där nu. Eller?

Skrivet av KentRoyal:

Kör "Portable PHP password hashing framework" om du vill ha mer av ett ordentligt skydd.
Läs första svaret här: http://stackoverflow.com/questions/1581610/help-me-make-my-pa...
Officiella sidan här: http://www.openwall.com/phpass/

Väldigt enkelt att använda.

Verkar ju skitsmidigt. Tror allt jag fått det rätt nu

Tack som fan än en gång

Visa signatur

Desktop|i5 3570k(@4,4GHz)|Asus P8Z77-V|AMD 6950|12GB RAM|Crucial BX500 480GB|Manjaro|
Laptop|Lenovo T440s|i7|8GB RAM|Debian Jessie|
Server|Fujitsu Primergy TX1310|G1820|8GB RAM|15TB|Unraid|
Ring, lånad mail

Permalänk
Medlem

Sedan värt att notera är extra säkerhet som att kolla IP mot din databas bland anant, det hela beror helt och hållet på hur säkert du vill att det ska vara.

Vill du t.ex. minimera risken med att t.ex. folk sniffar eller själ cookies ( vist inte vanligt men ingen omöjlighet ) så kan du dessutom ha en extra koll mot IP't och stämmer inte det så logga ut användaren, detta är heller ingen 100% lösning men ger ytterligare skydd.

Edit: Såg att du precis la till det bah sorry ignorera min post.

Visa signatur

Speldator: i7-8700k, 32GB DDR4, RTX2080
Server 1: SB 2500k, MZI -P67GD55, 32GB DDR3, Corsair MX 240GB SSD
Surface Pro 2017, Konsoler: Typ alla, Oculus Rift

Permalänk
Medlem

Hej igen!

Håller på att bli galen här. Har använt detta skript (från min senaste post) lokalt när jag jobbat med den här sidan, och allt har fungerat galant. Nu när jag ska sätta upp det på den riktiga servern, funkar inte skriptet. Den säger att det är fel lösenord.
Jag har kollat, massor med gånger, att det är rätt hash som lagras i databasen, och att det är rätt hash från rätt rad som hämtas från databasen. "Portable PHP password hashing framework" som jag använder, fungerar också på servern, i alla fall enligt deras egna test.
När jag lägger lösenordet, och hashet som variabler i php-filen fungerar det, då säger portable hashing framework att det är rätt lösenord, men inte när jag hämtar från databasen eller från formuläret.

Någon som har nån ide på vad som kan vara fel?

Visa signatur

Desktop|i5 3570k(@4,4GHz)|Asus P8Z77-V|AMD 6950|12GB RAM|Crucial BX500 480GB|Manjaro|
Laptop|Lenovo T440s|i7|8GB RAM|Debian Jessie|
Server|Fujitsu Primergy TX1310|G1820|8GB RAM|15TB|Unraid|
Ring, lånad mail

Permalänk
Medlem
Skrivet av Xburk:

Hej igen!

Håller på att bli galen här. Har använt detta skript (från min senaste post) lokalt när jag jobbat med den här sidan, och allt har fungerat galant. Nu när jag ska sätta upp det på den riktiga servern, funkar inte skriptet. Den säger att det är fel lösenord.
Jag har kollat, massor med gånger, att det är rätt hash som lagras i databasen, och att det är rätt hash från rätt rad som hämtas från databasen. "Portable PHP password hashing framework" som jag använder, fungerar också på servern, i alla fall enligt deras egna test.
När jag lägger lösenordet, och hashet som variabler i php-filen fungerar det, då säger portable hashing framework att det är rätt lösenord, men inte när jag hämtar från databasen eller från formuläret.

Någon som har nån ide på vad som kan vara fel?

Det var ett tag sedan jag höll på med det här området men min första tanke är om det du får tillbaka är en annan charset än det som har lagrats.

Visa signatur

i7 920 | 12GB DDR3 | GTX 480 | GA-X58A-UD7 | 160GB SSD X25-M G2 | 1TB F3 HD103SJ | W7 64-bit | Mac Mini
Webb: bluekitestudios.com

Permalänk
Medlem
Skrivet av save:

Det var ett tag sedan jag höll på med det här området men min första tanke är om det du får tillbaka är en annan charset än det som har lagrats.

Det verkade inte så när jag testade igår.. Men jag kan ha tabbat mig där. Ska kolla igen.

När jag kollade errorloggen så sade den att imagick saknas.. Det är väl inget som behövs nu? Och enligt testfilen, så skulle hashing frameworket fungera.

Edit: Öhrm.. mb_detect_encoding() säger bara ASCII, men i databasen är det latin1_swedish_ci som gäller. Kan detta vara något som strular?

EDIT2: Puckat nog av mig insåg jag inte förrns nu att jag inte kan använda samma hash jag generat på min WAMP-server..... Nu funkar't galant.

Visa signatur

Desktop|i5 3570k(@4,4GHz)|Asus P8Z77-V|AMD 6950|12GB RAM|Crucial BX500 480GB|Manjaro|
Laptop|Lenovo T440s|i7|8GB RAM|Debian Jessie|
Server|Fujitsu Primergy TX1310|G1820|8GB RAM|15TB|Unraid|
Ring, lånad mail

Permalänk
Medlem

Har börjat jobba med en ny inloggningsklass i PHP - än så länge bara en sessionhandler.
Tänkte först starta en ny tråd men kom på att jag nog hade en gammal.

Kollade in denna guide: http://www.wikihow.com/Create-a-Secure-Session-Managment-Syst...
Och har kodat lite efter den, eller nästan bara copy-pasteat - men det har dykt upp lite frågor och problem som jag skulle vilja ha er hjälp med!

Koden:

class SessionManager { function __construct() { // set our custom session functions. session_set_save_handler(array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'), array($this, 'destroy'), array($this, 'gc')); // This line prevents unexpected effects when using objects as save handlers. register_shutdown_function('session_write_close'); } function start_session($session_name, $secure) { // Make sure the session cookie is not accessable via javascript. $httponly = true; $session_hash = 'whirlpool'; // Check if whirlpool is available if (in_array($session_hash, hash_algos())) { // Set the has function. ini_set('session.hash_function', $session_hash); } // How many bits per character of the hash. // The possible values are '4' (0-9, a-f), '5' (0-9, a-v), and '6' (0-9, a-z, A-Z, "-", ","). ini_set('session.hash_bits_per_character', 5); // Force the session to only use cookies, not URL variables. ini_set('session.use_only_cookies', 1); // Get session cookie parameters $cookieParams = session_get_cookie_params(); // Set the parameters session_set_cookie_params($cookieParams["lifetime"], $cookieParams["path"], $cookieParams["domain"], $secure, $httponly); // Change the session name session_name($session_name); // Now we can start the session session_start(); // This line regenerates the session and delete the old one. // It also generates a new encryption key in the database. session_regenerate_id(true); } function open() { $sess_db = new PDO('mysql:host=localhost;dbname=sec_sessions', 'sess_usr', 'password'); $sess_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //Kommer självklart inte köra ERRMODE_EXCEPTION i skarp version $this->db = $sess_db; return true; } function read($id) { if(!isset($this->read_stmt)) { $this->query = $this->db->prepare("SELECT data FROM sessions WHERE id = ? LIMIT 1"); } $this->query->execute(array($id)); $row = $this->query->fetchAll(PDO::FETCH_ASSOC); $data = $row[0]; $data = $data['data']; $key = $this->getkey($id); $data = $this->decrypt($data, $key); return $data; } function write($id, $data) { // Get unique key $key = $this->getkey($id); // Encrypt the data $data = $this->encrypt($data, $key); $time = time(); if(!isset($this->w_stmt)) { $this->w_stmt = $this->db->prepare("REPLACE INTO sessions (id, set_time, data, session_key) VALUES (:id, :time, :data, :key)"); } $params = array('id' => $id,'time' => $time,'data' => $data,'key' => $key); $this->w_stmt->execute($params); return true; } function destroy($id) { if(!isset($this->delete_stmt)) { $this->delete_stmt = $this->db->prepare("DELETE FROM sessions WHERE id=:id"); } $this->delete_stmt->bindValue(':id', $id, PDO::PARAM_STR); $this->delete_stmt->execute(); if ($this->delete_stmt->rowCount() > 0) { return true; } else { $this->errmsg .= "Failed to delete session from DB. \n"; return false; } } function close() { return true; } function gc($max) { if(!isset($this->gc_stmt)) { $this->gc_stmt = $this->db->prepare("DELETE FROM sessions WHERE set_time < :old"); } $old = time() - $max; $this->gc_stmt->bindValue(':old', $old, PDO::PARAM_STR); $this->gc_stmt->execute(); return true; } private function getkey($id) { if(!isset($this->key_stmt)) { $this->key_stmt = $this->db->prepare("SELECT session_key FROM sessions WHERE id = ? LIMIT 1"); } $this->key_stmt->execute(array($id)); if($this->key_stmt->rowCount() == 1) { $row = $this->key_stmt->fetchAll(PDO::FETCH_ASSOC); $row = $row[0]; return $row['session_key']; } else { $random_key = hash('sha512', uniqid(mt_rand(1, mt_getrandmax()), true)); return $random_key; } } private function encrypt($data, $key) { $salt = 'cH!swe!ASÖUOIFHD=(D/ASFHÖKJH(/oijhKJASas*ewr4n39=E@rAsp7c-Ph@pH'; $key = substr(hash('sha256', $salt.$key.$salt), 0, 32); $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB); $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); $encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_ECB, $iv)); return $encrypted; } private function decrypt($data, $key) { $salt = 'cH!swe!ASÖUOIFHD=(D/ASFHÖKJH(/oijhKJASas*ewr4n39=E@rAsp7c-Ph@pH'; $key = substr(hash('sha256', $salt.$key.$salt), 0, 32); $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB); $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($data), MCRYPT_MODE_ECB, $iv); return $decrypted; } }

Första frågan: Är det någon idé att lagra sessiondata i databas?
Är metoden med AES + nyckel i databas säkrare än senaste PHPs session-hantering?

Och så är jag lite förvirrad. Om jag t. ex. skall förstöra en session vid en utloggning; ska jag köra $SessionManager->destroy() ? Isf behöver jag ju ha id till sessionen, och hur får jag fram det?

Visa signatur

Desktop|i5 3570k(@4,4GHz)|Asus P8Z77-V|AMD 6950|12GB RAM|Crucial BX500 480GB|Manjaro|
Laptop|Lenovo T440s|i7|8GB RAM|Debian Jessie|
Server|Fujitsu Primergy TX1310|G1820|8GB RAM|15TB|Unraid|
Ring, lånad mail