Безпосередня модифікація суперглобалів


20

Я бачив, як люди (які зазвичай пишуть хороший код) безпосередньо змінюють $_POSTмасив таким кодом:

// 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.

update_record()Має сенс, що не слід отримувати доступ до $ _POST безпосередньо, тому ми можемо передавати до нього інші масиви даних, але, напевно, це ледачий, поганий дизайн чи, можливо, просто неправильний? Однак ми все ще передаємо дійсний масив update_record(), тож навіщо створювати новий?

Це не питання, а лише приклад використання. Однак я чув, як багато людей кажуть, що цього не слід робити з $_REQUESTданими, і це погана практика. Але чому? Виглядає досить нешкідливо.

Приклади:

  • Встановлення значення за замовчуванням $_GET(або повідомлення), яке насправді не існує

  • Додавання $_POSTзначень, які насправді не були розміщені після надсилання форми

  • Безпосередньо дезінфікувати або фільтрувати $_GETзначення масивів або ключі дуже рано на сценарії (резервна санітарія ... чому б і ні?)

  • Встановлення $_POSTзначення вручну перед подачею форми для заповнення вводу зі значенням за замовчуванням (коли вхід читає $_POSTйого за замовчуванням; я це зробив)

  • Формування власних $_SERVERцінностей? Звичайно, ей, чому б і ні?

  • Як щодо інших, як $_COOKIEі $_SESSION? Звичайно, ми маємо їх змінити прямо так? Тоді чому б не інші?

Чи не слід робити прямих модифікацій суперглобалів , чи це в деяких випадках нормально ?


Я погоджуюся з №1, №2 та №3, оскільки це несподіване використання (особливо №1 та №2).
Кевін Пено

Гарне питання. Змінювати глобальні масиви неправильно так само, як і використовувати глобальні значення, неправильно. Також ці масиви мають своє призначення (передача параметрів ззовні), що робить їх зміною прямим способом безладу в коді. Але я вважаю, що деякі з цих масивів можуть бути очищені на початку створення сценарію, лише щоб не виникнути проблем у коді.
Тадек

1
Я використовую обгортки вхідного масиву OO (неявна фільтрація), які друкують додаткове повідомлення, коли підробляються змінні $ _GET або $ _POST. Це все ще можливо, але слід обмежуватися вузькими ситуаціями. (Сигналізація між модулями, хоча це потребує лише диспетчер / фронт-контролер.)
Маріо,

@mario: Я хотів би дізнатися більше про те, як ви це зробили, якщо ви можете поглянути на це питання: stackoverflow.com/questions/12954656
Wesley Murch

Відповіді:


16

Зважаючи на те, що PHP вже встановлює ці суперглобали, я не думаю, що це зло, щоб їх змінювати. У деяких випадках це може бути найкращий спосіб вирішити проблеми ... особливо, коли ви маєте справу з кодом третьої сторони, який ви не можете легко змінити. (Вони можуть використовувати $_GETбезпосередньо або припускати, що в ньому існує якийсь ключ $_SERVERтощо)

Однак, загалом кажучи, я думаю, що це погана практика, коли ви пишете власний код. Модифікація $_REQUESTданих за допомогою деякого фільтру за кадром, який працює на кожній сторінці автоматично, може призвести до побічних ефектів. (Дивіться всі проблеми, які "магічні цитати" викликали для доказу.)

Тож якщо ви цього не збираєтесь (автоматично фільтруйте суперглобали), то наступне не принесе жодних переваг:

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

коли ви можете просто зробити це:

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

Я думаю , що це набагато більш ясно , щоб зробити общесистемное відмінність , яке $_POSTі $_GETбуде завжди нефільтроване, ненадійні дані, і вони не повинні ніколи бути використаний як є.

Копіюючи відфільтроване значення в іншу змінну, ви заявляєте, що "я розумію, що я роблю ... Я відфільтрував цей вхід, і це безпечно для використання".


Дякую за вклад, мій Інтернет вже майже 2 дні не працює, тому я не мав можливості відповісти нікому. У прикладі я змінив $ _POST і використовував його як масив даних для переходу до функції оновлення, припускаючи, що є кілька інших ключів $ _POST, які ми будемо читати в цій функції. Я вважаю за краще створити новий масив, але я бачив, як люди роблять це замість цього, тому я все ще трохи не впевнений, називати це чи не "поганим кодом", але думаю, що принаймні так схиляється. Я думаю, що коли б ви відчували потребу в цьому, завжди є кращий спосіб.
Веслі Мерч

1
@Wesley, головна причина, що це "погано", полягає в тому, що це набагато ймовірніше, що ви забудете провести санітацію деяких даних користувачів. У вашому прикладі ви змінюєте один ключ, а потім передаєте весь масив. Що робити, якщо частина цих даних містить зловмисні введення, які не обробляються? Набагато краще створити цей новий масив вручну, скопіювавши в нього лише те, що вам потрібно $_POST, і дезінфікувати їх. А щодо інших людей, які роблять це ... ну, багато людей пишуть дуже поганий PHP-код, але це теж не привід для вас. :)
konforce

Я думаю, я мав би наголосити на інших застосуваннях зловживань суперглобалів, окрім просто оздоровлення даних. Я, мабуть, мав би це взагалі залишити і написав чітке запитання, люди особливо люблять брати участь у аспектах безпеки та часто ігнорувати решту. Щоб не звучати невдячно, я дуже ціную відгуки. Я зачекаю одного дня і дам цей прапорець, оскільки ви звернулися до деяких хороших пунктів, але пропустили партію голосування протягом 3 хвилин, коли питання було новим :) Дякую ще раз!
Веслі Мерч

9

Я, як правило, пропоную вам не змінювати заздалегідь визначені суперглобали, щоб було зрозуміло, що таке санізовані дані, а що - необроблені / ненадійні дані.

Інші можуть припустити, що якщо ви очистите надглобали на початку циклу запитів, вам не потрібно буде турбуватися про них в іншому місці.

Я б завжди відповідав їм, коли вам це потрібно:

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

або подібне.

З точки зору інших змінних , це хороша практика , щоб не писати на будь-який з $_GET, $_POST, $_REQUEST,$_SERVER або $_COOKIE. $_SESSIONоднак відрізняється тим, що вам часто хочеться записувати дані в сеанс, який зберігається в різних запитах сеансу.


2
Більше причин, чому такі види глобальних мереж повинні піти замінені об'єктами / методами, за допомогою яких можна залучати їх, коли це необхідно. Чому це setcookieіснує, але ми отримуємо файли cookie через $_COOKIE? Крім того, оскільки $_COOKIEвін встановлюється лише тоді, коли починається поточний сеанс, і ніколи не оновлюється, він вимагає змінити / встановити файли cookie в обох областях, щоб пізніші області коду мали актуальну інформацію.
Кевін Пено

Дякую, Джеймс, я деякий час був в офлайні, тому не зміг відповісти. Якщо коротко розповісти - я з вами згоден. Завжди є краще рішення, ніж писати на пост / get / тощо, але я все ще не впевнений, чи вважається це суто поганою ідеєю, як у " ніколи цього не роби ". Отже, якщо я знову натрапляю на такий тип коду, чи вважаєте ви, що я маю право "викликати їх" на неохайному коді, чи можна іноді це використовувати розумним, безпечним способом?
Веслі Мерч

@Wesley Якби це було "ніколи цього не роби", суперглобали, ймовірно, суворо лише для читання - вони не є. Я б просто назвав це поганою практикою встановлювати або перезаписувати їх у коді програми - з зазначених причин.
Мішель Фельдхайм

3

Вам слід уникати цього. Можливо, якийсь час ви забули щось санітувати, тоді ви можете отримати небезпечні дані. Якщо ви скопіюєте дані в нову структуру під час санітарії

  • Ви отримуєте лише те, що хочете / потребуєте, а не те, що $_POSTтеж є
  • Ви, ймовірно, отримаєте помилку, якщо в новоствореному масиві відсутні деякі клавіші або він взагалі відсутній

Додаткові інші сценарії можуть припускати, що масив недоторканий і може реагувати цікаво.


2

Мені ніколи не подобалася ідея зміни суперглобалу, оскільки вона вводить в оману. Це швидкий гакітний спосіб зробити щось, що, швидше за все, буде кращим способом зробити.

Якщо ви зміните значення $_POST , наприклад, ви говорите, що програмне забезпечення отримало дані, які не зробили.

РЕАЛЬНА ПРОБЛЕМА

Існує реальна життєва ситуація, коли це стає великою проблемою:

Уявіть, що ви працюєте в команді. В ідеальному світі всі використовують один і той же синтаксис, але ми не живемо в ідеальному світі. Один розробник, Джон, любить отримувати доступ до опублікованих даних за допомогою $_POST. Він щось змінює у публікації:

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

Тоді у вас є інший розробник, Кріс, який вважає за краще використовувати filter_inputдля доступу до введених даних (тобто GET, POST, SERVER, COOKIE), оскільки він захищає програмне забезпечення при обробці даних, до яких користувач може втручатися. У своїй частині програмного забезпечення йому потрібно отримати посадову вартість ranking. Його частина коду - ПІСЛЯ Джона.

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

З наведеного вище прикладу, змінивши суперглобал, ви зламали PHP. Джон $_POST['ranking']з будь-якої причини встановив значення 2, але тепер Кріс отримав значення 1

Коли я не бачив іншого способу зробити це:

Я працював над проектом, який використовував wordpress як свій блог за балансиром навантаження AWS. Це змінює значення $_SERVER['remote_address']. У цьому випадку інший розробник не мав іншого вибору, як зробити наступне:

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

Висновок

Майже напевно є кращий спосіб, ніж змінювати суперглобали


1

Я думаю, що справжнє питання тут: «навіщо вам змінювати тему?». Я не бачу вагомих причин для цього. Якщо вам потрібно очистити імпульс, ви можете скористатися локальною змінною ...

Якщо ви не будете досить коротким (скажімо, довжиною менше 50 рядків), зміна цих супер-глобальних лише ускладнить ваш код у підтримці та підкреслення.

До речі, вам не потрібно передавати $ _POST функції, оскільки це надглобальний масив, який може отримати доступ навіть у локальній області функції.


3
Але він повинен це пройти. Інакше дуже важко перевірити і неможливо викликати функцію / метод з іншими значеннями без будь-яких (навіть потворніших) хак
KingCrunch

Ну, це залежить від того, чим займається його метод. Якщо він розроблений лише для розбору того, що є в масиві $ _POST, йому не потрібно його передавати. Звичайно, якщо він служить більш загальній / абстрактній меті, ніж ви праві.

2
@Thomas, я згоден з королем тут. Навіть якщо вони є глобальними, ви не повинні використовувати глобальне нічого в межах інших областей, оскільки це спричиняє тісне з'єднання (саме тому функцію неможливо повторно використовувати). З огляду на ваш приклад, якщо функція полягає в очищенні даних, то чому він лише санітує $_POSTдані? При переході $_POSTв функцію функціонує будь-які дані.
Кевін Пено

0

Після того як спочатку відповідав на це запитання, кажучи, що не повинно бути причин змінювати суперглобали, я редагую цю відповідь на прикладі часу, коли я вирішив.

Зараз я працюю над таблицею бази даних для перезапису URL-адрес, згідно з якою request стовпець спрямовує користувача до відповідного targetстовпця.

Наприклад, requestможе бути blog/title-hereі йогоtarget може бути blog.php?id=1.

Оскільки blog.phpочікує $_GETзмінних, і я не хочу змінювати їх header("Location:"), мені залишається робити щось подібне:

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

Це створює $_GETмасив, що містить призначені параметри, переданіtarget стовпцем.

Зрештою, я б настійно радив не змінювати суперглобали, якщо вам цього абсолютно не доведеться .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.