Chroń zachowania serwera PHP przed podatnością na wstrzyknięcie SQL

Ostatnia aktualizacja 27 kwi 2021

Problem

Niektóre bazy danych umożliwiają wysyłanie wielu poleceń SQL w jednym zapytaniu. W związku z tym istnieje potencjalne ryzyko bezpieczeństwa podczas przekazywania parametrów w ciągu zapytania do dynamicznie generowanego zapytania bazy danych.Hakerzy mogą próbować modyfikować zmienne adresu URL lub formularza w zapytaniu dynamicznym poprzez dołączenie złośliwych instrukcji SQL do istniejących parametrów. Taki atak często jest określany nazwą SQL injection. Niektóre elementy kodu zachowania serwera Dreamweaver można zmienić tak, aby zmniejszyć ryzyko ataków SQL injection. Więcej informacji na temat ataków SQL injection podano w tym artykule w Wikipedii. (Nr ref. 201713)

Uwaga: Problem opisany w tej notatce technicznej został naprawiony w aktualizacji Dreamweaver 8.0.2.

Ta notatka techniczna obejmuje zachowania serwera PHP programu Dreamweaver. Istnieją osobne notatki techniczne dla zachowań serwera ColdFusion, ASP VBScript, ASP JavaScript i JSP. Zachowania serwera ASP.NET nie są narażone.

Rozwiązanie

Jeśli w ciągu zapytania jest przekazywany parametr, zadbaj o to, by przesyłane były jedynie oczekiwane informacje. Adobe utworzyło aktualizację Dreamweaver 8.0.2, która zmniejsza ryzyko ataków wstrzyknięcia SQL. Te poprawki zostaną włączone do wszystkich przyszłych wydań programu Dreamweaver. Po aktualizacji programu Dreamweaver 8 należy ponownie zastosować zachowania serwera na stronach, które z nich korzystają, i ponownie wdrożyć te strony na serwerze.

Pozostała część tej notatki technicznej dokumentuje sposób ręcznej edycji kodu programu Dreamweaver MX 2004 w celu zapobiegania atakom iniekcji SQL przy użyciu modelu serwera PHP.Zachowania serwera podatne na te ataki są wymienione poniżej wraz z poprawkami:

Zestaw rekordów z filtrem

Niefiltrowane zestawy rekordów nie muszą być modyfikowane, ale filtrowane — tak. W poniższym przykładzie zestaw rekordów Dreamweaver MX 2004 o nazwie „rs_byDate" jest filtrowany przez wartość daty przekazaną z parametru adresu URL.Wyróżniony kod poniżej to to, co wymaga zmiany:

<?php $colname_rs_byDate = "1"; if (isset($_GET['relDate'])) { $colname_rs_byDate = (get_magic_quotes_gpc()) ? $_GET[&apos;relDate&apos;] : addslashes($_GET[&apos;relDate&apos;]); } mysql_select_db($database_dreamqa2, $dreamqa2); $query_rs_byDate = sprintf(&quot;SELECT * FROM albums WHERE relDate = &apos;%s&apos;&quot;, $colname_rs_byDate); $rs_byDate = mysql_query($query_rs_byDate, $dreamqa2) or die(mysql_error()); $row_rs_byDate = mysql_fetch_assoc($rs_byDate); $totalRows_rs_byDate = mysql_num_rows($rs_byDate); ?>

Aby zabezpieczyć zestaw rekordów przed atakami typu SQL injection, na górze strony należy utworzyć niestandardową funkcję GetSQLValueString(). Ta funkcja weryfikuje typ danych parametru zapytania. Po utworzeniu funkcji należy zaktualizować kod zestawu rekordów, aby używał funkcji GetSQLValueString().

Przejdź do drugiej linii w widoku kodu, zaraz po pliku dołączenia dla połączenia, i dodaj funkcję GetSQLValueString() na początku kodu w następujący sposób:

<?php if (!function_exists("GetSQLValueString")) { function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") { $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue; $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue); switch ($theType) { case "text": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "long": case "int": $theValue = ($theValue != "") ? intval($theValue) : "NULL"; break; case "double": $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL"; break; case "date": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "defined": $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue; break; } return $theValue; } } ?>

Usuń kod magic quotes, aby ukośniki wsteczne nie były niepoprawnie dodawane do danych GET i POST, gdy magic quotes są wyłączone na serwerze PHP.

Oryginalny kod:

$colname_rs_byDate = (get_magic_quotes_gpc()) ? $_GET['relDate'] : addslashes($_GET['relDate']);

Zmodyfikowany kod:

$colname_rs_byDate = $_GET['relDate'];



Zaktualizuj kod zestawu rekordów, aby utworzyć ciąg SQL przy użyciu funkcji GetSQLValueString() utworzonej powyżej. Zaktualizuj wartość zmiennej $query_rs_byDate:

Oryginalny kod:

$query_rs_byDate = sprintf("SELECT * FROM albums WHERE relDate = '%s'", $colname_rs_byDate);

Zmodyfikowany kod zabezpieczający:

$query_rs_byDate = sprintf("SELECT * FROM albums WHERE relDate = %s", GetSQLValueString($colname_rs_byDate, "date"));

Zachowania serwera Insert, Update i Delete Record oraz kreator Record Insertion Form

Zachowania serwera Insert, Update i Delete Record oraz kreator Record Insertion Form już używają funkcji GetSQLValueString(), więc muszą zostać udoskonalone, aby zapobiec iniekcjom SQL.Jedyne potrzebne zmiany znajdują się w funkcji GetSQLValueString().

Oto oryginalny kod. Linia do zmiany jest podświetlona na żółto:

if (!function_exists("GetSQLValueString")) { function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") {$theValue = (!get_magic_quotes_gpc()) ? addslashes($theValue) : $theValue; switch ($theType) { case "text": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "long": case "int": $theValue = ($theValue != "") ? intval($theValue) : "NULL"; break; case "double": $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL"; break; case "date": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "defined": $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue; break; } return $theValue; } }

Zaktualizuj definicję zmiennej $theValue:

Oryginalny kod:

$theValue = (!get_magic_quotes_gpc()) ? addslashes($theValue) : $theValue;

Zmodyfikowany zabezpieczony kod:

$theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue; $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue);

Zachowanie serwera Zalogowanie użytkownika

Oto kroki naprawienia zachowania serwera Zaloguj się użytkownika:

Utwórz funkcję GetSQLValueString() zgodnie z opisem w sekcji Zestaw rekordów z filtrem powyżej.

Zmodyfikuj kod, który konstruuje zapytanie SQL logowania.



Oryginalny kod:

$LoginRS__query=sprintf("SELECT username, password FROM login WHERE username='%s' AND password='%s'", get_magic_quotes_gpc() ? $loginUsername : addslashes($loginUsername), get_magic_quotes_gpc() ? $password : addslashes($password));

Zmodyfikowany zabezpieczony kod:

$LoginRS__query=sprintf("SELECT username, password FROM login WHERE username=%s AND password=%s", GetSQLValueString($loginUsername, "text"), GetSQLValueString($password, "text"));

Zachowanie serwera Sprawdzenie nowego użytkownika

Zachowanie serwera Sprawdzenie nowego użytkownika wymaga, aby najpierw dodać do strony zachowanie serwera Wstawienie rekordu. Funkcja GetSQLValuesString() jest dołączona do zachowania serwera Insert Record.Jedyne co trzeba zrobić to aktualizacja GetSQLValuesString() w celu lepszego obsłużenia iniekcji SQL i aktualizacja zachowania serwera Sprawdź nowego użytkownika, aby używało tej funkcji gdy parametr jest przekazywany.

Zmień pierwszą linię w funkcji, aby zaktualizować funkcję GetSQLValuesString() zgodnie z opisem w sekcji Aktualizacja definicji zmiennej $theValue.

Zmodyfikuj kod konstruujący zapytanie SQL logowania, aby używał GetSQLValuesString() do przekazywania parametrów. W górnej części strony znajdź kod w sekcji zaczynającej się od // *** Redirect if username exists.



Oryginalny kod:

$LoginRS__query = "SELECT username FROM login WHERE username='" . $loginUsername . "'"



Zmodyfikowany zabezpieczony kod:

$LoginRS__query = sprintf("SELECT username FROM login WHERE username=%s", GetSQLValueString($loginUsername, "text"));

Zestaw stron Dane podstawowe

W obiekcie aplikacji Zestaw stron Dane podstawowe zmiany dotyczą głównie zestawu rekordów tworzonego przez program Dreamweaver dla strony ze szczegółami. Jeśli nie na stronie podstawowej nie ma odfiltrowanego zestawu rekordów, nie muszą być na niej wprowadzane żadne zmiany. Jeśli masz filtrowany zestaw rekordów, zobacz zestaw rekordów z filtrem, aby zaktualizować kod zestawu rekordów.

Dreamweaver tworzy filtrowany zestaw rekordów na stronie szczegółów, więc kod tego zestawu rekordów musi zostać zaktualizowany, aby używał funkcji GetSQLValueString() do przekazywania parametrów i sprintf() do konstruowania ciągu SQL.

Utwórz funkcję GetSQLValueString() zgodnie z opisem w sekcji Zestaw rekordów z filtrem powyżej.

Zaktualizuj kod deklaracji zmiennej i skonstruuj zapytanie SQL używając funkcji GetSQLValuesString().



Oryginalny kod:

$recordID = $_GET['recordID']; $query_DetailRS1 = "SELECT * FROM albums WHERE ID = $recordID";



Zmodyfikowany zabezpieczony kod:

$colname_DetailRS1 = "-1"; if (isset($_GET['recordID'])) { $colname_DetailRS1 = (get_magic_quotes_gpc()) ? $_GET['recordID'] : addslashes($_GET['recordID']); } $query_DetailRS1 = sprintf("SELECT * FROM albums WHERE ID = %s", GetSQLValueString($colname_DetailRS1, "int"));

Kreator formularza aktualizacji rekordu

Zaktualizuj definicję zmiennej $theValue zgodnie z opisem w sekcji Zachowania serwera Wstaw, Aktualizuj i Usuń rekord oraz kreator formularza wstawiania rekordów.
Zaktualizuj kod zestawu rekordów wymagany do formularza aktualizacji rekordów zgodnie z opisem w kroku 2 dotyczącym zestawu rekordów z filtrem.

Dodatkowe informacje

Więcej informacji na temat wstrzyknięć SQL można znaleźć w następującym artykule: Artykuł w Wikipedii na temat wstrzyknięć SQL.

Biuletyn bezpieczeństwa Adobe: APSB 06-07 Luka w zabezpieczeniach wstrzyknięcia SQL w zachowaniach serwera Dreamweaver.