• доступно о веб-разработке
19.11.2012 Базы Данных, Технологии ,

Безопасность при работе с базами данных

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

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

Вы знаете, что в обществе всегда есть процент жуликов. Ровно тоже самое и в Интернете, только в силу вседозволенность жуликов в нем больше.

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

Честно говоря, всегда сочувствовал таким людям, но они есть, и это факт.

Поэтому безопасность — это первый вопрос, о котором мы должны думать при веб-разработке.

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

Пока же обсудим, как сделать работу с базой данных безопасной.

Любые данные, которые вы получаете от пользователя должны быть отфильтрованы.

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

И вот у вас есть скрипт /user.php, который может отобразить профиль пользователя, логин которого вы передаете через GET-параметр.

Вот так будет выглядеть адрес запроса:

/user.php?login=username

Как вы обрабатываете данные? Вот неопытный программист сделает это так:

<?php

$pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password');
$statement = $pdo->query("SELECT * FROM users WHERE login = '{$_GET[login]}'";
$row = $statement->fetch(PDO::FETCH_ASSOC);
// Далее вывод информации о пользователе

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

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

/user.php?login=» UNION SELECT  1, user, password FROM admin

Таким образом он может, получить доступ к данным даже из других таблиц.

Этот тип атаки называется инъекция SQL (SQL-injection).

Более подробно о нём вы можете прочитать по ссылкам:

http://ru.wikipedia.org/wiki/Внедрение SQL-кода

http://forum.antichat.ru/thread43966.html

http://www.xakep.ru/post/19146/default.asp

http://habrahabr.ru/search/?q=sql-injection

Как избежать этого? Фильтровать данные.

Например, если вы получаете целое число, то убедитесь в том, что вы получаете целое число или приведите тип переменной к целому числу.

/user.php?id=123

<?php

$user_id = intval($_GET['id']);

В каждом классе для работы с БД есть специальные методы экранирования (чтобы злоумышленник не мог разорвать кавычки в запросе):

<?php

$login = $pdo->quote($_GET['login'];

$result = $pdo->query('SELECT * FROM users WHERE login ='".$login."'');

Более того, вы можете при помощи специальных методов того же PDO задать тип переменной в запросе:

<?php

$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);
$sth->execute();

В старых драйверах есть метод mysql_real_escape_string, в более новом mysqli одноименный метод:

<?php
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");

/* check connection */
if (mysqli_connect_errno()) {
    printf("Connect failed: %s\n", mysqli_connect_error());
    exit();
}

$mysqli->query("CREATE TEMPORARY TABLE myCity LIKE City");

$city = "'s Hertogenbosch";

/* this query will fail, cause we didn't escape $city */
if (!$mysqli->query("INSERT into myCity (Name) VALUES ('$city')")) {
    printf("Error: %s\n", $mysqli->sqlstate);
}

$city = $mysqli->real_escape_string($city);

/* this query with escaped $city will work */
if ($mysqli->query("INSERT into myCity (Name) VALUES ('$city')")) {
    printf("%d Row inserted.\n", $mysqli->affected_rows);
}

$mysqli->close();

Задание. Создайте скрипт /users.php, который бы получал запрос $_GET, как указано выше,  и обращался бы к базе за поиском пользователя. Базу можете создать на ходу из 2-3 полей (id, login, password) и вбить в неё парочку значений.

Сделайте реализацию запроса безопасной. Привыкните думать о безопасности в первую очередь. Ну а мы к этой теме еще не раз вернемся.

Поделиться

Комментарии Правила дискуссии

Читайте ранее:
Глобальные переменные

Как в обычной жизни мы начинаем все не с чистого листа(есть заданная система координат — время, погода и т.д.), так...

Закрыть