Strona logowania i brak możliwości zalogowania się

0

Dzień dobry w Nowym 2020 Roku.
Tak, przegrzebałam gugla i to dziesiątki razy, znalazłam metody strukturalne, obiektowe, PDO. Obejrzałam kilka filmików na jutubie. Spędziłam naście jak nie dziesiąt godzin klepiąc, poprawiając, testując i patrząc co wylezie w logach bazy danych php-fpm i bazy danych. Przejrzałam - z nadzieją - przyklejony na górze działu wątek ze Skryptami php ale nie znalazłam nic o logowaniu się do aplikacji WWW.
I jestem ciągle na początku - nie działa a wg paru tutoriali powinno.
Problem: po zalogowaniu nie widzę abym była zalogowana, nie wyświetla się pobrany login z bazy jako login zalogowanego użytkownika.

Apache: 2.4.41
PHP. 7.3.12
MariaDB: 10.3.20
Linux: Fedora 31

Żeby nie było - robię to dla siebie, to nie jest żadne zaliczenie do żadnej szkoły i nikt mi za to nie zapłaci.

Poniżej po kolei: struktura bazy danych i tabeli w niej, html z formularzem, i kolejne istotne pliki PHP.

Tabela:

+-----------------------------+-----------+----------------------+----------------------------------+
| login                       | imie      | nazwisko             | pass                             |
+-----------------------------+-----------+----------------------+----------------------------------+
| jankowalski                 | Jan       | Kowalski             | 02ca50c0dc0680dd7325064d979d3f83 |
| januszwisniewki             | Janusz    | Wiśniewski           | 7e55e7adf9224de1d161ad1782baecf9 |
| grzegorzbrzeczyszczykiewicz | Grzegorz  | Brzęczyszczykiewicz  | 3e0ea140b7f6117df5cd574c72e7b196 |
| marcinkromchalski           | Marcin    | Krochmalski          | a61561482cba57209cd23031f23225cc |
| adambednarz                 | Adam      | Bednarz              | d41d8cd98f00b204e9800998ecf8427e |
| ireneuszkosmiderski         | Ireneusz  | Kośmiderski          | c4ca4238a0b923820dcc509a6f75849b |
| gustawwalczynski            | Gustaw    | Walczyński           | c81e728d9d4c2f636f067f89cc14862c |
| annanowakowska              | Anna      | Nowakowska           | eccbc87e4b5ce2fe28308fd9f2a7baf3 |
| katarzynaotowska            | Katarzyna | Otowska              | a87ff679a2f3e71d9181a67b7542122c |
| test                        | test      | testowy              | test                             |
| ts                          | test      | testowy              | 4d682ec4eed27c53849758bc13b6e179 |
| janek                       | Janek     | kowalczyk            |                                  |
+-----------------------------+-----------+----------------------+----------------------------------+

Formularz:

<html>
<body>
<form action="form.php" method="post">
<tr>
<td>Login: </td><td> <input type="text" style="border:1px solid #000000" name="user" /></td>
<td>Pass: </td><td><input type="text" style="border:1px solid #000000" name="pass" /></td>
<td></td><td><input type="submit" style="border:1px solid #000000" value="Login" /></td>
</tr>
<br /><br />
</form>
</body>
</html>

form.php - dostaje dane z formularza

<?php
    $dbhost     = "localhost";
    $dbname     = "site1";
    $dbuser     = "site1";
    $dbpass     = "site1";

    try {
        $db = new PDO('mysql:host=localhost;dbname=site1;charset=utf8mb4', 'site1', 'site1');
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);  
    } catch (PDOException $e) {
        echo "Connection failed : ". $e->getMessage();
    }

    $username = trim($_POST['user']);
    $password = trim($_POST['pass']);
    $msg = "";
    if($username == "") {
        echo "Brak podanego loginu!";
//        header("Location: http://localhost/testsec/form.html");
        die();
    }
    
    try {
        $query = "select * from admin where login='$username' and pass='$password'";
        $stmt = $db->prepare($query);
        $stmt->bindParam('username', $username, PDO::PARAM_STR);
        $stmt->bindValue('password', $password, PDO::PARAM_STR);
        $stmt->execute();
        $count = $stmt->rowCount();
        $row =   $stmt->fetch(PDO::FETCH_ASSOC);
        echo '<pre>'; print_r($array); echo '</pre>';

        if ($count == 1 && !empty($row)) { 
            $_SESSION['sess_user_id'] = $row['id'];
            $_SESSION['sess_user_name'] = $row['login'];
            $_SESSION['sess_name'] = $row['nazwisko'];
        } else {
            $msg = "Invalid username and/or password";
        }
    } catch (PDOException $e) {
        echo "Error :".$e->getMessage();
    }
    // debug:
    echo $username.'<br />';
    echo $password.'<br />';
    
    // jeśli się uda to przekierowuję
    header("Location: home.php");

home.php

<?php
    session_start();
    if(isset($_SESSION['sess_user_id']) && $_SESSION['sess_user_id'] != "") {
        echo '<h2>Witaj '.$_SESSION['sess_name'].'</h2>';
        echo '<h4><a href="logout.php">Logout</a>';
    } else {
        header('location: form.html');
    }
?>

index.php

<?php
    session_start();
    if(isset($_SESSION['sess_user_id']) && $_SESSION['sess_user_id'] != "") {
        echo '<h2>Witaj '.$_SESSION['sess_name'].'</h2>';
        echo '<h4><a href="logout.php">Logout</a>';
    } else {
        header('location: form.html');
    }
?>

logout.php

<?php
    session_start();
    $_SESSION['sess_user_id'] = "";
    $_SESSION['sess_username'] = "";
    $_SESSION['sess_name'] = "";
    if (empty($_SESSION['sess_user_id'])) header("location: form.html");
?>
0

Pierwsze co mi do głowy przychodzi to to, że hasła masz zahashowane w bazie, a nie hashujesz hasła z formularza. Chociaż są 2 wyjątki: hasło dla usera test nie jest hashowane, a dla usera janek w ogóle nie istnieje^^

EDIT:
Bindowanie parametrów masz źle:

$query = "select * from admin where login='$username' and pass='$password'";
$stmt = $db->prepare($query);
$stmt->bindParam('username', $username, PDO::PARAM_STR);
$stmt->bindValue('password', $password, PDO::PARAM_STR);

A powinno być coś takiego:

$query = "select * from admin where login=:username and pass=:password";
$stmt = $db->prepare($query);
$stmt->bindParam(':username', $username, PDO::PARAM_STR);
$stmt->bindValue(':password', $password, PDO::PARAM_STR);

https://www.php.net/manual/en/pdostatement.bindparam.php

0

@serek - dzięki. Wiedziałam, że o czymś zapomnę a więc:

  • hasła dla janka nie ma celowo aby przetestować logowanie bez hasła
  • jeden z userów ma niehashowane hasło aby początkowo ominąć ewentualne dodatkowe problemy z hashowaniem
    Poprawiłam kod i teraz dostaję:
[01-Jan-2020 11:55:56 UTC] PHP Parse error:  syntax error, unexpected ':' in /var/www/html/testsec/form.php on line 28

Czyli czepia się właśnie tych zmian, które zaproponowałeś.

0

Możesz spróbować też wersji ze znakami zapytania:

$query = "select * from admin where login = ? and pass = ?";
$stmt = $db->prepare($query);
$stmt->bindParam(1, $username, PDO::PARAM_STR);
$stmt->bindValue(2, $password, PDO::PARAM_STR);
0

Nie startujesz sesji przed ustawieniem jej w form.php; nie ma nic na zmiennej $array.
Poza tym utworzyłbym nowy plik init.php, w którym startujesz raz sesje, nawiazujesz polaczenie z baza danych i includujesz go do każdego pliku

0

DZIĘKI!!! :)
Działa :) Poprawiony kod poniżej - dla potomnych i innych szukajacych :)

<?php
    session_start();
    $dbhost     = "localhost";
    $dbname     = "site1";
    $dbuser     = "site1";
    $dbpass     = "site1";

    try {
        $db = new PDO('mysql:host=localhost;dbname=site1;charset=utf8mb4', $dbuser, $dbpass );
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);  
    } catch (PDOException $e) {
        echo "Connection failed : ". $e->getMessage();
    }
    $username = trim($_POST['user']);
    $password = trim($_POST['pass']);
    $msg = "";
    if($username == "") {
        echo "Brak podanego loginu!";
//        header("Location: http://localhost/testsec/form.html");
        die();
    }
    
    try {
        $query = "select * from admin where login = ? and pass = ?";
        $stmt = $db->prepare($query);
        $stmt->bindParam(1, $username, PDO::PARAM_STR);
        $stmt->bindValue(2, $password, PDO::PARAM_STR);
        $stmt->execute();
        $count = $stmt->rowCount();
        $row =   $stmt->fetch(PDO::FETCH_ASSOC);
        echo '<pre>'; print_r($row); echo '</pre>';

        if ($count == 1 && !empty($row)) { 
            $_SESSION['sess_user_id'] = $row['id'];
            $_SESSION['sess_user_name'] = $row['login'];
            $_SESSION['sess_name'] = $row['imie'];
        } else {
            $msg = "Invalid username and/or password";
        }
    } catch (PDOException $e) {
        echo "Error :".$e->getMessage();
    }
    
    echo $username.'<br />';
    echo $password.'<br />';
    
    // jeśli się uda to przekierowuję
    header("Location: home.php");
?>

A na koniec jeszcze jedno pytanie - require/_once czy include dla pliku z configiem bazy danych?
I dlaczego?

0

_once unika powtórnego załadowania pliku w kodzie tzn. powielenia tego samego
include w razie jeśli brakuje pliku, to dostajesz błąd ale kod dalej sie wykonuje
require w razie jeśli brakuje pliku, to dostajesz piekny fatal error i kod dalej się nie wykonuje

require_once będzie najbezpieczniejsze

0
[01-Jan-2020 13:44:27 UTC] PHP Warning:  Use of undefined constant dbuser - assumed 'dbuser' (this will throw an Error in a future version of PHP) in /var/www/html/testsec/config.php on line 2
[01-Jan-2020 13:44:27 UTC] PHP Warning:  Use of undefined constant dbpass - assumed 'dbpass' (this will throw an Error in a future version of PHP) in /var/www/html/testsec/config.php on line 3
[01-Jan-2020 13:44:27 UTC] PHP Warning:  Use of undefined constant dbname - assumed 'dbname' (this will throw an Error in a future version of PHP) in /var/www/html/testsec/config.php on line 4
[01-Jan-2020 13:44:27 UTC] PHP Warning:  Use of undefined constant dbhost - assumed 'dbhost' (this will throw an Error in a future version of PHP) in /var/www/html/testsec/config.php on line 5
[01-Jan-2020 13:44:27 UTC] PHP Notice:  Undefined variable: dbuser in /var/www/html/testsec/form.php on line 6
[01-Jan-2020 13:44:27 UTC] PHP Notice:  Undefined variable: dbpass in /var/www/html/testsec/form.php on line 6

Niezależnie od tego czy require_once czy include nie chce mi czytać pliku config.php, kod form.php (początek):

I dostaję błąd z phpa z PDO oczywiście:

Error :SQLSTATE[3D000]: Invalid catalog name: 1046 No database selected
<?php
    session_start();
        require_once "config.php";

    try {
        $db = new PDO('mysql:$dbhost,$dbname,charset=utf8mb4', $dbuser, $dbpass );
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);  
    } catch (PDOException $e) {
        echo "Connection failed : ". $e->getMessage();
    }

config.php

<?php
define (dbuser, "site1");
define (dbpass, "site1");
define (dbname, "site1");
define (dbhost, "localhost");
?>

Co robię źle i jak podstawić zawartość pobranych zmiennych z config.php do form.php?
Dlaczego PDO nie chce przyjąć zmiennych?

0

stałych używa się inaczej:

<?php
define('DB_HOST', 'a');
$dbname = 'b';

$test = "mysql:host=".DB_HOST.",dbname={$dbname},charset=utf8mb4";

echo $test;
0

Dokładnie jak przedmówca napisał, odczyt stały następuje po wpisaniu jego nazwy bez znaku $.
Według dobrych praktyk stałe piszemy zawsze wielkimi literami a zamiast spacji używamy znaku _. (na końcu pliku PHP nie używamy już ?>)

<?php

define (DB_USER, 'site1');
define (DB_PASS, 'site1');
define (DB_NAME, 'site1');
define (DB_HOST, 'localhost');

<?php
    session_start();
    require_once 'config.php';

    try {
        $db = new PDO(sprintf('mysql:host=?;dbname=?;charset=utf8mb4', DB_HOST, DB_NAME), DB_USER, DB_PASS);
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);  
    } catch (PDOException $e) {
        echo "Connection failed : ". $e->getMessage();
    }

W prezentowanym kodzie jest poważny błąd logiczny

$db = new PDO('mysql:$dbhost,$dbname,charset=utf8mb4', $dbuser, $dbpass );

Ponieważ string w PDO jest pomiędzy apostrofami, zmienne nie zostaną podmienione na wartości tylko potraktowane jako zwykły string. Zalecam używanie sprintf itp.
Dodatkowo zostały usunięte parametry "host= ... dbname=".

Przy zapytaniach SQL radzę unikać wstrzykiwania bezpośrednio zmiennych, zwłaszcza jak są one pobierane z tablicy $_POST, jest to miejsce podatne na ataki sql injection.

0

Udało sie i działa - dzielę się działającym kodem. Nie radząc sobie z tym PDO do końca, wspomogłam się jeszcze raz guglem i wynalazłam również to:
, czas: 4:34
form.php

<?php
    require_once "cfgdb.php";
    session_start();
    try {
        $db = new PDO("mysql:host=$dbhost;dbname=$dbname",$dbuser,$dbpass);
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);  
    } catch (PDOException $e) {
        echo "Connection failed : ". $e->getMessage();
    }
    $username = trim($_POST['user']);
    $password = trim($_POST['pass']);
    $msg = "";
    if($username == "") {
        echo "Proszę o wpisanie loginu - <a href='form.html'>powrót do strony logowania</a>.";
        header("refresh:5;url=form.html");
        die();
    }
    try {
	$query = "select * from admin where login = ? and pass = ?";
	$stmt = $db->prepare($query);
	$stmt->bindParam(1, $username, PDO::PARAM_STR);
	$stmt->bindValue(2, md5($password), PDO::PARAM_STR);
        $stmt->execute();
        $count = $stmt->rowCount();
	$row =   $stmt->fetch(PDO::FETCH_ASSOC);
        if ($count == 1 && !empty($row)) { 
            $_SESSION['sess_user_id'] = $row['id'];
            $_SESSION['sess_user_name'] = $row['login'];
	    $_SESSION['sess_name'] = $row['imie'];
	    // jeśli się uda to przekierowuję
	    header("Location: home.php");
        } else {
            $color = "<div style='background-color:red;color:white;padding:1%;'><font size=10px><b>";
            $msg = "Nieprawidłowy login i/lub hasło użytkownika";
            $endcolor = "</b></font></div><br />";
            echo $color,$msg,$endcolor;
            header("refresh:6;url=form.html");
        }
    } catch (PDOException $e) {
        echo "Error :".$e->getMessage();
    }
?>
<?php
    $dbhost = "localhost";
    $dbuser = "site1";
    $dbname = "site1";
    $dbpass = "site1";
?>

Dzięki za pomoc :)
P.S. @PinguVanEx - a jak ten zmieniony kod ^^?

0

@Katrina: skoro pytasz, to zrobie szybkie code review (tak wiem, ze dopiero zaczynasz)

<?php
    $dbhost = "localhost";
    $dbuser = "site1";
    $dbname = "site1";
    $dbpass = "site1";
?>
  • do tego rodzaju danych, na których nie będziemy w kodzie operować zalecam używanie stałych
  • na końcu pliku nie używamy ?>
<?php
    require_once "cfgdb.php"; 
    session_start();
    try {
        $db = new PDO("mysql:host=$dbhost;dbname=$dbname",$dbuser,$dbpass);
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);  
    } catch (PDOException $e) {
        echo "Connection failed : ". $e->getMessage(); **zalecam użycie sprintf do łączenia stringów
        **brakuje zatrzymania aplikacji wraz z wyjątkiem (throw new ...)
    }
    $username = trim($_POST['user']); **warto dodać domyślną wartość w przypadku braku klucza user ($_POST['user'] ?? '')
    $password = trim($_POST['pass']); **warto dodać domyślną wartość w przypadku braku klucza pass ($_POST['user'] ?? '')
    $msg = ""; **po co jest ta zmienna?
    if($username == "") { **jeżeli dodamy wartość domyślną możemy również sprawdzać już typ $username === "" albo użyć wielniejszej ale pewnej funkcji  if(empty($username))
        echo "Proszę o wpisanie loginu - <a href='form.html'>powrót do strony logowania</a>.";
        header("refresh:5;url=form.html"); **przy aktualnym kodzie może być błąd nagłówka ponieważ w przypadku wystąpienia błedu PDO zostanie już wyświetlona treść na ekranie (moża też zrobić złego hack-a z ob_start)
        die();
    }
    try {
    $query = "select * from admin where login = ? and pass = ?";
    $stmt = $db->prepare($query);
    $stmt->bindParam(1, $username, PDO::PARAM_STR);
    $stmt->bindValue(2, md5($password), PDO::PARAM_STR); **hasło w md5 to zły pomysł, polecam bcrypt (https://www.php.net/manual/en/function.password-hash.php)
        $stmt->execute();
        $count = $stmt->rowCount();
    $row =   $stmt->fetch(PDO::FETCH_ASSOC);
        if ($count == 1 && !empty($row)) { **$count === 1, czy instrukcja warunkowa nie jest bez sensu? jezeli krotka nie istnieje (row) to count bedzie 0
            $_SESSION['sess_user_id'] = $row['id'];
            $_SESSION['sess_user_name'] = $row['login'];
        $_SESSION['sess_name'] = $row['imie'];
        // jeśli się uda to przekierowuję
        header("Location: home.php"); 
        } else {
            $color = "<div style='background-color:red;color:white;padding:1%;'><font size=10px><b>";
            $msg = "Nieprawidłowy login i/lub hasło użytkownika";
            $endcolor = "</b></font></div><br />";
            echo $color,$msg,$endcolor;
            header("refresh:6;url=form.html");
        }
    } catch (PDOException $e) {
        echo "Error :".$e->getMessage();
    }
?>
0

Dzięki za pomoc :)
Na razie rozwijam aplikacyjkę i md5 chwilowo musi wystarczyć (tylko dla) moich testów, zaszyfrowanie haseł zostawiłam "na potem".

@marchewa - SQL Injection to dla mnie temat dość istotny jak to dla "adminki" którą jestem i do tego tematu będę wracała kiedy tylko skończę robić jak to się nazywa? CRUD? Swoich danych w aplikacyjce.

1 użytkowników online, w tym zalogowanych: 0, gości: 1