Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [MySQL][PHP] filtrowanie danych z formularzy - bezpieczeństwo.
Forum PHP.pl > Forum > Przedszkole
Doody
Kiedyś napisałem prosty CRM który działał ale był skrajnie niebezpieczny, tzn nie ma żadnego filtrowania danych przesyłanych z formularzy ani też tych zapisywanych w bazie.
Teraz zająłem się właśnie kwestią bezpieczeństwa aby mój twór stał się w końcu przydatny.

Na pierwszy ogień poszła kwestia panelu logowania i tu pytanie: czy sprawdzenia hasła i loginu jak poniżej jest Waszym zdaniem wystarczająco bezpieczne:
  1. <?php
  2. include('db_con.php'); //łączy z serwerem i wybiera bazę danych
  3. if(isset($_POST['login']) || isset($_POST['haslo'])){ //sprawdza czy hasło lub login zostały wprowadzone i przesłane metodą POST
  4. session_start(); //rozpoczyna sesję
  5. $zapytanie = "SELECT * FROM biz_user WHERE p_login = '".trim($_POST['login'])."'"; // wybiera z bazy MySQL rekordy z komórką p_login równą podanemu loginowi
  6. if(preg_match('/^[a-zA-Z0-9.\-_]+\@[a-zA-Z0-9.\-]+\.[a-z]{2,4}$/D', trim($_POST['login'])) && mysql_num_rows(mysql_query($zapytanie)) > 0){ // sprawdza czy ilość znalezionych rekordów jest większa od zera oraz czy login będący adresem e-mail jest zgodny z odpowiadającym mu wyrażeniem regularnym
  7. $zapytanie = "SELECT * FROM biz_user WHERE p_login = '".$_POST['login']."'&& p_haslo = '".$_POST['haslo']."'"; // wybiera z bazy MySQL rekordy z komórką p_haslo równą podanemu hasłu i komórką p_login równą podanemu loginowi
  8. if(preg_match('/[^\/\'\`\"\n\s\\\\]{2,20}$/D', $_POST['haslo']) && mysql_num_rows(mysql_query($zapytanie)) > 0){ // sprawdza czy ilość znalezionych rekordów jest większa od zera
  9. $zapytanie = "SELECT * FROM biz_user WHERE p_login = '".$_POST['login']."' LIMIT 1"; // pobiera używane w sesji dane użytkownika
  10. while($wiersz=mysql_fetch_array(mysql_query($zapytanie))){
  11. $_SESSION['user_id'] = $wiersz['p_id']; //zapisuje ID użytkownika w zmiennej sesyjnej
  12. $_SESSION['user'] = $wiersz['p_imie'].' '.$wiersz['p_nazwisko']; //zapisuje imię i nazwisko użytkownika w zmiennej sesyjnej
  13. $_SESSION['status'] = $wiersz['p_status']; //zapisuje ststus użytkownika w zmiennej sesyjnej
  14. break;
  15. }
  16. $_SESSION['zalogowany'] = true;
  17. header("Location: lista_firm.php");
  18. }
  19. else{
  20. header('Location: index.php?log_error=2');
  21. }
  22. }
  23. else{
  24. header('Location: index.php?log_error=1');
  25. }
  26. }
  27. else{
  28. <center>
  29. <br />
  30. <br />
  31. <img src="picture/logo.png">
  32. <br />
  33. <br />
  34. <h3>Logowanie do systemu</h3>
  35. ';
  36. if (isset($_GET['log_error'])){
  37. $log_error=$_GET['log_error'];
  38. if ( $log_error == 1 ) echo '<b>Nie istnieje taki użytkownik! </b><br />';
  39. if ( $log_error == 2 ) echo '<b>Podane hasło jest nieprawidłowe! </b><br />';
  40. }
  41. <form action="index.php" method="post">
  42. <br />
  43. Login: <input type="text" name="login" size="30"/><br/>
  44. <br />
  45. Hasło: <input type="password" name="haslo" size="30"/><br/>
  46. <br />
  47. <input class="przycisk" type="submit" value="WEJŚCIE" /><br/>
  48. </form>
  49. </center>
  50. ';
  51. }
  52. ?>


Zakładam że loginem jest adres e-mail a hasło nie może zawierać znaków \ / ' ` " \n \s


redeemer
Nie jest w ogóle bezpieczne. W linii 5 i 7 masz błędy typu SQL injection. Dodatkowo zapytanie w linii 9 jest zupełnie niepotrzebne, bo przecież te dane już raz dostałeś w linii 7.
Doody
Czy teraz jest ok?
  1. if(isset($_POST['login']) || isset($_POST['haslo'])){
  2. if(!preg_match('/^[a-zA-Z0-9.\-_]+\@[a-zA-Z0-9.\-]+\.[a-z]{2,4}$/D', trim($_POST['login'])))
  3. header('Location: index.php?log_error=1');
  4. else
  5. $clean['login']=trim($_POST['login']);
  6. if(!preg_match('/[^\/\'\`\"\n\s\\\\]{2,20}$/D', trim($_POST['haslo'])))
  7. header('Location: index.php?log_error=1');
  8. else
  9. $clean['haslo']=trim($_POST['haslo']);
  10.  
  11. $zapytanie = "SELECT * FROM biz_user WHERE p_login = '".$clean['login']."'";
  12. if(mysql_num_rows(mysql_query($zapytanie)) > 0){
  13. $zapytanie = "SELECT * FROM biz_user WHERE p_login = '".$clean['login']."'&& p_haslo = '".$clean['haslo']."'";
  14. if(mysql_num_rows(mysql_query($zapytanie)) > 0){
  15. while($wiersz=mysql_fetch_array(mysql_query($zapytanie))){
  16. $_SESSION['user_id'] = $wiersz['p_id'];
  17. $_SESSION['user'] = $wiersz['p_imie'].' '.$wiersz['p_nazwisko'];
  18. $_SESSION['status'] = $wiersz['p_status'];
  19. break;
  20. }
  21. $_SESSION['zalogowany'] = true;
  22. header("Location: lista_firm.php");
  23. }
  24. else{
  25. header('Location: index.php?log_error=2');
  26. }
  27. }
  28. else{
  29. header('Location: index.php?log_error=1');
  30. }
  31. }


Wkleiłem tylko część odpowiedzialną za przetwarzanie danych.
ber32
Witam.
Nie
http://pl.wikipedia.org/wiki/SQL_injection
Cytat
redeemer

podał rozwiązanie lepiej

PDO
Doody
Ale czy rzeczywiście tej kod jest ciągle podatny na SQL_injection. Przecież wyrażenia regularne eliminują możliwość przedostania się do zapytania niebezpiecznych znaków.
ber32
Przeanalizuj te trzy linijki

  1. $clean['login']=trim($_POST['login']);
  2. if(!preg_match('/[^\/\'\`\"\n\s\\\\]{2,20}$/D', trim($_POST['haslo'])))
  3.  
  4. $zapytanie = "SELECT * FROM biz_user WHERE p_login = '".$clean['login']."'";

i zobacz jak twój kod działa
foxbond
Nie wystarczy przypadkiem stare, dobre htmlspecialchars() i addslashes()
Doody
Zupełnie nie rozumiem dlaczego mam analizować akurat te trzy podane przez Ciebie linie kodu wyjęte z kontekstu.

  1. if(!preg_match('/^[a-zA-Z0-9.\-_]+\@[a-zA-Z0-9.\-]+\.[a-z]{2,4}$/D', trim($_POST['login'])))
  2. header('Location: index.php?log_error=1');
  3. else
  4. $clean['login']=trim($_POST['login']);
  5.  
  6. if(!preg_match('/[^\/\'\`\"\n\s\\\\]{2,20}$/D', trim($_POST['haslo'])))
  7. header('Location: index.php?log_error=1');
  8. else
  9. $clean['haslo']=trim($_POST['haslo']);


Wydaje mi się że jeśli string pobrany z formularza nie odpowiada wyrażeniu regularnemu to zostanie wykonany tylko header i nic więcej, a zmiennej $clean['login'] zostanie przypisana wartość zmiennej trim($_POST['login']) tylko jeśli login jest zgodny ze schematem. Czy się mylę i dlaczego?
ber32
Sryy
Cytat
SQL Injection (z ang., dosłownie zastrzyk SQL) – luka w zabezpieczeniach aplikacji internetowych polegająca na nieodpowiednim filtrowaniu lub niedostatecznym typowaniu i późniejszym wykonaniu danych przesyłanych w postaci zapytań SQL do bazy danych. Podatne są na niego systemy złożone z warstwy programistycznej (przykładowo skrypt w PHP, ASP, JSP itp.) dynamicznie generującej zapytania do bazy danych (MySQL, PostgreSQL itp.). Wynika on zwykle z braku doświadczenia lub wyobraźni programisty.
Doody
Jestem początkujący ale co oznacza SQL Injection wiem.
Rozumiem że skoro się wypowiadasz, to jesteś bardziej zaawansowany. Jeśli możesz wskaż po prostu proszę gdzie jest ta luka, bo jestem za głupi żeby zrozumieć twoje zdawkowe odpowiedzi. Cytowanie mi def. SQL Injection nic nie wnosi do tematu.
Turson
W liniach 5,7,9 masz kod podany na SQLi, z resztą już koledzy wyżej powiedzieli.
Twoje filtrowanie ograniczyło się do trim(), czyli o dużo za mało.4

  1. function filtruj($zmienna)
  2. {
  3. $zmienna=trim($zmienna);
  4. $zmienna=htmlspecialchars($zmienna);
  5. $zmienna=addslashes($zmienna);
  6. }

Będzie bezpieczniej.

Najlepiej zastosuj PDO BindValue()
gitbejbe
Musisz się jeszcze dużo uczyć

Po pierwsze :
if(isset($_POST['login']) || isset($_POST['haslo'])) -- wystarczy sprawdzić czy istnieje submit

session_start(); -- dałeś dopiero po warunku , dlaczego ? Później powstają tematy, że sesja nie działa na wszystkich stronach. Otwieraj sesje najlepiej na początku dokumentu. Wiem, ze to logowanie i po logowaniu ta strona nie bedzie dostepna, ale moze robisz ten sam błąd w innych dokumentach

$zapytanie = "SELECT * FROM biz_user WHERE p_login = '".trim($_POST['login'])."'"; -- napisałeś, że znasz się na atakach typu sql. Nie znasz sie ani troche. Przestudiuj dokładnie o co z nimi chodzi a zobaczysz jak powazną luke masz w tym miejscu. Funckja trim przed niczym Cię tutaj nie chroni.

jeśli jesteś początkujący, to jeśli piszesz sprawdzenie jakiegoś formularza to zacznij najpierw od zdefiniowania zmiennych, np:

$login = mysql_escape_string($_POST['login']);
$haslo = "$_POST['haslo'];

Jeśli nie uzywasz PDO, to przeflitruj dodatkowo login przez wyrażenie regularne - to chyba najpewniejszy sposób.
Dopiero po sprawdzeniu zmiennych tworzysz warunek, gdzie jesli obie te zmienne sa ok , to dopiero pobierasz dane z bazy exclamation.gif!

Hasła nie musisz sprawdzać. Każdy powinien miec takie hasło jakie chce.

Z bazy danych sprawdzac tylko czy jest taki login w bazie i pobierasz jego hasło. Jeśli hasło zgadza sie z tym podanym z formularza, to tworzysz sesje
Doody
Cytat(gitbejbe @ 16.09.2013, 10:46:37 ) *
Musisz się jeszcze dużo uczyć


To nie podlega dyskusji.

Cytat(gitbejbe @ 16.09.2013, 10:46:37 ) *
Po pierwsze :
if(isset($_POST['login']) || isset($_POST['haslo'])) -- wystarczy sprawdzić czy istnieje submit


Tak też zrobię.

Cytat(gitbejbe @ 16.09.2013, 10:46:37 ) *
session_start(); -- dałeś dopiero po warunku , dlaczego ? Później powstają tematy, że sesja nie działa na wszystkich stronach. Otwieraj sesje najlepiej na początku dokumentu. Wiem, ze to logowanie i po logowaniu ta strona nie bedzie dostepna, ale moze robisz ten sam błąd w innych dokumentach

W pozostałych plikach mam start sesji na początku. dokładnie wygląda to tak:
  1. <?php
  2. if(!isset($_SESSION['zalogowany']))
  3. header("Location: index.php");
  4. ?>


Cytat(gitbejbe @ 16.09.2013, 10:46:37 ) *
$zapytanie = "SELECT * FROM biz_user WHERE p_login = '".trim($_POST['login'])."'"; -- napisałeś, że znasz się na atakach typu sql. Nie znasz sie ani troche. Przestudiuj dokładnie o co z nimi chodzi a zobaczysz jak powazną luke masz w tym miejscu. Funckja trim przed niczym Cię tutaj nie chroni.

jeśli jesteś początkujący, to jeśli piszesz sprawdzenie jakiegoś formularza to zacznij najpierw od zdefiniowania zmiennych, np:

$login = mysql_escape_string($_POST['login']);
$haslo = "$_POST['haslo'];

Jeśli nie uzywasz PDO, to przeflitruj dodatkowo login przez wyrażenie regularne - to chyba najpewniejszy sposób.
Dopiero po sprawdzeniu zmiennych tworzysz warunek, gdzie jesli obie te zmienne sa ok , to dopiero pobierasz dane z bazy exclamation.gif!

Nie napisałem że znam się na atakach typu sql. Napisałem że wiem co to jest atak typu SQL Injection, to spora różnica.
Natomiast jeśli spojrzysz na mój drugi post, zauważysz że właśnie tak zrobiłem. Login jest przefiltrowany przez wyrażenie regularne, a dopiero później zmienna $clear['login'] umieszczona w zapytaniu do bazy.

Cytat(gitbejbe @ 16.09.2013, 10:46:37 ) *
Hasła nie musisz sprawdzać. Każdy powinien miec takie hasło jakie chce.

Z bazy danych sprawdzac tylko czy jest taki login w bazie i pobierasz jego hasło. Jeśli hasło zgadza sie z tym podanym z formularza, to tworzysz sesje

Dzięki - rzeczywiście wystarczy porównać.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.