Diskuze: Registrace -> oveření
V předchozím kvízu, Online test znalostí PHP, jsme si ověřili nabyté zkušenosti z kurzu.

Člen

Zobrazeno 28 zpráv z 28.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Online test znalostí PHP, jsme si ověřili nabyté zkušenosti z kurzu.
ten kód má několik zásadních chyb.
1.) neobjektový, takhle se ti s ním blbě pracuje a ještě §ř se řeší
problémy
2.) starý databázový ovladač, v dokumentaci k PHP je to i výslovně
napsané
3.) SQL injekce, pokud bych jako nick uvedl KarelHacker a email
';TRUNCATE `users`--
smažu ti všechny uživatele.
4.) příliš mnoho zanoření (ale to souvisí i s bodem 1
if ($overeni1['username']!==$nick){
if ($overeni1['email']!==$e_mail){
if ($heslo1 == $heslo2){
if ($nick AND $e_mail){
FUJ!
5.) vypisovaní HTML. Logika má být oddělena od výstupu.
6.) pole písmen si naplň cyklem, ale to pole mi přijde celkově
zbytečné.
přečti si zdejší tutorialy hodně ti pomůžou.
Pokud se na tom teprve učí, OOP bych do toho netahal.
Úplně se v tom scriptu nevyznám, ale zkusím pomoct:
Zprávu pro mail bych vytvářel až v případě, že takový uživatel neexistuje (tzn. se registruje nový účet). Pokud ji vytváříš před kontrolami, pak je to jednak zbytečné, jednak je to nepřehledné (alespoň mně to tak přijde).
První SQL dotaz je nebezpečný na SQL inject, o tom psal už Misaz.
Ten druhý ti vybírá všechny záznamy z tabulky users
, ale v
proměnné $overeni1 je pouze jeden záznam (myslím, že ten poslední v
tabulce), takže ti to nezkontroluje, jestli už nějaký uživatel s daným
jménem / emailem existuje.
Doporučuji nepoužívat názvy proměnných, ve kterých je na konci číslo, jako např. $dotaz1 a $dotaz2. Je to nepřehledné a navíc máš potom $overeni1 u $dotaz2 (takže k sobě ani ta čísla nesedí).
Při dotazu INSERT doporučuji vyjmenovat sloupce, do kterých vkládáš hodnoty. Jednak pak nemusíš ručně psát všechny hodnoty (např. ID, které je AI, nebo sloupce, které mají defaultní hodnotu), tak se nestane chyba, že ti třeba nějaký sloupec přebývá.
Heslo bych nehashoval pomocí funkce md5(), která už není bezpečná.
Pokud program obsahuje hodně rozvětvení (podmínek), je otázka, zda nebude lepší podmínky psát opačně a jednotlivé chyby si ukládat do pole. Výhoda je v přehlednosti a také můžeš vypsat více chyb naráz. Např.:
$chyby = array();
if (empty($_POST["jmeno"])) {
$chyby[] = "Nebylo vyplněno jméno".
}
if (empty($_POST["heslo"])) {
$chyby[] = "Nebylo vyplněno heslo".
}
if (empty($_POST["heslo_kontrola"])) {
$chyby[] = "Nebylo vyplněno kontrolní heslo".
}
if (empty($_POST["email"])) {
$chyby[] = "Nebyl vyplněn e-mail".
}
// zde může být další hromada podmínek
if (empty($chyby)) { // pokud je pole s chybami prázdné
$jmeno = mysql_real_escape_string($_POST["jmeno"]);
$heslo = nejaka_hash_funkce($_POST["heslo"]);
$email = $_POST["email"]; // zde se předpokládá, že hodnota mailu byla ošetřena např. regulárním výrazem (aby byl správný formát)
$mail_zprava = "nějaké texty";
$mail_hlavicka = "...";
mysql_query("
INSERT INTO `users` (`jmeno`, `heslo`, `email`)
VALUES ('{$jmeno}', '{$heslo}', '{$email}')
");
mb_send_mail($email, $mail_zprava, $mail_hlavicka);
header("location:nekam.php");
exit;
}
// Chyby pak můžeš vypisovat poměrně snadno:
if (!empty($chyby)) {
echo "<ul>";
foreach ($chyby as $chyba) {
echo "<li>{$chyba}</li>";
}
echo "</ul>";
}
S emailem mi to taky blbo, vyřešil jsem to jednoduše, že jsem v PHPMyAdmin dal na sloupec email UNIQUE. To ti vlastně zakáže vkládat do sloupce email stejné(opakující se) údaje.
Michal Žůrek - misaz
Jak píše pod tebou, učím se to teprve a OOP moc neholduji. Databázový
ovladač mám ze zdejších tutoriálů, zde se taky vše učím a proto se i
zde obracím.
ale děkuji ti, kritika je nejlepší Alespoň vím na čem zapracovat
Martin Konečný (pavelco1998)
O prvním SQL vím, akorát jsem to zatím nepředělal, vše provádím na
lokálním serveru (můj pc/ntb).
S heslama bych potřeboval také poradit, když už jsme u toho. Zkoušel jsem použít SHA1, ale to mi pak nešlo přihlašování.. (Heslo to nebralo)
S chybama do pole, koukám dobrý nápad, jdu to předělat.
Tobě také děkuji.
Když ho nastavím na UNIQUE, nebo primární klíč, tak se mi sice
duplicitní záznam nepřidá, ale já spíše řeším to, aby mi to napsalo
hlášku, že tento email je již používám, nebo něco na ten způsob
$dotaz1=mysql_query("SELECT * FROM users WHERE username=$nick OR email=$e_mail");
$overeni1=mysql_fetch_array($dotaz1);
Takhle když to budu mít, tak vlastně pokud budou nějaké záznamy z tohoto dotazu, tak uživatel existuje, tudíž ho nezaregistruji a vypíšu mu chybu, s konkrétní hláškou. Takže by to mělo takhle fungovat?
Takhle už ti to vytáhne jen ty lidi, které mají daný nick a mail (v
tvém případě jednoho, když nepovolíš víc stejných jmen nebo
mailů).
Jen ty hodnoty raději dej do apostrofů, nejsem si jistý, jestli to bez nich
bude fungovat.
Nebo můžeš využít již zmiňovaný UNIQUE. To ti zabrání vložit záznam se stejnou hodnotou a ty zjistit můžeš, jestli se záznam vložil nebo ne (pomocí funkce mysql_affected_rows()) a nějak musí jít vydolovat i důvod. Možná to bude pomocí funkce mysql_error(), ale to nevím, tento ovladač už dlouho nepoužívám.
$pismeno=array("a","A","b","B","c","C","d","D","e","E","f","F","g","G","h","H","i","I","j","J","k","K","l","L","m","M","n","N","o","O","p","P","q","Q","r","R","s","S","t","T","u","U","v","V","w","W","x","X","y","Y","z","Z");
$kod=array();
for($i=1; $i<20; $i++){
$n=rand(0,50);
$char=$pismeno[$n];
$kod[$i]=$char;
$cislo=rand(0,9);
$kod[$i].=$cislo;
}
$akod=implode($kod);
$dotaz=mysql_query("SELECT * FROM users where username='".mysql_real_escape_string($_POST['nick'])."' AND email='".mysql_real_escape_string($_POST['email'])."'");
mb_internal_encoding("UTF-8");
$heslo1=mysql_real_escape_string($_POST['heslo']);
$heslo2=mysql_real_escape_string($_POST['heslo2']);
$nick=mysql_real_escape_string($_POST['nick']);
$e_mail=mysql_real_escape_string($_POST['email']);
$jmeno=mysql_real_escape_string($_POST['jmeno']);
$prijmeni=mysql_real_escape_string($_POST['prijmeni']);
$adresa="[email protected]";
$predmet="Registrace nového uživatele";
$text="Uživatel: ".$nick." se právě zaregistroval na web.<br/>";
$text.="Jméno uživatele: ".$jmeno."<br />";
$text.="Příjmení: ".$prijmeni."<br />";
$text.="Email: ".$e_mail."";
$hlavicka= 'From: Lorem-ipsum.bot';
$hlavicka2= 'From: admin';
$hlavicka.= "\nMIME-Version: 1.0\n";
$hlavicka.= "Content-Type: text/html; charset=\"utf-8\"\n";
$hlavicka2.= "\nMIME-Version: 1.0\n";
$hlavicka2.= "Content-Type: text/html; charset=\"utf-8\"\n";
$ip=$_SERVER['REMOTE_ADDR'];
$zprava="Dobrý den, <br /> děkuji za registraci ne mém webu. Ještě než se budete moci přihlásit, musíte svůj účet aktivovat.";
$zprava.="<br /><br /> <br />";
$zprava.="Váš aktivační kód je:".$akod." <br />";
$chyby = array();
if (empty($jmeno)) {
$chyby[] = "Nebylo vyplněno jméno";
}
if (empty($heslo1)) {
$chyby[] = "Nebylo vyplněno heslo";
}
if (empty($heslo2)) {
$chyby[] = "Nebylo vyplněnné druhé heslo";
}
if (empty($e_mail)) {
$chyby[] = "Nebyl vyplněn e-mail";
}
if (empty($nick)){
$chyby[]="Nebylo vyplněnné jméno";
}
$dotaz1=mysql_query("SELECT * FROM users WHERE username='$nick' OR email='$e_mail'");
$overeni1=mysql_num_rows($dotaz1);
$overeni1_1=mysql_fetch_array($dotaz1);
if ($overeni1_1['email']==$e_mail){
$chyby[]="Uživatel s tímto emailem již existuje";
}
if ($overeni1_1['username']==$nick){
$chyby[]="Uživatel s touto přezdívkou již existuje";
}
if ($heslo1!==$heslo2){
$chyby[]="Zadaná hesla se neshodují";
}
if ($chyby){
echo '<script>alert("V registrace nebylo možné pokračovat z níže uvedených důvodů: \n';
foreach ($chyby as $chyba) {
echo '-'.$chyba.'\n';
}
echo '");</script>';
}
if ($overeni1==0){
$heslo=md5($heslo1);
mysql_query("
INSERT INTO users (username, email, pw, firstname, lastname, ip, datum, akt_kod)
VALUES ('$nick', '$e_mail', '$heslo', '$jmeno', '$prijmeni', '$ip', '".time()."', '$akod')
");
mb_send_mail($adresa, $predmet, $text, $hlavicka);
mb_send_mail($e_mail, "Aktivace uživatelského účtu", $zprava, $hlavicka2);
if (mysql_error()){
echo mysql_error();
}
}
Takhle to funguje jak má.
No děkuji za pomoc. Teď mám dotaz ohledně toho hashování hesel, můžu zde, nebo mám založit jiný topic?
Výhodou toho pole s chybami je, že můžeš testovat kdykoliv, jestli není
prázdné. Osobně bych to udělal tak, že projdu podmínky pro samotný
formulář (vyplněné údaje, správný formát, správná délka, ...), pak
zkontroluji, jestli pole není prázdné a teprve potom dávám dotazy do
DB.
Řešil bych to asi takhle:
<?php
$chyby = array();
// prvně kontrola, zda pole vůbec něco obsahuje (= jestli byl odeslán formulář)
if ($_POST) {
if (empty($_POST["jmeno"])) {
$chyby[] = "Nebylo vyplněno jméno";
}
if (empty($_POST["heslo"])) {
$chyby[] = "Nebylo vyplněno heslo";
}
if (empty($_POST["heslo2"])) {
$chyby[] = "Nebylo vyplněnné druhé heslo";
}
if (empty($_POST["email"])) {
$chyby[] = "Nebyl vyplněn e-mail";
}
if (empty($_POST["nick"])){
$chyby[] = "Nebylo vyplněnné jméno";
}
// prvně zkontroluji, jestli data poslaná přes formulář jsou validní
// pokud jsou OK, mohu postupovat dál
if (empty($chyby)) {
$nick = mysql_real_escape_string($_POST["nick"]);
$email = mysql_real_escape_string($_POST["email"]);
$heslo = $_POST["heslo"];
$hesloOvereni = $_POST["heslo2"];
$dotazUzivatel = mysql_query("
SELECT `username`, `email`
FROM `users`
WHERE `username` = '{$nick}' OR `email` = '{$email}'
LIMIT 1
");
$existuje = (bool) mysql_num_rows($dotazUzivatel); // pokud vrátí 0, automaticky bude FALSE, pokud 1 a víc, automaticky bude TRUE
// pokud uživatel existuje, uložíme chybu
if ($existuje) {
$chyby[] = "Takový uživatel existuje.";
}
// nyní znovu zkontrolujeme stav chyb
if (empty($chyby)) {
$uzivatel = mysql_fetch_assoc($dotazUzivatel);
if ($uzivatel["email"] == $email) {
$chyby[] = "Uživatel s tímto emailem již existuje.";
}
if ($uzivatel["username"] == $nick) {
$chyby[] = "Uživatel s tímto nickem již existuje.";
}
if (empty($chyby)) {
if ($heslo !== $hesloOvereni) {
$chyby[] = "Zadaná hesla se neshodují.";
}
if (empty($chyby)) {
$hesloHash = nejaka_hash_funkce($heslo);
mysql_query("
INSERT INTO `users` (`username`, `email`, `pw`, ...)
VALUES ('{$nick}', '{$email}', '{$heslo}', ...)
");
mb_send_mail($email, "predmet", "zprava");
// přesměrování a ukončení scriptu
header("location:ucet.php");
exit;
}
}
}
}
}
?>
<h1>Registrace uživatele</h1>
<?php
if (!empty($chyby)) {
echo "<ul>";
foreach ($chyby as $chyba) {
echo "<li>{$chyba}</li>";
}
echo "</ul>";
}
?>
<form>
...
</form>
Obecně platí, že by jakékoliv zpracování mělo být na začátku
souboru před jakýmkoliv výstupem (např. vykreslení HTML).
Proto se formulář zpracovává nad jeho vykreslením, chyby se ukládají
postupně do pole. V případě chyby se stránka načte znovu a pod nadpisem se
vypíší všechny chyby. V případě úspěchu se přesměruje na soubor
ucet.php a script se ukončí.
Nevím, zda by to takhle fungovalo, psal jsem to z hlavy. Je to jen takový
příklad a ukázka, jak bych to udělal já.
Větvení je tam taky hodně, někdy se tomu úplně vyhnout nedá. Když už
ale musím hodně rozvětvovat, snažím se to dělat přehledně.
Další věc je, že pokud není vyplněné např. jméno ve formuláři, pak $_POST["jmeno"] neexistuje, tudíž by ti to mělo hodit NOTICE na řádku, kde máš:
$jmeno=mysql_real_escape_string($_POST['jmeno']);
Názvy tabulek a sloupců se píší se zpětnými apostrofy. To se dělá kvůli tomu, aby nedošlo ke kolizi názvu a klíčového slova. Samotné hodnoty se pak píší s klasickými apostrofy.
Kdyby ještě něco, dej vědět.
Pokud to patří k tématu, napiš to klidně sem.
No je to sporné, ale patří to i sem. Zde mi to i ostatní vytýkali, že používám md5.
Takže pro registraci a přihlašování používám hash funkci md5, ale ta už není bezpečná a je snadno prolomitelná. No tak jsem si řekl dobře, použiji jinou fci, třeba sha1. Tak jsem vše upravil, co bylo třeba (registrace a přihlašování), ale poté jsem se nikdy už nepřihlásil, protože se mi heslo zadané ve formuláři a heslo v db neshodovali, tak mě to nepřihlásilo.
Jasně, protože hesla v DB jsou uložena podle md5(), ale na přihlášení
už používáš jinou funkci. Budeš muset vytvořit nová hesla a zahashovat
je podle nové funkce.
Obvykle se k heslu přidává ještě tzv. sůl. Používám pro to funkci
hash_hmac() a SHA512.
$heslo = hash_hmac("SHA512", "heslo", "sůl");
Takže pokud to dobře chápu.
Registrace:
$heslo=hash_hmec("SHA512", "$overeneHeslo", "asd123456");
No a login by vypadal jak?
Teď vypadá:
$dotaz2=mysql_query("select * from users where aktivovan=1 AND username= '$login' AND pw = '$md5heslo'");
a $md5heslo:
$md5heslo=md5($heslo)
Je možné, že přihlašování bude vypadat:
$hashHeslo=hash_hmac("SHA512", "$heslo", "asd123456")
a $hahsHeslo dosadím do dotazu:
$dotaz2=mysql_query("select * from users where aktivovan=1 AND username= '$login' AND pw = '$hashHeslo'");
U registrace a přihlášení musíš mít použitý stejný hash. Takže použiješ stejnou funkci jak při registraci uživatele, tak i jeho přihlášení.
Pokud jsem tě správně pochopil, tak jo
Obvykle jako sůl používám jméno uživatele, protože je unikátní. Ale
nejsem si jist, jestli je to úplně OK řešení.
Moc jsem nepobral, co tím máš na mysli. První způsob určitě ne, protože pro sůl je určený jen třetí parametr.
Nemáš zač. Když tak se ptej... každopádně doporučuji projít ten tutorial na PDO.
Bohužel nemám práva na zamykání témat
Ahoj. Jestli můžu, zkus se podívat na toto videjko z jedné z přednášek Michala Špačka, který je právě k problematice ukládání hesel. https://www.youtube.com/watch?…
Ahoj
Díky, určitě se podívám.
Ale jak je zde psáno, funguje to tak, jak jsme se zde zmiňovali.
Zobrazeno 28 zpráv z 28.