Ко мне в блог приходят люди по запросам, связанным с Perl. Часто это новички в Perl, поэтому я решил написать эту статью о безопасности и поделиться готовым решением, которое использую сам.
Большинство программистов Perl использует модуль DBI для работы с базами данных. Этот модуль умеет эскейпить значения, делая их безопасными и предотвращая SQL инъекции. Это метод quote.
Вызывается так:
use DBI;
$dbh = DBI->connect(.....данные для подключения к БД....);
$dbh->quote($val);
Лично я делаю $dbh глобальной переменной, видимой отовсюду. Это позволяет удобно использовать самописные библиотеки для работы с базой данных. Например, у меня есть библиотека для работы с очисткой и проверкой данных. Там есть функция _quote(). Вот она:
sub _quote {
my $val = shift;
my $qval = $dbh->quote($val);
return "''" if $qval eq '';
return $qval;
}
При любой вставке сторонних данных в SQL-запрос я использую функцию _quote() и чувствую себя в безопасности в этой части.
Разумеется, нельзя доверять данным извне. Если вы передаете, скажем, имя файла скрипту — обязательно проверяйте и удаляйте все ненужные символы, проверяйте на наличие этого файла и вообще.
Во-первых, нужно быть уверенным что запрашиваемый файл можно отдать. Обычно для таких файлов выделяется специальная папка на сервере, путь к которой точно известен. Чтобы не воротить странных вещей с относительными путями вроде "../../data/" я в какой-то момент стал использовать абсолютные пути. Это удобно и переносимо. Это легко читается и повышает безопасность. Почему? Потому что я могу отрезать все штуки вроде "../" и задавать именно нужный путь от корня сайта.
Получить путь к корню сайта очень просто:
our $DIR = $ENV{DOCUMENT_ROOT};
Теперь в глобальной переменной $DIR содержится путь от корня сервера к корню сайта. Теперь добавляя к этой переменной названия папок, можно получать точные пути:
our $DIR = $ENV{DOCUMENT_ROOT};
my $image_dir = "$DIR/iamges/";
my $templates_dir = "$DIR/templates/";
my $user_dir = "$DIR/users/$username/";
Теперь, если передается имя файла как параметр, можно смело удалять у него все лишнее:
my $file = '..//.//./.../.htaccess';
$file =~ s!^.*\/!!g;
print($file);
Так мы получили имя файла без всяких относительных путей.
Аналогично SQL инъекциям, бывают RegExp инъекции. Например:
my $var = 'test foo bar data for real men';
my $test = 'test.*data';
if($var =~ /$test/) {
print 'Presents';
}
Этот код выведет "Presents". Хотя могло подразумеваться другое. Если мы ищем именно сочетание символов "test.*data", то нужно эскейпить эту строку функцией quotemeta.
my $var = 'test foo bar data for real men';
my $test = quotemeta('test.*data');
if($var =~ /$test/) {
print 'Presents';
}
В таком виде этот код не выведет ничего, потому что искомой строки нет.
У меня есть две служебные функции, которыми я пользуюсь при работе с целочисленными переменными. Например, айдишниками.
sub clear_int {
my $t = shift;
$t =~ s/\D+//gis;
return $t;
}
sub is_int {
my $i = shift;
return 1 if($i=~/^\d+$/);
return undef;
}
Пользуюсь я ими так:
sub myfunc {
my $id = clear_int(shift);
return undef unless(is_int($id));
}
Таким образом, я пытаюсь вытащить id, переданный в параметрах. Даже если он загрязнен посторонними данными, например, попыткой SQL-инъекции, я получаю именно номер, именно айди. А потом проверяю. Если это и правда целочисленное — значит это похоже на id и даже возможно, такая запись есть в базе.
Иногда нужно проверять приходящие параметры по списку возможных значений. Например, если это данные формы и нам нужно проверить поле выпадающего списка. Возможные значения известны заранее и нужно проверять не наличие параметра, а его соответствие этому списку. Я делаю так:
sub valid_value {
my $var = shift;
my $correct = shift;
my %test = map {($_,1)} @$correct;
if($test{$var}) {
return 1;
}
return 0;
}
Эта функция принимает проверяемую переменную и ссылку на массив возможных ее значений. Преобразует массив значений в хэш и проверяет наличие элемента с проверяемым значением в этом хэше. Пример использования:
my @correct = qw(cow dog cat sheep);
my $var = 'alligator';
if(valid_value($var, \@correct)) {
print "OK, this is animal from our farm.";
}
Так мы не пускаем аллигаторов на ферму.
Я думаю, я продолжу делиться своими наработками в будущем, так что если интересно — есть смысл подписаться на RSS моего блога.
Есть еще способ следить за своим сайтом — это проверять его на доступность. Для этого существует ряд сервисов. Большая часть из них платна, но есть и хорошие бесплатные сервисы. Например, проверить сайт на доступность позволяет сайт webpinger.ru. Он умеет бесплатно поддерживать несколько ваших сайтов и пинговать их периодически, оповещая вас о неполадках. Рекомендую ознакомиться.