Блог Веб-разработчика.

SQL-инъекции, это достаточно удивительный и парадоксальный вид уязвимости разрабатываемых приложений. Во-первых, этот тип уязвимости у всех “на слуху”, об этом много написано. Во-вторых, этот тип уязвимости относительно легко детектировать при проверке кода на безопасность. В-третьих, принципы защиты и безопасного программирования, в контексте SQL инъекции, уже давно определены и реализованы в коде многих популярных языков программирования... Казалось бы - вчерашний день. Но, тем не менее эта уязвимость по-прежнему встречается очень часто. Даже надо сказать  - слишком часто!

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

Суть уязвимости и примеры.

Суть этой атаки заключается в том, что пользователь (хакер) взаимодействуя с веб-приложением (например, через форму поиска по сайту), отправляет некорректные данные и таким образом модифицирует прописанные в коде приложения SQL-запросы к базе данных так, что приложение и сервер баз данных корректно их обрабатывают и возвращают информацию, или выполняют операции в базе данных, которые соответствуют задачам хакера. При этом такая работа не была предусмотрена самим разработчиком приложения. Очевидно, что данный тип уязвимости характерен только для систем взаимодействующих с базой данных через язык структурированных запросов (SQL - Structured Query Language).

Реальный случай. Простой авторский блог, на котором публикуются статьи, написанные автором блога. Сам блог разработан на заказ одной “студией” с нуля, за неплохие деньги. Страницы сайта со статьями имеют адрес вида:

/index.php?mod=article&id=40

если этот запрос модифицировать следующим образом:

/index.php?mod=article&id=10000 UNION SELECT name,password,id,4,5 FROM grt24_users WHERE id=1

и совершить по нему запрос к сайту, то на странице блога вместо заголовка статьи красовался логин администратора, а вместо текста - пароль. Конечно, Слава Богу, пароль был записан в виде md5-хэш функции, поэтому вывод этой информации дал мало. Пока...

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

/index.php?mod=article&id=10000 UNION SELECT 1,load_file('/home/аккаунт на хостинге блога/public_html/домен блога/includes/config.php'),3,4,5 is not null

Но, он пошел дальше! Он успешно загрузил во временную папку файл с директивой phpinfo() и несколько простых php-шеллов, используя запросы типа:

/index.php?mod=article&id=10000 UNION SELECT '<?php','phpinfo();','','','?>' into outfile '/home/аккаунт на хостинге блога/public_html/домен блога/tmp/info.php' from grt24_articles;

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

Такой вот подарочек на Новый Год...

Источник уязвимости. Признаки наличия уязвимости.

Прежде чем разбираться со способами, которые позволят заблокировать возможность SQL-инъекций, необходимо четко понять для себя в чем источник этой проблемы. Какие нюансы кода делают подобную уязвимость возможной. И здесь можно выделить два пункта:

  1. динамическое формирование строк запросов к базе данных. То есть содержимое запросов управляется входными данными, которые задают пользователи веб-приложения. Особенно здесь следует отметить использование чистой конкатенации строк или замены подстроки по какому-то ключевому слову
  2. отсутствие проверки входных данных. То есть введенные данные напрямую вставляются в запрос, без предварительной проверки на корректность.

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

$id = $_GET["id"];
$query = "SELECT * FROM `articles` WHERE `id`=".$id;
// или:
$query = "SELECT * FROM `articles` WHERE `id` = $id";
// или:
$query = str_replace("%id%", $id, "SELECT * FROM `articles` WHERE `id` = %id%");

в связке с отсутствием проверки пришедшего значения на корректность (то есть пришедшее значение вставляется “как есть”), это создает угрозу модификации строки запроса сторонними пользователями вашего приложения.

На основе вышесказанного, можно выделить ряд признаков, по которым Вы можете судить о возможности SQL-инъекции в разрабатываемом Вами коде:

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

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

Методы защиты.

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

Экранирование одинарных и двойных кавычек.

Передавая вместе со значением какого-либо строкового параметра одинарную или двойную кавычку (в зависимости от того какие используются в приложении), хакер может вносить изменения в структуру формируемого SQL-запроса. Если например, передаваемый параметр в строке запроса обрамляется в одинарные кавычки, то получив в запросе вместе со значением параметра одинарную кавычку ‘ и символ комментария -- (или /*), приложение обработает такой запрос без ошибок:

// передан запрос по такому url:
http://mojet-byt-vash-site.ru/index.php?mod=user&name=login" -- 
 
// как частный случай, если запрос формируется таким образом:
$name = $_GET["name"];
$query = "SELECT * FROM `users` WHERE `name` = "".$name.""";
 
// то в переменную $query, будет помещена строка:
$query = "SELECT * FROM `users` WHERE `name` = "login" --"";

то есть хакер сам ввел закрывающую кавычку, а “лишнюю”, которая теперь образовалась - закомментировал. Теперь он может ввести любое выражение между закрывающей кавычкой ‘ и комментарием --, и если это выражение будет синтаксически верным - то оно будет обработано тоже без ошибок. Поэтому важно в строковых параметрах экранировать все передаваемые кавычки, чтобы осложнить выполнение подобных трюков.

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

// в процедурном стиле:
mysql_real_escape_string()
// или:
mysqli_real_escape_string()
 
// в объектно-ориентированном стиле
mysqli::real_escape_string()

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

Пример использования функции в процедурном стиле:

// для процедурного стиля:
$db = mysqli_connect ("host","user","password","data_base");
 
/*
какой-то код Вашего приложения...
*/
 
$name = mysqli_real_escape_string($db, $_POST["name"]); // экранирование пришедшей от пользователя строки
$query = "SELECT * FROM `users` WHERE `login` = "".$name.""";
$result = mysqli_query($db,$query);
 
/*
дальнейший код Вашего приложения...
*/
 

и в объектно-ориентированном:

// для объектно-ориентированного стиля:
$db =  new mysqli("host","user","password","data_base");
 
/*
какой-то код Вашего приложения...
*/
 
$name = $db->real_escape_string($_POST["name"]); // экранирование пришедшей от пользователя строки
$query = "SELECT * FROM `users` WHERE `login` = "".$name.""";
$result = $db->query($query);
 
/*
дальнейший код Вашего приложения...
*/

Если Ваше приложение должно взаимодействовать с базой данных PostgreSQL, то рекомендовано для экранирования запроса использовать функцию pg_escape_string ():

  $db = pg_connect("host=localhost port=5432 dbname=db1 user=user1 password=pass");
  $data = pg_escape_string($_POST["data"]); // экранирование строки
  pg_query("SELECT * FROM `table` WHERE `data` = "".$data.""");

Однако, согласно документации PHP, если тип столбца в таблице базы данных - bytea, то следует использовать для экранирования строки функцию pg_escape_bytea ().

Если же Вы используете какую-либо другую базу данных, то документация рекомендует использование функции addslashes().

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

Проверка типа или принудительное задание типа данных.

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

$data = (int)$_GET["id"]; // если требуется целое число
$data = (float)$_GET["id"]; // если требуется число с плавающей точкой
 
// или:
$data = settype($_GET["id"],"integer"); // если требуется целое число
$data = settype($_GET["id"],"float"); // если требуется число с плавающей точкой
 
// или сразу создать отформатированную строку запроса через sprintf():
$query = sprintf("SELECT * FROM `tabl` WHERE `data` = %d;",$data); // если требуется целое число
$query = sprintf("SELECT * FROM `table` WHERE `data` = %f;",$data); // если требуется число с плавающей точкой

во всех этих случаях, если в переданном значении после числа идет какой-то символ типа одинарной или двойной кавычки, например 51’ или 51”, или 51abc - то окончание будет отброшено и в запрос будет вставлено только число 51.

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

$data = $_GET["id"];
 
// проверяет - все ли символы в переданной строке числовые.
if (ctype_digit($data)) {
  // осуществляем запрос к базе данных...
} else {
  $data = 1; // значение по-умолчанию
  // осуществляем запрос к базе данных со значением по-умолчанию...
}
 
// или (проверяет - является ли значение числом с плавающей точкой):
if (is_float($data)) {
  // осуществляем запрос к базе данных...
} else {
  $data = 1.5; // значение по-умолчанию
  // осуществляем запрос к базе данных со значением по-умолчанию...
}
 
// или аналогично:
if (is_numeric($data)) {
  // осуществляем запрос к базе данных...
} else {
  $data = 1; // значение по-умолчанию
  // осуществляем запрос к базе данных со значением по-умолчанию...
}
 
// и другие аналогичные функции...

Однако, в самом последнем примере, следует учитывать, что функция is_numeric() определяет числа не только в целочисленной или дробной десятичной записи, но и например в шестнадцатиричной записи, то есть “5A3”  - будет опознано как верное числовое значение.

Проверка структуры присланной строки с помощью регулярных выражений.

Если по замыслу предполагается, что присылаемая строка имеет какую-то структуру, например это e-mail клиентов, или телефон, или дата - то есть смысл “прогнать” такие значения через регулярные выражения. Тем самым, Вы сможете проверить точность введенных данных и одновременно отсечь все нежелательные символы и недопустимые модификации передаваемой строки:

$email = $_POST["mail"];
if (preg_match("/^\w @[a-zA-Z_] ?\.[a-zA-Z]{2,6}$/",$email)) {
  // формируем запрос к базе данных
} else {
  // выводим ошибку или формируем строку запроса по-умолчанию
}

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

$num = $_POST["id"];
if (preg_match("/^\d{4}$/",$num)) {
  // формируем запрос к базе данных
} else {
  // выводим ошибку или формируем строку запроса по-умолчанию
}
 
// или:
 
$login = $_POST["login"];
if (preg_match("/^[a-z]{5,10}$/",$login)) {
  // формируем запрос к базе данных
} else {
  // выводим ошибку или формируем строку запроса по-умолчанию
}

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

Использование массива значений передаваемого параметра.

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

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

$engine_types = array ("diesel","petrol","hybrid","gas","biofuels");
 
$engine = (int)$_POST["engine"];
if ($engine_types[$engine]) {
  //формируем запрос к базе данных c параметром $engine_types[$engine]
  $query = "SELECT * FROM `auto` WHERE `engine_type` = "".$engine_types[$engine].""";
  // далее запрос...
} else {
  // формируем запрос по умолчанию "вывести все авто"
  $query = "SELECT * FROM `auto`";
  // далее запрос...

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

Подготовленные запросы к базе данных.

Второе, к чему нам следует придти, после всего вышесказанного - это отказ от конкатенации или замены подстроки при формировании запроса к базе данных. На самом деле перечисленные выше методы защиты от SQL-инъекций необходимы и уже достаточны для того чтобы быть практически спокойными, что в наших разработках таких уязвимостей нет. Однако, все же наличие операции конкатенации оставляет возможность SQL-инъекции, например в случае не внимательной обработки данных регулярными выражениями (забыли запретить кавычки в передаваемом параметре, например).  Поэтому от сложения строк и замены подстроки запроса  - следует отказаться в пользу так называемых подготовленных или параметризованных запросов к СУБД.

Подготовленные запросы - это реализованный в PHP инструмент, позволяющий с одной стороны, значительно оптимизировать нагрузку и скорость работы приложений, взаимодействующих с базами данных; с другой - позволяющий полностью исключить возможность SQL-внедрений именно через параметры SQL-запроса. Однако, это не убирает необходимости проверять входящие данные, хотя бы для корректности возвращаемой информации, ведь пользователь, например, может опечататься набирая свой e-mail. К тому же, может быть полезной предварительная проверка входящих данных для того, чтобы более оперативно детектировать попытки поиска хакером уязвимости типа SQL-инъекция.

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

Процедурный стиль.

// ...ключ подключения к базе данных хранится в переменной $db_connect
 
// подготовка выражения запроса к БД.
$query_tmpl = mysqli_prepare($db_connect, "SELECT * FROM `table` WHERE `typeid` = ? AND `status` = ? AND `power` = ?");
 
// привязка переменных к псевдопеременным запроса
mysqli_stmt_bind_param($query_tmpl, "isd", $typeid, $status, $power);
 
 
$typeid = (int)$_POST["id"];
$status = status_types[(int)$_POST["level"]];
$power = (float)$_POST["energy"];;
 
// выполнение подготовленного запроса
mysqli_stmt_execute($query_tmpl);
 
// дальнейшая обработка результата запроса...

Функция mysqli_prepare() подготавливает запрос, этот запрос анализируется и под него заранее выделятеся память. Далее, через mysqli_stmt_bind_param() к псевдопеременным “?” в порядке следования в запросе, привязывается переменная кода, при этом указывается тип данных передаваемых в каждой переменной: i - integer, s - string, d - double (float), b - blob (большой объем данных разбиваемый на пакеты и пересылаемый частями). mysqli_stmt_execute() - выполняет подготовленный запрос, сохраненный в идентификаторе $query_tmpl.

Объектно-ориентированный стиль.

Тот же самый код, только в стиле объектно-ориентированного программирования:

// ...ключ подключения к базе данных хранится в переменной $db_connect
 
// подготовка выражения запроса к БД.
$query_tmpl = $db_connect -> prepare("SELECT * FROM `table` WHERE `typeid` = ? AND `status` = ? AND `power` = ?");
 
// привязка переменных к псевдопеременным запроса
$query_tmpl -> bind_param("isd", $typeid, $status, $power);
 
 
$typeid = (int)$_POST["id"];
$status = status_types[(int)$_POST["level"]];
$power = (float)$_POST["energy"];;
 
// выполнение подготовленного запроса
$query_tmpl -> execute();
 
// дальнейшая обработка результата запроса...

Использование подготовленных запросов уже само по себе полностью закрывает возможность SQL-инъекции через параметры запроса. А проверка входящих данных сделает работу приложения устойчивой к ошибкам ввода и более дружелюбной для пользователя.

Однако, важно понимать, что подстановка значений в запрос через функционал подготавливаемых запросов возможна только в определенные места запроса, например:

  • в список VALUES(), оператора INSERT,
  • в список значений полей SET оператора UPDATE,
  • после оператора WHERE, задающего условие выполняемого запроса.

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

Дополнительные принципы безопасности.

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

Сообщения об ошибках.

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

Шифрование данных.

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

Привелегии пользователя СУБД.

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

Область видимости переменных.

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

Непредсказуемость.

Очень частно имена передаваемых параметров, не важно каким способом, совпадают с именами столбцов таблиц базы данных. Это удобно и логично, но часто упрощает процесс анализа уязвимости. Поэтому, пусть поля в форме, параметры GET-запроса, параметры cookies - имеют названия отличные от названий таблиц, полей и значений в базе данных. К тому же, следует избегать стандартных названий типа: user, id, pass, password, login, admin и другие... Иначе есть возможность в процессе анализа уязвимости предугадывать их значения, что упрощает и ускоряет взлом.

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

Обработка вывода.

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

Проверяйте данные на стороне сервера.

Технологии javascript/jquery/ajax дают возможность проверять данные в браузере, на стороне пользователя, и не редко разработчики на этом и останавливаются. Почему-то многим не приходит в голову, что работу скриптов работающих на стороне клиента (то есть в браузере) может контролировать сам клиент. Поэтому проверка на клиентской части - это хорошо и красиво, но всегда проверяйте данные и на сервере - там возможности контроля со стороны пользователя сведены к минимуму, если поддерживать безопасность со всех сторон.

Логирование запросов.

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

Ссылки для скачивания.

Здесь Вы можете скачать сопроводительные материалы к статье:

regexp.pdf (98Kb)

* * * * * * * * * * * *

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

Комментарии к статье:


Всеволод Чупрыгин © webengineer.pro 2014. Все права защищены.
Копирование материалов сайта разрешено только с указанием имени автора (Всеволод Чупрыгин) и прямой индексируемой ссылки на источник на сайте www.WebEngineer.pro.
ИП Чупрыгин Всеволод Андреевич, ИНН: 333410747832, ОГРН: 311333426300044
http://vkontakte.ru/chuprygin_va, Google +

.
Проверить аттестат
Мы принимаем Webmoney Мы принимаем практически все платежи через Robokassa Мы принимаем Яндекс.Деньги Мы принимаем платежи через QIWI. Мы принимаем платежи через привязанные к QIWI карты VISA/Mastercard.