Статьи

Дополнительные советы для защитного программирования на PHP

Общее определение защитного программирования (как показано в части 1, обзорной части по защитному программированию в PHP ), которое используется здесь, выглядит следующим образом:

Проще говоря, защитное программирование — это программирование с намерением предвидеть вероятные точки отказа. Цель состоит в том, чтобы обойти эти вероятные проблемы до того, как они возникнут.

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

«Fail Fast» не следует рассматривать как счетчик оборонительного программирования. Все это попадает под один зонтик. Каковы эти методы, если не способы предвидеть, что ваша программа может потерпеть неудачу, и либо предотвращать их, либо как-то иначе, чтобы надлежащим образом обрабатывать эти сбои?

Щитовая цель, поражаемая стрелами

Терпеть неудачу быстро и громко

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

function thisTestFunction($testInt) { if(!is_int($testInt)){ // Do something here } } 

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

Проверка входных данных

Существует много способов безопасной проверки ввода пользователя.

Typecasting — это интересный способ «проверить» пользовательский ввод. Иногда это выглядит примерно так:

 $member->property = (int) $_GET['property']; 

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

 $member->property = filter_input(INPUT_GET, 'property', FILTER_VALIDATE_INT); if (false === $member->property) { throw new Exception('Property was not an int'); } 

Использование встроенной функции filter_input в современном PHP имеет много преимуществ, и вы можете прочитать о них больше в вышеупомянутой статье или на php.net .

Предотвращение случайного назначения в сравнениях

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

 if($member->property == 12345){ // Do crazy cool stuff } else { // Don't do any fun stuff } 

Это относительно нормальное сравнение, да? Однако что произойдет, если вы случайно используете «=» вместо «==» (или, в большинстве случаев, даже лучше «===» )? Простое скольжение пальца по клавиатуре? Может быть, рассеянность? Внезапно ваше сравнение теперь звучит правдиво, всегда, во всех случаях. Если у вас нет хорошей IDE, предупреждающей вас об этом, сколько времени вам понадобится, чтобы узнать? В некоторых случаях это может быть молчаливой ошибкой в ​​течение некоторого времени. Тем не менее, есть чрезвычайно простой способ предотвратить это:

 if(12345 == $member->property){ // Do crazy cool stuff } else { // Don't do any fun stuff } 

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

Работа с Try / Catch и Исключениями

операторы try/catch являются еще одной горячей темой среди разработчиков PHP. Давайте сначала кратко рассмотрим, о чем мы говорим.

 try { if($member->property <= 0){ throw new Exception("Value must be 1 or greater"); } // If no exception was thrown, this will be output. echo "The value is acceptable"; } catch(Exception $e) { echo 'Message: '.$e->getMessage(); } 

Хорошо известным инструментом защитного программирования является оператор try/catch и класс Exception . Они отлично подходят для перехвата и регистрации ошибок при правильном использовании. Хороший программист будет использовать операторы try/catch чтобы предвидеть возможные ошибки или другие ситуации, которые могут нарушить нормальный поток. Когда возникают эти исключения, они должны обрабатываться соответствующим образом. Пользователь приложения, при необходимости, должен получить разумное сообщение об ошибке, которое может быть полезным без разглашения конфиденциальной информации. Администратор (ы) приложения должны получать подробные оповещения и / или журналы. Исключения, которые неправильно обрабатываются или игнорируются, игнорируют рекомендацию «Неудачно громко» и могут позволить программе некоторое время оставаться в состоянии по умолчанию, по умолчанию, что не подходит ни для кого.

Подробнее об исключениях в PHP смотрите здесь и здесь .

операции

Транзакции — это функция баз данных, которая позволяет группировать запросы так, что в случае сбоя одного запроса все они терпят неудачу. Это реализация ACID, о которой вы можете прочитать больше здесь . Идея состоит в том, что объединение нескольких запросов в один процесс иногда может быть более безопасным и стабильным решением, особенно когда запросы зависят друг от друга. Разработчики PHP часто полностью игнорируют транзакции или считают их ненужными, но при взаимодействии с базами данных немного защитного программирования может иметь большое значение. Транзакции обсуждаются более подробно в этом прекрасном посте , но в двух словах, транзакции позволяют вам запускать обновления MySQL, а затем проверять результаты перед тем, как их зафиксировать. Если вы используете PDO (так и должно быть) , вы можете использовать методы PDO, чтобы начать транзакцию, зафиксировать результаты, а также выполнить откат. В дополнение к вышеупомянутому обзору транзакций, углубиться в них с этим подробным руководством .

Вот краткий пример того, как может выглядеть использование транзакций в PDO:

 try{ // Start the transaction $db->beginTransaction(); // Perform one update $statement1 = $db->prepare("QUERY LANGAUGE HERE"); $statement1->execute(array($a,$b)); // Perform another update $statement2 = $db->prepare("MORE QUERY LANGUAGE HERE"); $statement2->execute(array($a,$b,$c)); // Commit the transaction $db->commit(); } catch(PDOException $ex) { // If an exception occurs, first, roll back updates $db->rollBack(); // Then, in this block, further handle the exception } 

Вывод

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

Если от этого отнимается только одна вещь, особенно у новых разработчиков, это то, что вы должны программировать в обороне — планировать возможность ошибок и ошибок. Обращайтесь с ними соответствующим образом. Не позволяйте тихим ошибкам прогрессировать. Сбой быстро. Проверьте свой код. Создавая надежные приложения, которые тестируют и решают проблемы, а также предвидят и обрабатывают будущие, вы делаете свои приложения более надежными и, надеемся, помогаете создать лучший пользовательский опыт из-за кулис.