Опубликован: 07.05.2010 | Доступ: свободный | Студентов: 1678 / 62 | Оценка: 4.56 / 4.06 | Длительность: 34:11:00
Лекция 11:

Основы ADO.NET

Атаки на базу данных внедрением SQL

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

Рассмотрим это на примере.

  • Сделайте копию с именем UserSqlGridData.aspx из файла GridViewDataReader.aspx и назначьте ее стартовой
  • Разместите выше элемента GridView текстовое поле TextBox и кнопку Button, чтобы пользовательский интерфейс на этапе проектирования был таким

  • Откорректируйте дескрипторный и встроенный коды страницы так
    <%@ Page Language="C#" %>
        
    <script runat="server">
        
        protected void Page_Load(object sender, EventArgs e)
        {
            // Извлекли строку соединения из web.config
            string connectString = System.Web.Configuration.WebConfigurationManager.
                ConnectionStrings["Northwind"].ConnectionString;
        
            // Создали объект соединения
            System.Data.SqlClient.SqlConnection con =
                new System.Data.SqlClient.SqlConnection(connectString);
            
            // Создали строку SQL-запроса
            string sql =
                "SELECT Orders.CustomerID, Orders.OrderID, COUNT(UnitPrice) AS Items, "
              + "SUM(UnitPrice * Quantity) AS Total FROM Orders "
              + "INNER JOIN[Order Details] "
              + "ON Orders.OrderID = [Order Details].OrderID "
              + "WHERE Orders.CustomerID = '" + TextBox1.Text + "' "
              + "GROUP BY Orders.OrderID, Orders.CustomerID";
        
            // Создали объект Command
            System.Data.SqlClient.SqlCommand cmd =
                new System.Data.SqlClient.SqlCommand(sql, con);
        
            using (con)
            {
                // Открыли соединение
                con.Open();
        
                // Выполняем команду и получаем результирующий набор данных
                System.Data.SqlClient.SqlDataReader reader =
                    cmd.ExecuteReader();
        
                // Подключаем элемент управления GridView
                // к полученному результирующему набору
                GridView1.DataSource = reader;
        
                // Наполняем элемент управления GridView
                // всеми извлеченными записями DataReader
                GridView1.DataBind();
        
                // Закрываем набор данных
                reader.Close();
            }
        }
    </script>
        
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head id="Head1" runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div>
                Введите ID пользователя:<br />
                <asp:TextBox ID="TextBox1" runat="server" Width="248px" Font-Bold="True">ALFKI</asp:TextBox> 
                <asp:Button ID="Button1" runat="server" Text="Получить" /><br />
                <br />
                <asp:GridView ID="GridView1" runat="server">
                </asp:GridView>
            </div>
        </form>
    </body>
    </html>
  • Исполните страницу и должен получиться такой результат

CustomerID OrderID Items Total
ALFKI 10643 3 1086,0000
ALFKI 10692 1 878,0000
ALFKI 10702 2 330,0000
ALFKI 10835 2 851,0000
ALFKI 10952 2 491,2000
ALFKI 11011 2 960,0000

При значении текстового поля ALFKI вычисленный SQL-оператор будет иметь такой вид:

string sql =
    "SELECT Orders.CustomerID, Orders.OrderID, COUNT(UnitPrice) AS Items, "
  + "SUM(UnitPrice * Quantity) AS Total FROM Orders "
  + "INNER JOIN[Order Details] "
  + "ON Orders.OrderID = [Order Details].OrderID "
  + "WHERE Orders.CustomerID = 'ALFKI' "
  + "GROUP BY Orders.OrderID, Orders.CustomerID";

Злоумышленник может попытаться подделать оператор SQL. Главной целью такой атаки является получение сообщения об ошибке. Если ошибка не обрабатывается правильно, то злоумышленник получит низкоуровневую информацию, которая может быть использована для разработки более изощренной атаки.

Предположим, что злоумышленник введет в текстовое поле следующий текст

ALFKI' OR '1' = '1

В этом случае строка запроса будет такой

string sql =
    "SELECT Orders.CustomerID, Orders.OrderID, COUNT(UnitPrice) AS Items, "
  + "SUM(UnitPrice * Quantity) AS Total FROM Orders "
  + "INNER JOIN[Order Details] "
  + "ON Orders.OrderID = [Order Details].OrderID "
  + "WHERE Orders.CustomerID = 'ALFKI' OR '1' = '1' "
  + "GROUP BY Orders.OrderID, Orders.CustomerID";

Этот оператор вернет все записи о заказах, поскольку условие 1=1 истинно для всех строк

  • Исполните страницу UserSqlGridData.aspx и введите как злоумышленник в текстовое поле строку
    ALFKI' OR '1' = '1

Должен получиться результат, малая часть которого приведена ниже


Начальная часть результата, возвращенного по искаженной строке запроса
CustomerID OrderID Items Total
VINET 10248 3 440,0000
TOMSP 10249 2 1863,4000
HANAR 10250 3 1813,0000
VICTE 10251 3 670,8000
SUPRD 10252 3 3730,0000
HANAR 10253 3 1444,8000
CHOPS 10254 3 625,2000
RICSU 10255 4 2490,5000
WELLI 10256 2 517,8000
HILAA 10257 3 1119,9000
ERNSH 10258 3 2018,6000
CENTC 10259 2 100,8000

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

Возможны и более сложные атаки. Например, злонамеренный пользователь может просто закомментировать остаток оператора SQL, добавив два тире (--). Эта атака специфична для SQL Server, но аналогичная атака возможна и в MySQL, если использовать символ #, и в Oracle, если задействовать точку с запятой (;).

Злоумышленник может применить пакетную команду, чтобы выполнить произвольный оператор SQL. При использовании поставщика данных SQL Server достаточно просто добавить точку с запятой, за которой передать дополнительную команду. Таким образом можно, например, удалить содержимое другой таблицы, или даже вызвать системную хранимую процедуру SQL Server xp_cmdshell, чтобы запустить произвольную программу из командной строки.

Вот что злоумышленник может ввести в текстовое поле, чтобы осуществить более изощренную атаку внедрением SQL, удалив все строки из таблицы Customers:

ALFKI'; DELETE * FROM Customers --

Не делайте этого с нашей учебной базой!!!

Для того, чтобы противостоять атаке внедрением, следует помнить несколько правил:

  1. Использовать свойство TextBox.MaxLength, чтобы предотвратить длинный ввод, когда в этом нет необходимости
  2. Обрабатывать ошибки самому, чтобы ограничить информацию стандартного исключения Exception.Message
  3. Выявлять и удалать из строки ввода специальные символы, например, заменять одиночную кавычку парой одиночных кавычек
    string ID = TextBox1.Text.Replace("'", "''");
  4. Использовать параметризованные команды или хранимые процедуры, которые предусматривают собственную защиту от атак внедрением SQL
  5. Ограничить права учетной записи, от имени которой выполняется доступ к базе данных. Этот способ может предовратить атаки, связанные с удалением таблиц, но не препятствует похищению информации, поскольку права по извлечению информации ограничить нельзя