Непосредственно модифицирующие суперглобалы

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).
Кевин Пено
Хороший вопрос. Изменение глобальных массивов является неправильным, так же как использование глобальных значений является неправильным. Кроме того, эти массивы имеют свое назначение (передача параметров извне), что делает их изменение простым способом запутаться в коде. Но я считаю, что некоторые из этих массивов могут быть очищены в начале скрипта, просто чтобы не вызывать проблем в коде.
Tadeck
1
Я использую оболочки входного массива OO (неявная фильтрация), которые печатают дополнительное уведомление, когда переменные $ _GET или $ _POST подвергаются фальсификации. Это все еще возможно, но должно быть ограничено узкими ситуациями. (Межмодульная сигнализация, хотя это может понадобиться только диспетчеру / фронт-контроллеру.)
Mario
@mario: Я хотел бы узнать больше о том, как вы достигли этого, если вы сможете взглянуть на этот вопрос: stackoverflow.com/questions/12954656
Уэсли Мёрч,

Ответы:

16

Учитывая , что PHP уже эти настройки суперглобального, я не думаю , что это зло , чтобы изменить их. В некоторых случаях это может быть лучшим способом решения проблем ... особенно когда речь идет о стороннем коде, который вы не можете легко изменить. (Они могут использовать $_GETнапрямую или предположить, что какой-то ключ существует в $_SERVERи т. Д.)

Однако, вообще говоря, я думаю, что это плохая практика, когда вы пишете свой собственный код. Изменение $_REQUESTданных с помощью некоторого скрытого фильтра, который автоматически запускается на каждой странице, может вызвать побочные эффекты. (См. Все проблемы, которые «магические цитаты» вызвали для доказательства.)

Так что, если вы не собираетесь этого делать (автоматически фильтруете суперглобалы), то следующее не даст вам никаких преимуществ:

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

когда вы можете легко сделать:

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

Я думаю , что это гораздо более ясно , чтобы сделать общесистемное различие , которое $_POSTи $_GETбудет всегда нефильтрованными, ненадежные данные, и они не должны никогда быть использован как есть.

Копируя отфильтрованное значение в другую переменную, вы заявляете, что «я понимаю, что делаю ... Я отфильтровал этот ввод, и его можно безопасно использовать».

konforce
источник
Спасибо за вклад, мой интернет был в сети почти 2 дня, поэтому у меня не было возможности ответить кому-либо. В этом примере я изменил $ _POST и использовал его как массив данных для передачи в функцию обновления, предполагая, что в этой функции мы будем читать еще несколько ключей $ _POST. Я бы предпочел создать новый массив, но я видел, как люди делают это вместо этого, поэтому я все еще немного не уверен, называть это «плохим кодом» или нет, но я думаю, что это, по крайней мере, так. Я думаю, что в любой момент, когда вы почувствуете необходимость сделать это, всегда найдется лучший способ.
Уэсли Мёрч
1
@ Уэсли, основная причина, по которой он «плохой», заключается в том, что он повышает вероятность того, что вы забудете очистить некоторые пользовательские данные. В вашем примере вы модифицируете один ключ, а затем передаете весь массив. Что если некоторые из этих данных содержат вредоносный ввод, который не обрабатывается? Гораздо лучше создать этот новый массив вручную, копируя в него только то, что вам нужно $_POST, и санировать по мере продвижения. Что касается других людей, которые делают это ... ну, многие пишут очень плохой PHP-код, но это также не оправдывает вас. :)
konforce
Я думаю, что я должен был подчеркнуть другие применения злоупотребления superglobals помимо просто очистки данных. Вероятно, мне следовало бы вообще об этом забыть и написать более четкий вопрос, людям особенно нравится разбираться в аспектах безопасности и часто игнорировать все остальное. Не звучит неблагодарно, я действительно ценю обратную связь. Я собираюсь подождать один день и поставить галочку, так как вы обратились к некоторым хорошим моментам, но пропустили участник голосования в течение 3 минут, когда вопрос был новым :) Еще раз спасибо!
Уэсли Мёрч
9

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

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

Я всегда сопоставляю их, когда они вам нужны:

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

или похожие.

С точки зрения других переменных , это хорошая практика , чтобы не писать ни одному из $_GET, $_POST, $_REQUEST, $_SERVERили $_COOKIE. $_SESSIONоднако отличается, потому что вы часто хотите записать данные в сеанс, которые затем сохраняются по различным запросам в сеансе.


источник
2
Еще одна причина, по которой такие глобальные переменные должны исчезать, заменяемые объектами / методами, которые можно вызывать для получения их при необходимости. Почему setcookieсуществует, но мы получаем куки через $_COOKIE? Кроме того, поскольку $_COOKIEон устанавливается только при запуске текущего сеанса и никогда не обновляется, требуется, чтобы вы изменили / установили файлы cookie в обеих областях, чтобы более поздние области кода имели актуальную информацию.
Кевин Пено
Спасибо Джеймс, я был в автономном режиме некоторое время, поэтому я не мог ответить. Короче говоря, я согласен с вами. Всегда есть лучшее решение, чем писать в post / get / etc, но я все еще не уверен, считается ли это строго плохой идеей, как в « никогда не делай этого никогда ». Итак, если я снова столкнусь с этим типом кода, вы думаете, что я имею право «вызывать их» в неаккуратном коде, или это иногда можно использовать умным, безопасным способом?
Уэсли Мёрч
@Wesley Если бы это было «никогда не делай этого никогда», то суперглобалы, вероятно, были бы строго доступны только для чтения - это не так. Я бы назвал это плохой практикой - устанавливать или перезаписывать их в коде приложения - по указанным причинам.
Мишель Фельдхейм
3

Вам следует избегать этого. Может быть, когда-нибудь вы забудете что-то очистить, тогда вы сможете извлечь опасные данные. Если вы копируете данные в новую структуру во время очистки

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

Дополнительные другие скрипты могут предполагать, что массив не тронут и может реагировать любопытно.

KingCrunch
источник
2

Мне никогда не нравилась идея модификации superglobal, потому что она вводит в заблуждение. Это быстрый хакерский способ сделать что-то, что, скорее всего, будет лучшим способом.

$_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
@ Томас, я согласен с Кингом здесь. Даже если они являются глобальными, вы не должны использовать что-либо глобальное в других областях, потому что это вызывает тесную связь (вот почему эту функцию нельзя использовать повторно). В вашем примере, если функция заключается в очистке данных, почему она выполняет только очистку $_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столбцом.

В конечном счете, я бы настоятельно рекомендовал не изменять суперглобальные переменные, если вам это не нужно .

rybo111
источник