Header_http_status_codes.php

У меня есть PHP-скрипт, который должен делать ответы с кодами ответа HTTP (статусные коды), например HTTP 200 OK или с кодом 4XX или 5XX.

Я только что нашел этот вопрос и подумал, что ему нужен более полный ответ:

Начиная с PHP 5.4 для этого есть три метода:

Просто сделайте http_response_code(404) .

Если у вас более низкая версия PHP, попробуйте header(‘ ‘, true, 404); (обратите внимание на пробелы в строке).

Если вы хотите установить также причину, попробуйте:

header(‘HTTP/ 433 Reason Phrase As You Wish’);

Фактический текст кода не имеет значения. Вы могли бы сделать

header(‘The goggles, they do nawtink!’, true, 404);

и он все равно будет отображаться браузером как 404 – это важный код.

Zend Framework имеет пакетное решение в Zend_Http_Response

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

Мне нужно отправить «500 Internal Server Error» из скрипта PHP при определенных условиях. Сценарий должен быть вызван сторонним приложением. Сценарий содержит пару выражений die(“this happend”) для которых мне нужно отправить код ответа 500 Internal Server Error вместо обычных 200 OK . Третий сторонний скрипт будет повторно отправлять запрос при определенных условиях, которые не включают код ответа 200 OK .

Мне нужно отправить 200 заголовков только после того, как была выполнена последняя строка.

Вы можете использовать следующую функцию для отправки изменения статуса:

Вы можете использовать его как таковое:

PHP 5.4 имеет функцию http_response_code , поэтому, если вы используете PHP 5.4, вы можете просто сделать:

Чтобы ответить на ваш последующий вопрос, HTTP 1.1 RFC говорит:

Приведенные здесь фразы причины – это только рекомендации – они МОГУТ быть заменены локальными эквивалентами, не затрагивая протокол.

Это означает, что вы можете использовать любой текст, который вы хотите (исключая возврат каретки или линейные каналы) после самого кода, и он будет работать. Как правило, обычно используется лучший код ответа. Например, вместо того, чтобы использовать 500 без записи, вы можете отправить 404 (не найдено), а для чего-то вроде «условия не удались» (я предполагаю ошибку проверки), вы могли бы отправить что-то вроде 422 (непроцессорный организация).

Вы можете просто поставить:

header(“HTTP/1.0 500 Internal Server Error”);

в ваших условиях:

Что касается запроса к базе данных, вы можете просто сделать это вот так:

$result = mysql_query(“..query string..”) or header(“HTTP/1.0 500 Internal Server Error”);

Вы должны помнить, что вы должны поместить этот код перед любым тегом html (или выходом).

Вы можете упростить его так:

Он вернет следующий заголовок:

Добавлено: Если вам нужно точно знать, что пошло не так, сделайте что-то вроде этого:

Ваш код должен выглядеть так:

Я предполагаю, что вы прекратите выполнение, если что-то пойдет не так.

С точки зрения безопасности вывод сообщений об ошибках несет в себе
как плюсы, так и минусы.

Одна из стандартных методик, применяемых в атаках – ввод некорректных данных с
последующим анализом содержания и характера сообщений об ошибках. Это дает
взломщику возможность проверить скрипты и данные сервера на наличие
потенциальных дыр. Например, если взломщик получил некоторую информацию о
странице на основании отправки формы, он попробует предопределить некоторые
передаваемые значения или модифицировать их:

Пример #1 Атака на переменные в HTML-странице

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

Пример #2 Использование стандартных отладочных переменных

Независимо от метода обработки ошибок возможность проверки системы
на наличие ошибок снабжает взломщика дополнительной информацией.

Например, стандартный вывод об ошибке указывает операционную систему,
в которой выполняются PHP скрипты. Если взломщик анализирует обычную
страницу сайта с расширением .html и пытается узнать систему, с
помощью которой работает сайт (для дальнейшего поиска уязвимых
мест), то подавая на ввод неверные данные он может обнаружить,
что система использует PHP.

Также уведомление об ошибке может дать информацию об используемой
базе данных, или, к примеру, как построена логика работы скриптов. Это, в свою очередь,
может позволить взломщику подключиться к открытому порту базы данных, либо найти
определенные ошибки в коде. Пробуя поочередно различные неверные блоки данных,
злоумышленник может определить порядок аутентификации в скрипте (например,
по номерам строк с ошибками) или проверять на наличие дыр различные участки кода.

Вывод стандартных ошибок или ошибок, связанных с файловой
системой, может указать, с какими привилегиями запущен
веб-сервер, и как организованы каталоги сайта. Обработка подобных ошибок,
написанная разработчиками приложения, может только усугубить проблему, если
взломщиком будет найден способ обнаружить “скрытую” отладочную информацию.

Существует три основных способа решения этой проблемы. Первый заключается в том,
чтобы структурировать все функции и попытаться компенсировать объем выдаваемых ошибок.
Второй способ – полностью отключить в работающем коде вывод сообщений об ошибках.
И, наконец, третий способ – использовать специальные средства PHP для создания
собственного обработчика ошибок. В зависимости от используемой вами политики безопасности
вы можете применить в вашей конкретной ситуации все три способа.

Читайте также:  Проблема с проброской 80 порта в прошивке от 21.05.2014

Один из возможных способов обезопасить ваш код перед его публикацией
в общий доступ – это использование функции PHP ,
которая может помочь выявить потенциально опасные переменные.
Тестируя код перед выпуском релиза при помощи значения
E_ALL, вы достаточно легко можете обнаружить участки
кода, в которых переменные могут быть подменены либо модифицированы.
После окончания тестирования, вы можете либо полностью отключить
все сообщения об ошибках установив
в 0, либо отключить их вывод с помощью директивы php.ini
display_errors, изолировав таким образом ваш
код от прощупывания. Если вы решили использовать последний способ,
то необходимо также указать путь к лог-файлу с помощью директивы
error_log и включить директиву
log_errors.

Пример #3 Поиск потенциально опасных переменных при помощи E_ALL

Вернуться к: Безопасность

Этот урок переехал в мой гитхаб: https://github.com/codedokode/pasta/blob/master/php/exceptions.md – ниже представлена старая версия, потому советую перейти и прочитать новую.

Если ты изучаешь ООП, ты наверняка натыкался на исключения. В мануале PHP описаны команды try/catch/throw и finally (доступна начиная с PHP 5.5), но не объясняется толком как их использовать. Чтобы разобраться с этим, надо узнать почему они вообще были придуманы.

А придуманы они были, чтобы сделать удобную обработку ошибок.

Для примера представим, что мы пишем приложение для вывода списка пользователей из файла на экран. Допустим, код выглядит как-то так:

Все ли тут верно? Нет, не все. Мы забыли сделать обработку ошибок. Файла может не существовать, к нему может не быть доступа, данные в нем могут быть в неверном формате. Хорошая программа, разумеется должна обрабатывать такие ситуации и выводить соответствующее сообщение.

Что же, давай улучшим код и переделаем функцию, чтобы она возвращала массив из 2 элементов: если все ок, то элемент success содержит true, а элемент result содержит массив пользователей. Если же произошла ошибка, то в success будет находиться false, а в элементе error текст ошибки.

Конечно, мы должны поменять и код, который вызывает функцию:

Теперь код гораздо лучше. Тот, кто вызывает функцию, может обрабатывать ошибки так, как ему хочется. Но за это нам пришлось заплатить усложнением кода: теперь мы должны после каждого вызова писать if и проверять, успешно ли выполнилась функция. Когда функций много, и каждая может вернуть ошибку, код начинает наполовину состоять из таких проверок.

Выбрасываем исключение

В качестве решения проблемы были придуманы исключения. Если в функции произошла какая-то ошибка и она не может выполнить свою работу, то она выбрасывает исключение командой throw:

Исключение — это объект встроенного в PHP класса Exception (мануал по Exception) или его наследника. Объект исключения содержит подробности о причинах ошибки. Также, в PHP есть еще другие классы исключений, которые ты можешь использовать: http://php.net/manual/ru/spl.exceptions.php

В PHP7 определение расширено: исключения не обязаны наследоваться от Exception, это может быть любой класс, реализующий интерфейс Throwable, а так как сам Exception его реализует, то его наследники по-прежнему будут работать.

Также, ты можешь написать свой класс исключений, унаследовав его от Exception. В этом случае ты даже можешь добавить туда свои методы и свойства. Свой класс надо писать, если ты хочешь позже ловить выброшенное функцией исключение или добавить какие-то дополнительные подробности об ошибке (например, имя файла из которого не удалось загрузить пользователей).

Исключение выбрасывается в случае возникновения нештатной ситуации, когда функция обнаруживает что не способна выполнить свою задачу.

Исключение по умолчанию (если оно не перехватывается) выходит из всех вызовов функций до самого верха и завершает программу, выводя сообщение об ошибке. Таким образом, если ты не перехватываешь исключения, то все равно увидишь причину ошибки (а если у тебя установлено расширение xdebug то еще и стектрейс — цепочку вызовов функций, внутри которых оно произошло). И тебе больше не надо писать if:

Выброс исключения чем-то похож на возврат значения через return, однако в отличие от return, выброс исключения выходит не только из функции где оно выброшено, но и всех вышестоящих функций, которые ее вызвали, если они не ловят это исключение. Поскольку исключение сигнализирует о внештатной ситуации, которую не исправить, исключения обычно не ловят и позволяют им завершить выполнение программы.

Вот простой пример:

В данном примере кода исключение мгновенно выйдет из всех вложенных вызовов функций и завершит программу. Это хорошо, так как если произошла какая-то неисправимая ошибка, выполнять программу дальше нельзя. Это называется принципом fail fast (статья на Хабре).

Ловим исключения

Исключения можно “ловить”. Это полезно в нескольких случаях. Иногда мы можем как-то отреагировать на неудачу: например, при ошибке скачивании файла по сети можно сделать паузу и повторить попытку. Для этого нам надо перехватить выброшенное функцией исключение.

Также, исключения обычно ловят на верхнем уровне программы в веб-приложениях для того, чтобы сделать свою страницу, информирующую об ошибке (так как PHP при непойманном исключении завершает программу, и пользователь видит пустую белую страницу в браузере, что плохо). В приложениях, запускаемых в командной строке, а не в браузере, обычно это не требуется, так как они не ориентированы на “обычных” пользователей.

Читайте также:  КОД ОШИБКИ B 10 A 2 ФРИЛЕНДЕР 2

Перехватывать исключения в теории можно двумя способами: неструктурно и структурно. Неструктурно — это когда мы задаем обработчик исключений в начале программы:

Этот способ ловит неперехваченные исключения любых видов во всей программе, его можно использовать для того, чтобы сделать свою страницу ошибки (об этом подробнее написано ниже). После срабатывания обработчика программа будет завершена, предотвратить это невозможно.

Структурная обработка исключений – это когда мы ловим только исключения определенных типов в опредленном месте кода. Она реализуется с помощью try/catch:

В PHP5.5 и выше добавлен блок finally. Команды из этого блока будут выполнены после любого из блоков (try или catch) — в случае если исключения не произойдет и в случае если оно произойдет.

Перехватывать абсолютно любые типы исключений — плохая идея, так как мы обычно хотим обрабатывать только определенные, “наши”, типы ошибок и не знаем что делать с другими. Чтобы перехватывать только нужные нам исключения, надо сделать свой класс на основе встроенного в PHP Exception:

Выкидывать его в throw

И ловить в catch только наши исключения:

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

Поддержка исключений везде

Эта система работала бы идеально, если бы стандартные функции PHP выкидывали исключения при ошибках. Но увы, по историческим причинам эти функции просто генерируют сообщение об ошибке и возвращают false. Ну например, функция чтения файла в память file_get_contents поступает именно так, и ошибка чтения файла или его отсуствие не завершает программу. Потому ты обязан ставить if после каждого вызова:

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

Для этой проблемы есть решение. Можно установить общий обработчик ошибок (он вызывается при любой ошибке PHP, например обращении к несуществующей переменной или невозможности чтения файла), и в нем выкидывать исключение. Таким образом, любая ошибка или предупреждение приведут к выбросу исключения. Все это делается в несколько строчек с помощью встроенного в PHP класса ErrorException (мануал):

Этот код превращает любые ошибки и предупреждения PHP в исключения. Некоторые современные фреймворки (Slim) включают в себя такой код.

Исключения и PDO

Чтобы расширение для работы с базами данных PDO использовало исключения (например при попытке выполнить неправильно написанный SQL запрос), надо установить соответствующий параметр (рекомендуется). Без него ты будешь должен после вызова каждой функции проверять результат с помощью if:

Исключения и mysqli

Библиотека mysqli при ошибках не выбрасывает исключений и не генерирует предупреждений, а просто возвращает false (то есть молчит как партизан). Таким образом, после каждого действия ты должен проверять результат с помощью if, что видно в примерах кода в мануале: http://php.net/manual/ru/mysqli.query.php

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

Чтобы не писать ифы во всей программе, ты можешь сделать класс-обертку над mysqli. Или просто использовать PDO.

Так делать не надо

Не стоит ловить все исключения без разбора:

Лучше создать свой класс исключений и ловить только его.

Не надо скрывать информацию об ошибках. В 99% этот код, игнорирующий исключение, неправильный:

Не надо располагать try/catch и throw на одном уровне — в этом случае проще написать if:

Страница ошибки в веб-приложениях

По умолчанию при непойманном исключении PHP завершает скрипт. Если опция display_errors в php.ini равна 1, то PHP выводит подробности об исключении, а если она равна 0, то в браузере отображается просто белая страница. Также, PHP записывает информацию об исключении в лог ошибок сервера.

Очевидно что оба варианта не годятся для использования на продакшен (“боевом”) сервере: обычные пользователи не должны видеть непонятные надписи на английском о твоем приложении, и тем более не должны смотреть на пустую страницу и гадать в чем дело. А хакеры не должны видеть подробности об устройстве твоего приложения. Более того, PHP не выдает при ошибке HTTP код 500, который говорит роботам (вроде Гугла или Яндекса) что на странице ошибка и индексировать ее на надо. Разработчики PHP конечно выбрали неудачный способ поведения по умолчанию.

Потому на боевом сервере надо ставить display_errors в 0, а в приложении делать свою страницу ошибки.

Читайте также:  Код ошибки 949

Что будет, если просто не ловить исключение:

  • информация пишется в лог (ок)
  • на компьютере разработчика выводятся подробности (ок)
  • пользователь видит белую страницу или подробности ошибки (плохо)

Как надо обрабатывать исключения:

  • записать информацию в лог
  • показать пользователю заглушку (“сайт временно недоступен, вот контакты администратора”)
  • на заглушке выставить HTTP код ответа 503 для роботов
  • на компьютере разработчика (при display_errors = 1) можно показать подробности и стектрейс

Если ты используешь фреймворк, возможно, в нем все это уже реализовано. Современные фрейморки, такие как Slim, Yii 2, Symfony 2, выводят заглушку при непойманном исключении. Старые – не выводят.

Ссылки

Механизм исключений существует и в других языках в таком же виде: Java, C++, Ruby, Javascript и многих других. Статья в вики:

https://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D0%B8%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B9 (написано не очень понятно)

Также, про исключения можно почитать в книгах:

  • Мэтт Зандстра «PHP: Объекты, шаблоны, методики программирования»
  • Джордж Шлосснейгл «Профессиональное программирование на PHP»

Сборка кода ответа (PHP> = 4

Функция header() имеет специальный прецедент, который обнаруживает линию ответа HTTP и позволяет заменить ее на обычную

header(“HTTP/1.1 200 OK”);

Однако для этого требуется специальное лечение (Fast) CGI PHP:

$sapi_type = php_sapi_name(); if (substr($sapi_type, 0, 3) == ‘cgi’) header(“Status: 404 Not Found”); else header(“HTTP/1.1 404 Not Found”);

Примечание. Согласно HTTP RFC , фразой причины может быть любая пользовательская строка (соответствующая стандарту), но для удобства клиента я не рекомендую размещать там случайную строку.

Примечание. Для php_sapi_name() требуется PHP 4.0.1

Функция http_response_code (PHP> = 5

Функция http_response_code() была введена в PHP 5.4, и это упростило ситуацию.

Совместимость

  • Использование substr($sapi_type, 0, 3) == ‘cgi’ не позволяет обнаруживать быстрый CGI. При использовании PHP-FPM FastCGI Process Manager php_sapi_name() возвращает fpm, а не cgi
  • Есть как минимум 2 случая при вызове http_response_code() приводят к неожиданному поведению:
    Когда PHP сталкивается с кодом ответа HTTP, он не понимает, PHP заменит код на тот, который он знает из той же группы. Например, «521 веб-сервер выключен» заменяется «500 Internal Server Error». Многие другие необычные коды ответов от других групп 2xx, 3xx, 4xx обрабатываются таким образом. На сервере с php-fpm и nginx http_response_code () функция МОЖЕТ изменить код, как ожидалось, но не сообщение. Это может привести к появлению, например, странного заголовка «404 OK». Эта проблема также упоминается на веб-сайте PHP по комментарию пользователя http://www.php.net/manual/en/function.http-response-code.php#112423
  • Когда PHP сталкивается с кодом ответа HTTP, он не понимает, PHP заменит код на тот, который он знает из той же группы. Например, «521 веб-сервер выключен» заменяется «500 Internal Server Error». Многие другие необычные коды ответов от других групп 2xx, 3xx, 4xx обрабатываются таким образом.
  • На сервере с php-fpm и nginx http_response_code () функция МОЖЕТ изменить код, как ожидалось, но не сообщение. Это может привести к появлению, например, странного заголовка «404 OK». Эта проблема также упоминается на веб-сайте PHP по комментарию пользователя http://www.php.net/manual/en/function.http-response-code.php#112423

Для справки здесь приведен полный список кодов состояния HTTP-ответа (этот список включает коды из интернет-стандартов IETF, а также другие RFC-запросы IETF. Многие из них в настоящее время не поддерживаются функцией PHP http_response_code): http: //en.wikipedia .org / вики / List_of_HTTP_status_codes

Вы можете легко проверить эту ошибку, позвонив:

Сервер отправит код ответа HTTP «500 Internal Server Error», что приведет к непредвиденным ошибкам, если у вас есть, например, пользовательское клиентское приложение, вызывающее ваш сервер и ожидающее дополнительных HTTP-кодов.

Мое решение (для всех версий PHP начиная с версии 4.1.0):

http_response_code () не поддерживает все коды ответа HTTP и может перезаписать указанный код ответа HTTP другим из той же группы.

Новая функция http_response_code () не решает всех проблем, связанных с этим, но делает вещи хуже всего новыми ошибками.

Добавьте эту строку перед любым выводом тела, если вы не используете буферизацию вывода.

Замените часть сообщения («ОК») соответствующим сообщением и кодом состояния с кодом (404, 501 и т. Д.),

С функцией заголовка . В разделе приведен первый пример, который он принимает.

Если вы здесь, потому что WordPress дает 404 при загрузке среды, это должно устранить проблему:

Проблема связана с отправкой заголовка Status: 404 Not Found. Вы должны переопределить это. Это также будет работать:

define(‘WP_USE_THEMES’, false); require(‘../wp-blog-header.php’); header(“HTTP/1.1 200 OK”); header(“Status: 200 All rosy”);

поскольку PHP 5.4 вы можете использовать http_response_code() для получения и установки кода состояния заголовка.

вот документ этой функции в php.net:

3-й аргумент функции заголовка (PHP> = 4

Очевидно, что есть несколько проблем при использовании этого первого варианта. Самым большим из которых я считаю, что он частично анализируется PHP или веб-сервером и плохо документирован.

Начиная с версии 4.3, функция header имеет 3-й аргумент, который позволяет вам удобно установить код ответа, но для его использования требуется, чтобы первый аргумент был непустой. Вот два варианта:

header(‘:’, true, 404); header(‘X-PHP-Response-Code: 404’, true, 404);

Я рекомендую второй . Первый работает во всех браузерах, которые я тестировал, но некоторые небольшие браузеры или веб-сканеры могут иметь проблему с линией заголовка, которая содержит только двоеточие. Имя поля заголовка во втором. вариант, конечно, не стандартизирован каким-либо образом и может быть изменен, я просто выбрал наглядно описательное имя.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *