Rose debug info
---------------

Защита от SQL Injection в ASP.NET

Не думаю, что читатели моего блога не знают что такое SQL Injection.

Типичный пример SQL-запроса проверки логина и пароля:

SELECT login, password FROM users WHERE login=’ + strLogin + ’ AND password = ’ + strPassword + ’ 

если не проверять данные, которые приходят от посетителя страницы, то любой злоумышленник может воспользоваться SQL инъекцией, вписав в поле login: admin, а в поле password: ’ OR ’1’ = ’1 и сможет безпрепятственно получить права администратора, или же просто уничтожить какую-либо таблицу, введя в любое поле

 ’; DROP DATABASE publics —

Вот есть хорошее решение, которое рекомендует Microsoft:

’ URL уязвимой ASP.NET страницы
http://mysite.com/listauthordetails.aspx?SSN=172-32-9999';DROP DATABASE pubs —
’ а вот запрос, который выполнит SQL Server:
SELECT au_lname, au_fname FROM authors WHERE au_id = ’’;DROP DATABASE pubs —

Майкрософт рекомендует не создавать SQL запросы без «типизирования» данных. ADO и ADO.NET поддерживают типизацию параметров (string, data, integer и т. д.) и гарантируют безопасность данных, которые передаются в параметры.

Dim SSN as String = Request.QueryString(«SSN»)
Dim cmd As new SqlCommand(«SELECT au_lname, au_fname FROM authors WHERE au_id = @au_id»)
Dim param = new SqlParameter(«au_id», SqlDbType.VarChar)
param.Value = SSN
cmd.Parameters.Add(param)

Этот код обломает злоумышленника, так как ADO.NET будет знать, что au_id нужно преобразовать в string. Кстати, TableAdapter/DataSet designer встроенный в Visual Studio 2005 использует этот механизм автоматически =)

С другой стороны, достаточно лишь фильтровать символ ’ (одинарная кавычка):

private string SafeSqlLiteral(string inputSQL)
{
  return inputSQL.Replace(«’», «’’»);
}

Пока гуглил интернет, наткнулся на кучу рекомендаций по фильтрованию «нехороших» слов в запросах :-D

Например, вот этот ужас:

Public Shared blackList As String() = {«», «;», «;», «/*», «*/», «@@», _
 «@», «char», «nchar», «varchar», «nvarchar», «alter», _
«begin», «cast», «create», «cursor», «declare», «delete», _
«drop», «end», «exec», «execute», «fetch», «insert», _
«kill», «open», «select», «sys», «sysobjects», «syscolumns», _
«table», «update»}

бр-р-р! Некрасиво и далеко небезопасно. Достаточно прочитать пару статей об SQL инъекциях на xakep.ru и прочитать как обойти такую фильтрацию. Кстати, мой пример с OR 1=1 в этот фильтр бы не попал )))

Сказать по честному, я НЕ пользуюсь довольно надежным решением от майкрософт. Оно работает, и делает то что должно делать, а именно, не дает дурить голову SQL Server-у. Я пользуюсь вторым «ужасным» примером наоборот (пример ниже).

Не люблю правила «разрешено все, что не запрещено». В моем примере работают старые добрые правила OpenBSD: «все что не разрешено — то запрещено» :-D

  private bool IsLegalQuery(string strText)
    {
        bool islegal = false;
        if (strText.Length > 0)
        {
            char[] legalchars = «ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzАБВГДЕЁЖЗИЙКЛМНОПРСТУ
ФХЦЧШЩЭЫЪЬЮЯабвгдеёжзийклмнопрстуфхцчшщэъьыюя01234567890., „.ToCharArray();
            islegal = true;
            // посимвольно проверяем пришедший string
            for (int i = 0; i < strText.Length; i++)
            {
                // если символ в строке отсутсвет в массиве разрешенных, возвращаем false
                if (strText.LastIndexOfAny(legalchars, i, 1) < 0)
                {
                    islegal = false;
                    break;
                }
            }
        }
        return islegal;
    }

Эта функция возвращает false если в строке есть любой символ, который отсутствует в списке разрешенных. В своем коде, я еще и делаю логи «неправильных» SQL запросов.

  protected void btnSearch_Click(object sender, EventArgs e)
    {
        if (IsLegalQuery(txtSearch.Text) == true)
        {
            SearchArticles(txtSearch.Text);
        }
        else
        {
            lblStatus.Text = «Неверный запрос»;
            MakeLogSQL(txtSearch.Text);
        }
    }

Мне почти все равно, а администратору приятно.
Кстати, классная книга по защите ASP.NET приложений Хакинг кода: ASP.NET Web Application Security

 436   2008  
3 комментария
Kosten 2008

Еще один достаточно надежный вариант от sql injection — это использование серверных функций.
Метод с отсеиванием слов из "черного списка" не всегда может дать положительный результат. На пример, мы фильтруем(вырезаем) из запроса слово delete, что же будет если злоумышленник составит следующий вариант delDELETEete?

г-н Тараканофф 2010

Здравствуй, Адель! Очень интересный блог, много полезного нашел.

Можно задать тебе вопрос? Проблема на движке .NET блога (на котором и твой блог). Например, строка запроса http://www.drfaust.ru/post/2009/06/04/os-aviable-folders.aspx?[b]id=0[/b] сразу выдает (ошибка 404), что где-то внутри происходит UrlRewriting с использованием параметра id. Это происходит потому, что все прочие параметры запроса, просто присоединяются к пути перезаписи URL. И это правильно. Но как обойти дублирование параметров?

Nova 2010

Спасибо за полезную статью!

Прошу взять на заметку, что в методе IsLegalQuery для поиска разрешённых символов гораздо лучше использовать несложное регулярное выражение и класс RegEx :)