Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book

WordPress wypuszcza aktualizację – SQL injection

01 listopada 2017, 13:12 | Aktualności | komentarzy 8

Wczoraj udostępniona została aktualizacja WordPressa (4.8.3), która po raz drugi łata problemy z SQL injection (poprzednia nieudana – jak się okazało – próba była w 4.8.2).

Dziwi też zachowanie załogi WordPressa, która początkowo na zgłoszenie odpowiadała zgodnie z zasadą „spieprzaj dziadu”. Czy mamy gdzieś Twoje zgłoszenie, nie poprawimy. Dopiero groźba tzw. full disclosure (który zresztą macie w pierwszym linku tego posta) zadziałał na wyobraźnię deweloperów WordPressa i zabrali się za łatanie (które zajęło… 6 tygodni).

Do rzeczy – obecnie zalecaną i niezależną od języka programowania metodą ochrony przed podatnością SQL injection jest użycie zapytań parametryzowanych. To jakaś magiczna sztuczka chroniąca przed SQLi? ;-) Oczywiście nie – trzeba jeszcze przy tym chwilę pomyśleć. Przykład zapytania parametryzowanego:

$stmt = $dbh->prepare("SELECT * FROM users WHERE USERNAME = ? AND PASSWORD = ?");
$stmt->execute(array($username, $pass));

Jest ono bezpieczne, ale co się stanie jeśli zrobimy coś takiego?

$stmt = $dbh->prepare("SELECT * FROM users WHERE USERNAME = ? AND PASSWORD = ? $dodatkowa_wartosc_pochodzaca_od_uzytkownika");
$stmt->execute(array($username, $pass));

Stanie się tyle, że nasza aplikacja jest podatna na SQL injection. Wniosek – nie przekazywać żadnych dynamicznych wartości pochodzących od użytkownika do ->prepare (!).

Co z WordPressem? Tutaj z jakiegoś powodu twórcy wytworzyli własną funkcję odpowiedzialną za zapytania parametryzowane. Działa ona na zasadzie match & replace. Czyli np. %s podstawiany jest odpowiednią wartością. Podatny przykład z WordPress core (/wp-includes/meta.php):

if ( $delete_all ) {
  $value_clause = '';
  if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value ) {
    $value_clause = $wpdb->prepare( " AND meta_value = %s", $meta_value );
  }
  $object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $type_column FROM $table WHERE meta_key = %s $value_clause", $meta_key ) );
}

W linijce numer 4,  %s zostanie podmieniony (z odpowiednim escapowaniem mającym chronić przed SQLi) na $meta_value – wartość kontrolowaną przez użytkownika.  Ale co się stanie jeśli do $meta_value podstawimy %s ? W linijce 6-stej spowoduje to możliwość kontrolowania przez nas zapytania – czyli SQL injection!

To tak zwany double prepare query, którego należy unikać. Bardziej rozbudowany przykład, pokazany krok po kroku zobaczycie w oryginalnym badaniu.

Jak się chronić?

Dla większości użytkowników (na razie) wystarczy zaktualizować WordPressa do wersji 4.8.3 (chyba że używamy pluginu, który nadpisuje $wpdb – wtedy również należy go zaktualizować).

Bardziej pewna i bezpieczna zasada dotyczy developerów (pluginy!):

The simple fix is to not pass user input to the $query parameter to WPDB::prepare() in meta.php. Passing user input to $query is always wrong. Full stop.

Czyli wracamy do naszego pierwszego zalecenia:

nie przekazywać żadnych dynamicznych wartości pochodzących od użytkownika do ->prepare (!).

–Michał Sajdak

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

  1. qvwvp

    Trochę dziwne że właściciel tak popularnej aplikacji robi takie błedy. Podczas gdy normaly kowalski który bawi się na xampp wie że tak się nie robi…

    Odpowiedz
  2. Problem WP, że klepaną nowe wersji tak szybko, że nie mogą przetestować ich przed prezentacja. A wtedy mamy różne luki…

    Odpowiedz
    • wk

      To jest taki ogólny problem większości developerów ;-)

      Odpowiedz
  3. pawel

    Czyli poniższy sposób:

    $dane_z_post = $_POST[’post’];

    $zapytanie1= „SELECT * from drukarki WHERE
    serial=’$dane_z_post'”;

    $wynik = $pdo->prepare($zapytanie1);

    $wynik->execute();

    Jest poprawny czy nie ?

    Odpowiedz
    • Niepoprawne.

      Odpowiedz
    • as

      musisz uzyc bind do zmiennych pobieranych od usera i dopiero pozniej prepare. to co zrobiles jest dokladnie tym przeciw czemu przestrzega artykul. parametryzacja tutaj nic nie daje.

      Odpowiedz
  4. kazik

    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

    $query = $db->prepare(„SELECT * from drukarki WHERE
    serial=’:serial'”);
    $query->bindValue(’:serial’, $_POST[’serial’]);
    $query->execute();

    Odpowiedz
  5. Kamil

    Miałem zhakowaną stronę. Wyczyściłem ją i włączyłem tryb debugowania. Dostaję taki komunikat:

    Notice: Funkcja wpdb::prepare została wywołana nieprawidłowo. Zapytanie oczekiwało tylko jednego elementu zastępowanego, a wysłano tablicę. Dowiedz się więcej: Debugowanie w WordPressie. (Ten komunikat został dodany w wersji 4.9.0.) in […]/public_html/wp-includes/functions.php on line 4161

    Zastanawiam się o co tu chodzi. Może podmienić plik functions.php na plik z oryginalnej paczki?

    Odpowiedz

Odpowiedz