Bezpośrednie modyfikowanie superglobali

20

Widziałem ludzi (którzy zazwyczaj piszą dobry kod) bezpośrednio zmieniających $_POSTtablicę za pomocą takiego kodu:

// Add some value that wasn't actually posted
$_POST['last_activity'] = time();

// Alter an existing post value
$_POST['name'] = trim($_POST['name']);

// Our pretend function
// Pass the entire $_POST array as data to work with in the function
// The function update_record() will read only the values we actually need
update_record($_POST);

// ...That sure was easier than creating a new array 
//  with only the $_POST values we actually need.

Ma to sens, że update_record()nie należy bezpośrednio uzyskiwać dostępu do $ _POST, więc możemy na przykład przekazać do niego inne tablice danych, ale z pewnością jest to leniwy, zły projekt, a może po prostu zły? Nadal jednak podajemy prawidłową tablicę update_record(), więc po co tworzyć nową?

To nie jest sedno pytania, tylko przykład użycia. Słyszałem jednak, że wiele osób mówi, że nie należy tego robić z $_REQUESTdanymi, a to zła praktyka. Ale dlaczego? Wygląda wystarczająco nieszkodliwie.

Przykłady:

  • Ustawienie domyślnej $_GET(lub opublikowanej) wartości, która tak naprawdę nie istnieje

  • Dodawanie $_POSTwartości, które nie zostały faktycznie opublikowane po przesłaniu formularza

  • Bezpośrednie odkażanie lub filtrowanie $_GETwartości tablic lub kluczy na bardzo wczesnym etapie skryptu (awaryjne zabezpieczenia ... dlaczego nie?)

  • $_POSTRęczne ustawianie wartości przed przesłaniem formularza w celu wypełnienia danych wejściowych wartością domyślną (gdy dane wejściowe odczytują $_POSTwartość domyślną; zrobiłem to)

  • Tworzysz własne $_SERVERwartości? Jasne, hej czemu nie?

  • A co z innymi, jak $_COOKIEi $_SESSION? Oczywiście musimy modyfikować te bezpośrednio, prawda? Więc dlaczego nie inni?

Czy bezpośrednia modyfikacja superglobali nigdy nie powinna być wykonywana, czy też jest w niektórych przypadkach w porządku ?

Wesley Murch
źródło
Zgadzam się z numerami 1, 2 i 3, ponieważ jest to nieoczekiwane użycie (zwłaszcza nr 1 i 2).
Kevin Peno,
Dobre pytanie. Modyfikowanie globalnych tablic jest błędne w ten sam sposób, przy użyciu niewłaściwych wartości globalnych. Również te tablice mają swój cel (przekazywanie parametrów z zewnątrz), co sprawia, że ​​zmienianie ich jest prostym sposobem na bałagan w kodzie. Uważam jednak, że niektóre z tych tablic mogą zostać zdezynfekowane na początku skryptu, aby nie powodować problemów w kodzie.
Tadeck
1
Używam otoki tablic wejściowych OO (niejawne filtrowanie), które wypisują dodatkowe powiadomienie, gdy zmienne $ _GET lub $ _POST zostaną zmienione. Nadal jest to możliwe, ale powinno być ograniczone do wąskich sytuacji. (Sygnalizacja między modułami, chociaż powinien jej potrzebować tylko dyspozytor / kontroler z przodu).
Mario
@mario: Chciałbym usłyszeć więcej o tym, jak to osiągnąłeś, jeśli możesz spojrzeć na to pytanie: stackoverflow.com/questions/12954656
Wesley Murch

Odpowiedzi:

16

Biorąc pod uwagę, że PHP już ustawia te superglobale, nie sądzę, że modyfikowanie ich jest złe . W niektórych przypadkach może to być najlepszy sposób rozwiązywania problemów ... szczególnie w przypadku kodu innej firmy, którego nie można łatwo zmodyfikować. (Mogą używać $_GETbezpośrednio lub zakładać, że istnieje jakiś klucz $_SERVERitp.)

Jednak ogólnie mówiąc, myślę, że jest to zła praktyka, gdy piszesz swój własny kod. Modyfikowanie $_REQUESTdanych za pomocą filtra „za kulisami”, który jest automatycznie uruchamiany na każdej stronie, może spowodować skutki uboczne. (Zobacz wszystkie problemy spowodowane przez „magiczne cytaty” na dowód).

Więc jeśli nie zamierzasz tego robić (automatycznie filtrować superglobale), to następujące nie daje żadnych korzyści:

$_POST['foo'] = filter($_POST['foo']);

kiedy możesz łatwo zrobić:

$foo = filter($_POST['foo']);

Myślę, że to znacznie bardziej jasne wprowadzić rozróżnienie site-wide że $_POSTi $_GETzawsze niefiltrowane, niezaufane dane, a oni powinni nigdy być używane jak jest.

Kopiując odfiltrowaną wartość do innej zmiennej, twierdzisz: „Rozumiem, co robię ... Przefiltrowałem te dane wejściowe i można z nich bezpiecznie korzystać”.

konforce
źródło
Dzięki za wkład, mój internet był wyłączony przez prawie 2 dni, więc nie miałem okazji nikomu odpowiedzieć. W tym przykładzie zmodyfikowałem $ _POST i użyłem go jako tablicy danych do przekazania do funkcji aktualizacji, zakładając, że istnieje kilka innych kluczy $ _POST, które będziemy czytać w tej funkcji. Wolałbym stworzyć nową tablicę, ale widziałem, jak ludzie to robią, więc nadal nie jestem pewien, czy nazwać to „złym kodem”, ale myślę, że przynajmniej tak jest. Myślę, że za każdym razem, gdy poczujesz potrzebę zrobienia tego, zawsze jest lepszy sposób.
Wesley Murch
1
@Wesley, głównym powodem, dla którego jest „zły”, jest to, że znacznie bardziej prawdopodobne jest, że zapomnisz oczyścić niektóre dane użytkownika. W twoim przykładzie modyfikujesz jeden klucz, a następnie przekazujesz całą tablicę. Co się stanie, jeśli niektóre z tych danych zawierają złośliwe dane wejściowe, które nie są przetwarzane? O wiele lepiej jest zbudować tę nową tablicę ręcznie, kopiując do niej tylko to, czego potrzebujesz $_POST, odkażając w miarę postępów. A jeśli chodzi o innych ludzi, którzy to robią ... cóż, wiele osób pisze bardzo zły kod PHP, ale to też nie jest dla ciebie wymówką. :)
konforce
Myślę, że powinienem był podkreślić inne zastosowania nadużyć superglobalnych oprócz odkażania danych. Prawdopodobnie powinienem był to całkowicie pominąć i napisać jaśniejsze pytanie, ludzie szczególnie lubią podchodzić do kwestii bezpieczeństwa i często ignorują resztę. Aby nie brzmieć niewdzięcznie, naprawdę doceniam opinie. Któregoś dnia poczekam i dam temu znak zaznaczenia, ponieważ poruszyłeś kilka dobrych punktów, ale przegapiłeś głosowanie w ciągu 3 minut, kiedy pytanie było nowe :) Jeszcze raz dziękuję!
Wesley Murch
9

Zasadniczo sugerowałbym, aby nie modyfikować predefiniowanych superglobali, aby było jasne, co to są oczyszczone dane, a co surowe / niezaufane dane.

Inni mogą sugerować, że jeśli posprzątasz superglobale na początku cyklu żądania, nie musisz się o nie martwić gdzie indziej.

Zawsze dopasowuję je, kiedy ich potrzebujesz:

$id = (int)$_POST['id'];

lub podobne.

Pod względem pozostałych zmiennych jest to dobra praktyka, aby nie pisać do każdego z $_GET, $_POST, $_REQUEST, $_SERVERlub $_COOKIE. $_SESSIONjest jednak inny, ponieważ często chcesz zapisywać dane w sesji, które następnie są utrwalane dla różnych żądań w sesji.


źródło
2
Więcej powodów, dla których tego rodzaju globały powinny odejść, zastąpione obiektami / metodami, które można wywołać, aby uzyskać je w razie potrzeby. Dlaczego setcookieistnieje, ale otrzymujemy pliki cookie $_COOKIE? Ponadto, ponieważ $_COOKIEjest ustawiany tylko w momencie rozpoczęcia bieżącej sesji i nigdy nie jest aktualizowany, wymaga zmiany / ustawienia plików cookie w obu obszarach, aby późniejsze obszary kodu miały aktualne informacje.
Kevin Peno
Dzięki James, przez jakiś czas byłem offline, więc nie mogłem odpowiedzieć. Krótko mówiąc - zgadzam się z tobą. Zawsze jest lepsze rozwiązanie niż pisanie na post / get / etc, ale wciąż nie jestem pewien, czy jest to uważane za zły pomysł, jak w „ nigdy nie rób tego nigdy ”. Tak więc, jeśli ponownie napotkam ten typ kodu, czy uważasz, że mam prawo „wywoływać” go na niechlujnym kodzie, czy może czasami można go użyć w sprytny, bezpieczny sposób?
Wesley Murch
@Wesley Jeśli byłoby to „nigdy nie rób tego nigdy”, superglobale byłyby prawdopodobnie tylko do odczytu - nie są. Po prostu nazwałbym to złą praktyką ustawiania lub zastępowania ich w kodzie aplikacji - z tych powodów.
Michel Feldheim,
3

Powinieneś tego unikać. Może kiedyś zapomniałeś coś odkażać, a potem możesz odzyskać niebezpieczne dane. Jeśli skopiujesz dane do nowej struktury podczas dezynfekcji

  • Dostajesz tylko to, czego chcesz / potrzebujesz, a nie to, co jest w $_POSTśrodku
  • Prawdopodobnie wystąpi błąd, jeśli w nowo utworzonej tablicy brakuje niektórych kluczy lub w ogóle jej brakuje

Dodatkowe inne skrypty mogą zakładać, że tablica jest nietknięta i może reagować z ciekawością.

KingCrunch
źródło
2

Nigdy nie podobał mi się pomysł modyfikowania superglobalu, ponieważ wprowadza w błąd. Jest to szybki, zuchwały sposób na zrobienie czegoś, co prawdopodobnie będzie lepszym sposobem.

Jeśli zmienisz $_POSTna przykład wartość, oznacza to, że oprogramowanie otrzymało dane, których nie otrzymało.

PRAWDZIWY PROBLEM

W rzeczywistej sytuacji staje się to dużym problemem:

Wyobraź sobie, że pracujesz w zespole. W idealnym świecie wszyscy używają tej samej składni, ale nie żyjemy w idealnym świecie. Jeden programista, John, lubi uzyskiwać dostęp do opublikowanych danych za pomocą $_POST. Zmienia coś w post varsach:

$_POST['ranking'] = 2; // John has changed ranking from 1 to 2 for whatever reason

Następnie masz innego programistę, Chris, który woli korzystać filter_inputz dostępu do wprowadzonych danych (tj. GET, POST, SERVER, COOKIE), ponieważ zapewnia ochronę oprogramowania podczas przetwarzania danych, z którymi użytkownik może manipulować. W swojej części oprogramowania musi uzyskać wartość końcową ranking. Jego część kodu to PO napisie Johna.

$ranking = filter_input(INPUT_POST, 'ranking', FILTER_SANITIZE_NUMBER_INT);
// $ranking = 1

Z powyższego przykładu, zmieniając superglobal, złamałeś PHP. John ustawił wartość $_POST['ranking']na 2 z dowolnego powodu, ale teraz Chris otrzymał wartość 1

Gdy nie widziałem innego sposobu:

Pracowałem nad projektem, który wykorzystywał wordpress jako swojego bloga za równoważeniem obciążenia AWS. To zmienia wartość $_SERVER['remote_address']. W tym przypadku inny programista nie miał innego wyboru, jak tylko wykonać następujące czynności:

if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
    $_SERVER['REMOTE_ADDR'] = $parts[0];
}

Wniosek

Jest prawie na pewno lepszy sposób niż zmiana superglobali

Luke Madhanga
źródło
1

Myślę, że prawdziwe pytanie brzmi „dlaczego powinieneś modyfikować motyw?”. Nie widzę żadnego ważnego powodu, aby to zrobić. Jeśli chcesz zdezynfekować imput, możesz użyć zmiennej lokalnej…

O ile kod nie jest wystarczająco krótki (powiedzmy, mniej niż 50 wierszy), modyfikacja tych superglobalnych tylko utrudniłaby utrzymanie i przechowywanie kodu.

Nawiasem mówiąc, nie musisz przekazywać $ _POST do funkcji, ponieważ jest to superglobalna tablica, do której można uzyskać dostęp nawet w lokalnym zakresie funkcji.


źródło
3
Ale powinien to przekazać. W przeciwnym razie jest to bardzo trudne do przetestowania i nie jest możliwe wywołanie funkcji / metody z innymi wartościami bez (nawet bardziej
brzydkich
To zależy od tego, co robi jego metoda. Jeśli jest przeznaczony tylko do analizowania wszystkiego, co znajduje się w tablicy $ _POST, nie musi go przekazywać. Oczywiście, jeśli służy to bardziej ogólnemu / abstrakcyjnemu celowi, masz rację.
2
@Thomas, zgadzam się z Kingiem tutaj. Nawet jeśli są globalne, nie należy używać globalnego czegokolwiek w innych zakresach, ponieważ powoduje to ścisłe sprzężenie (dlatego funkcja nie może być ponownie użyta). Biorąc pod uwagę twój przykład, jeśli funkcja ma dezynfekować dane, dlaczego dezynfekuje tylko $_POSTdane? Przekazanie $_POSTpowoduje, że funkcja odkaża wszelkie dane.
Kevin Peno
0

Po wstępnej odpowiedzi na to pytanie stwierdzeniem, że nie powinno być powodu, aby modyfikować superglobale, edytuję tę odpowiedź na przykładzie, w którym postanowiłem.

Obecnie pracuję nad tabelą bazy danych przepisywania adresów URL, w której requestkolumna kieruje użytkownika do odpowiedniej targetkolumny.

Na przykład requestmoże być blog/title-herei targetmoże być blog.php?id=1.

Ponieważ blog.phpoczekuje $_GETzmiennych i nie chcę tego zmieniać header("Location:"), pozostaję robić coś takiego:

$uri    = explode('?', $uri_request)[0];
$params = explode('?', $uri_request)[1];
parse_str($params, $_GET);

Spowoduje to utworzenie $_GETtablicy zawierającej zamierzone parametry przekazane przez targetkolumnę.

Ostatecznie zdecydowanie odradzam modyfikowanie superglobali, chyba że absolutnie musisz .

rybo111
źródło