Защита от 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
Еще один достаточно надежный вариант от sql injection — это использование серверных функций.
Метод с отсеиванием слов из "черного списка" не всегда может дать положительный результат. На пример, мы фильтруем(вырезаем) из запроса слово delete, что же будет если злоумышленник составит следующий вариант delDELETEete?
Здравствуй, Адель! Очень интересный блог, много полезного нашел.
Можно задать тебе вопрос? Проблема на движке .NET блога (на котором и твой блог). Например, строка запроса http://www.drfaust.ru/post/2009/06/04/os-aviable-folders.aspx?[b]id=0[/b] сразу выдает (ошибка 404), что где-то внутри происходит UrlRewriting с использованием параметра id. Это происходит потому, что все прочие параметры запроса, просто присоединяются к пути перезаписи URL. И это правильно. Но как обойти дублирование параметров?
Спасибо за полезную статью!
Прошу взять на заметку, что в методе IsLegalQuery для поиска разрешённых символов гораздо лучше использовать несложное регулярное выражение и класс RegEx :)