Осторожное использование вызовов внешних команд
Прямое использование внешних вызовов команд (через "system" или "``") допускается только в особых случаях, при этом, если командная строка формируется внутри кода, необходимо быть абсолютно уверенным, что в командную строку не могут попасть входные переменные и параметры HTTP-запроса.
Кроме того, НЕ СЛЕДУЕТ использовать qx и system в коде контроллеров. Эти вызовы могут быть сосредоточены только в коде ядра системы и соответствующие функции обязательно должны быть покрыты тестами (проверяющими невозможность использования эксплойтов).
Всегда проверяйте коды возврата системных вызовов
Всегда проверяйте коды возврата системных вызовов. Если ошибка фатальна — вызывайте исключение с помощью die, Carp::confess или Carp::croak, передавая соответствующее сообщение об ошибке. Хорошее сообщение об ошибке должно включать: в каком месте программы возникла проблема, какой системный вызов был произведен, с какими аргументами, и (ОЧЕНЬ ВАЖНО) должно содержать стандартное системное описание ошибки.
Пример:
open( my $fh, ">", $filename ) or die "Can't open $filename for writing: $!"; print $fh '....';
Обработка исключений
При реализации обработки исключений, лучше использовать:
eval { ... 1; } or do { ... };
смысл eval-do в том что блок обработки ошибок (в "do") должен следовать непосредственно после eval, чтобы между ними нельзя было вставить какой-то другой код (который может испортить $@), так же решение о том, было ли исключение или нет, должно приниматься на основе кода возврата eval{} а не на основе переменной $@. если в данном конкретном eval-do не удобен, допустимы другие конструкции, основанные на том же принципе (однако для консистентности лучше попытаться всё же использовать eval-do):
if (eval { ...; 1 }) { ... } else { ... } ok ! eval { ...; 1 }, "some test";
если в блоке обработке ошибок мы не анализируем $@, отступление от этого правила (в части $@) допустимо:
my $success = eval { ... 1; }; ... if ($success) { ... } else { ... }
если речь идёт не о паттерне программирования "обработка исключений" (а о каком-то системном программировании, о eval EXPR), то допускается отходить от этого правила, если это сделает код лучше.
Die often, die earlier
При возникновении ошибки код должен выдавать исключение и падать как можно раньше. Исключения должны логироваться и оперативно исправляться.
Не нужно маскировать ошибку и давать коду продолжать выполнение. Ошибка, скорее всего, означает некорректные данные, а значит — неопределённое дальнейшее поведение.
Там, где есть серьёзные причины не падать, должна быть не маскировка, а полная обработка ошибки. Дальнейшее поведение кода при ошибке должно быть полностью продумано и определено (т. е. ошибка в этом месте уже не считается багом). Такая логика обычно сложнее, чем исправление причины ошибки, поэтому её введение должно быть обосновано (например, наличием внешних событий, которые мы не контролируем, или высокой критичностью функционала).
Пример: Проверка доступности домена через внешнего провайдера. Многие провайдеры ненадёжны, мы не можем на это повлиять. В случае ошибки обращения к провайдеру мы можем сделать повтор или по умолчанию считать домен свободным, чтобы пользователь мог продолжить заказ.
Недопущение возможностей для SQL-inject
Подстановка данных в SQL-запросы ТОЛЬКО через placeholders ("?").
Минимизация изменений тела запроса
В особых случаях (если необходимо подставить имя поля или название таблицы) допускается подстановка переменных напрямую в тело запроса, при этом должны быть соблюдены два условия:
- если подставялется имя таблицы или поля, то оно обязательно заключается в кавычки (для MySQL): `fieldname`,
- Вы должны быть АБСОЛЮТНО уверены в том, что значения переменных НЕ извлекается напрямую из входных параметров/переменных HTTP-запроса, а генерируется внутри кода и могут принимать только конечное дискретное множество значений.
Пример:
SELECT `$fieldname` FROM `$table`
Для кросс-dbms приложений можно использовать DBI-метод quote_identifier:
my $quoted_fieldname = $dbh->quote_identifier( 'fieldname' ); my $sql = "SELECT $quoted_name FROM mytable";