Najpewniejszym sposobem jest zawsze stosowanie zapytania parametryzowanego lub przygotowanych wyrażeń (prepared statements), a NIE formułowanie zapytań poprzez prostą konkatenację ciągów z wartościami użytkownika.
W przypadku bardziej złożonych scenariuszy (dynamiczny SQL w procedurach) należy stosować specjalne mechanizmy eskalacji i whitelisting (ścisłe filtrowanie) parametrów.
cursor.execute('SELECT * FROM users WHERE login = %s', [login])
EXECUTE format('SELECT * FROM %I WHERE id = $1', tablename) USING id;
Tutaj %I bezpiecznie podstawia tylko nazwy tabel/kolumn.
Czy następujące podejście jest bezpieczne przy wstawianiu liczby do ciągu?
EXECUTE 'SELECT * FROM users WHERE id = ' || user_input;
NIE! Nawet jeśli spodziewasz się liczby, napastnik może wprowadzić kod:
1; DROP TABLE users; — zapytanie zostanie wykonane. Dlatego zawsze używaj placeholderów i wyraźnego sprawdzenia typu wejściowych parametrów.
Historia 1
Formularz wyszukiwania pracowników budował warunki WHERE poprzez konkatenację ciągów po stronie aplikacji. Napastnik wprowadził: ' OR 1=1 --, co spowodowało wyświetlenie danych wszystkich pracowników. Później przez podobne podejście usunięto sekcję danych. Wniosek: konkatenacja istotnych parametrów — zawsze ryzyko.
Historia 2
W administracyjnym interfejsie wyboru raportu nazwa tabeli pobierana była z pola użytkownika i wstawiana do zapytania SQL. Użytkownik mógł wprowadzić nieprawidłową nazwę i uzyskać dostęp do tabel systemowych. Po tym zezwolono tylko na ściśle określone nazwy (whitelisting).
Historia 3
W projekcie e-commerce podczas dynamicznego sortowania użytkownicy przekazywali nazwę pola przez parametr GET. Haker podstawił price; DELETE FROM orders; --, co doprowadziło do utraty wszystkich zamówień. Filtrowanie dozwolonych nazw kolumn i stosowanie zapytań parametryzowanych rozwiązało problem.