Использование хранимых процедур
Для выполнения большинства операций с данными приложения могут использовать либо хранимые процедуры, либо динамически формируемые в приложении запросы. С точки зрения безопасности динамически формируемые в приложении запросы обладают рядом серьезных недостатков. Так, они позволяют выполнять атаки типа SQL Injection (ввод данных, инициирующий выполнение неавторизованного запроса) или осуществить неавторизованный доступ к данным. Применение хранимых процедур и параметризованных запросов с этой точки зрения обладает рядом преимуществ.
Для иллюстрации понятия SQL Injection приведем простой пример, взятый нами из книги Майкла Ховарда и Дэвида Леблана «Защищенный код» (издательство «Русская редакция», 2003). Представим себе следующий код клиентского приложения:
String sql = «select * from customer where name= ‘ «+ name +» ’»
значение переменной name в котором вводится пользователем. Если пользователь ввел в эту переменную значение «Иванов», он получит записи таблицы Customer, содержащие в поле Name
значение «Иванов».
Но если он ввел в это поле следующее значение:
Иванов’ or 1=1 —
то такая команда вернет все строки таблицы — записи таблицы Customer, содержащие в поле Name значение «Иванов», плюс записи, удовлетворяющие условию 1=1 (то есть все записи таблицы!). А если злоумышленник ввел в это поле значение
Иванов’ drop table customer
— или, не дай бог, фрагмент кода с вызовом хранимой процедуры xp_cmdshell, которая по недосмотру администратора базы данных оказалась доступной, последствия могут быть самыми плачевными.
С точки зрения безопасности приложения не должны использовать конструкции SELECT, INSERT, UPDATE и DELETE для запросов к базе данных. Вместо этого правильнее обращаться к хранимым процедурам, выполняющим подобные операции. Кроме того, их применение позволяет отказаться от предоставления пользователям прав доступа к таблицам и представлениям. Отметим, что применение хранимых процедур позволяет сделать клиентские приложения независимыми от схемы базы данных, что позволяет изменять ее без необходимости внесения изменений в само приложение. Это упрощает сопровождение таких приложений, позволяя избежать развертывания его новых версий.
Использование хранимых процедур в ряде случаев позволяет выполнять операции над конфиденциальными данными внутри сервера, не предоставляя их клиентскому приложению, а также сократить сетевой трафик — при их грамотном проектировании значительная часть обработки данных может быть осуществлена на сервере, а не в приложении.
Впрочем, и хранимые процедуры сами по себе не являются панацеей от всех бед. Рассмотрим еще два примера, приведенных в книге Майкла Ховарда и Дэвида Леблана. Представим себе следующий код клиентского приложения, использующий хранимую процедуру sp_GetName для получения записей о конкретном клиенте:
Sqlstring = @» exec sp_GetName ‘» + name + +» ’»; SqlCommand cmd = new SqlCommand (sqlstring, sql);
Если пользователь ввел в поле Name
Иванов’ or 1=1 —
он не получит все строки таблицы. Но, введя Иванов’ drop table customer —
он удалит таблицу, как и в предыдущем случае. И наконец, хранимые процедуры бывают и такими:
CREATE PROCEDURE sp_MyProc @input varchar(128) AS exec (@input)
Эта процедура просто выполняет введенный пользователем код и создает серьезную угрозу безопасности. Тем не менее случаи создания столь опасных процедур иногда происходят. Чтобы избежать подобных неприятностей, следует использовать параметризованные запросы и параметризованные вызовы хранимых процедур.