Дэвид Лебланк - 19 смертных грехов, угрожающих безопасности программ
Примеры из реальной жизни
Следующие примеры внедрения SQL–команд взяты из базы данных CVE (http://cve.mitre.org).
CAN–2004–0348
Цитата из бюллетеня CVE: «Уязвимость, связанная с внедрением SQL в сценарии viewCart.asp из программного обеспечения корзины для покупок компании SpiderSales, позволяет удаленному противнику выполнить произвольный SQL–запрос, воспользовавшись параметром userld».
Многие сценарии в программах SpiderSales не проверяют параметр userld, и этим можно воспользоваться для проведения атаки с внедрением SQL. Успешная атака позволяет противнику получить доступ к интерфейсу администратора SpiderSales и прочитать любую информацию из базы данных электронного магазина.
CAN–2002–0554
Цитата из бюллетеня CVE: «Модуль IBM Informix Web DataBlade 4.12 позволяет удаленному противнику обойти контроль доступа и прочитать произвольный файл путем атаки с внедрением SQL через HTTP–запрос».
Модуль Web DataBlade для базы данных Informix SQL динамически генерирует HTML–страницу на основе данных. Уязвимость отмечена в нескольких версиях Web DataBlade. Можно внедрить SQL–команды в любой запрос, обрабатываемый этим модулем. Результатом может стать раскрытие секретной информации или повышение уровня доступа к базе данных.
Искупление греха
Простейший и наиболее безопасный способ искупления – никогда не доверять входным данным, на основе которых формируется SQL–запрос, и пользоваться подготовленными или параметризованными предложениями (prepared statements).
Проверяйте все входные данные
Займемся для начала первой рекомендацией: никогда не доверять входным данным. Следует всегда проверять, что данные, подставляемые в SQL–предложение, корректны. Если вы работаете на языке достаточно высокого уровня, то проще всего воспользоваться для этого регулярным выражением.
Никогда не применяйте конкатенацию для построения SQL–предложений
Следующая рекомендация состоит в том, чтобы никогда не строить SQL–предложения посредством конкатенации или замены подстроки. Никогда! Пользуйтесь только подготовленными или параметризованными запросами. В некоторых технологиях это называется связыванием параметров (binding). В примерах ниже продемонстрированы некоторые из таких безопасных конструкций.
Примечание. Во всех примерах информация о параметрах соединения не хранится в тексте программы; мы вызываем специальные функции, которые считывают данные из пространства приложения. Искупление греха в С#
...public string Query(string Id) {
string ccnum;
string sqlstring = "";
// пропускаем только корректные ID (от 1 до 8 цифр)
Regex r = new Regex(@"^d{1,8}$");
if (!r.Match(Id).Success)
throw new Exception("Неверный ID. Попробуйте еще раз.");
try {
SqlConnection sqlConn = new SqlConnection(GetConnection);
string str = "sp_GetCreditCard";
cmd = new SqlCommand(str, sqlConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID", Id);
cmd.Connection.Open();
SqlDataReader read = myCommand.ExecuteReader();
ccnum = read.GetString(0);
}
catch (SqlException se) {
throw new Exception("Ошибка – попробуйте еще раз.");
}
}
Искупление греха в PHP 5.0 и MySQL версии 4.1 и старше
...<?php
$db = mysqli_connect(getServer(), getUid(), getPwd());
$stmt = mysqli_prepare($link, "SELECT ccnum FROM cust WHERE id=?");
$id = $HTTP_GET_VARS["id"];
// пропускаем только корректные ID (от 1 до 8 цифр)
if (preg_match('d{1,8}$/',$id);
mysqli_stmt_bind_param($stmt, "s", $id);
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $result);
mysqli_stmt_fetch($stmt);
if (empty($name)) {
echo "Результата нет!";
} else {
echo $result;
}
else {
echo "Неверный ID. Попробуйте еще раз.");
}
?>
Версии PHP ниже 5.0 не поддерживают связывания параметров с помощью показанной выше функции mysqli_prepare. Однако если для работы с базами данных вы пользуетесь архивом расширений PHP PEAR (PHP Extensions and Applications Repository, http://pear.php.net), то там есть функции DB_common::prepare() и DB_common::query() для подготовки параметризованных запросов.
Искупление греха в Perl/CGI
...#!/usr/bin/perl
use DBI;
use CGI;
print CGI::header();
$cgi = new CGI;
$id = $cgi->param('id');
// пропускаем только корректные ID (от 1 до 8 цифр)
exit unless ($id =~ /^[d]{1,8}$);
print "<html><body>";
// Параметры соединения получаем извне
$dbh = DBI->connect(conn(),
conn_name(),
conn_pwd())
or print "Ошибка connect";
# детальная информация об ошибке в $DBI::errstr
$sql = "SELECT ccnum FROM cust WHERE id = ?";
$sth = $dbh->prepare($sql)
or print "Ошибка prepare";
$sth->bind_param(1,$id);
$sth->execute()
or print "Ошибка execute";# Вывести данные
while (@row = $sth->fetchrow_array ) {
print "@row<br>";
}
$dbh->disconnect;
print "</body></html>";exit;
Искупление греха в Java с использованием JDBC
...public static boolean doQuery(String Id) {
// пропускаем только корректные ID (от 1 до 8 цифр)
Pattern p = Pattern.compile("^\d{1,8}$");
if (!p.matcher(arg).find())
return false;
Connection con = null;
try
{
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
con = DriverManager.getConnection("jdbc:microsoft:sqlserver: " +
"//localhost:1433", "sa", "$3cre+");
PreparedStatement st = con.prepareStatement(
"exec pubs..sp_GetCreditCard ?");
st.setString(1, arg);
ResultSet rs = st.executeQuery();
while (rx.next()) {
// Получить данные из rs.getString(1)
}
rs.close();
st.close();
}
catch (SQLException e)
{
System.out.println("Ошибка SQL");
return false;
}
catch (ClassNotFoundException e)
{
System.out.println("Ошибка во время исполнения");
return false;
}
finally
{
try
{
con.close();
} catch(SQLException e) {}
}
return true;
}
Искупление греха в ColdFusion
При работе с ColdFusion используйте cfqueryparam в теге <cfquery>, чтобы обезопасить запрос с параметрами.
Искупление греха в SQL
Не следует исполнять в хранимой процедуре строку, полученную из не заслуживающего доверия источника, как процедуру. В качестве одного из механизмов глубоко эшелонированной обороны можно воспользоваться некоторыми функциями для проверки корректности строкового параметра. В примере ниже проверяется, что входной параметр содержит ровно четыре цифры. Заметим, что длина параметра заметно уменьшена, чтобы усложнить передачу любой другой входной информации.
...CREATE PROCEDURE dbo.doQuery(@id nchar(4))
AS
DECLARE @query nchar(64)
IF RTRIM(@id) LIKE '[0-9][0-9][0-9][0-9]'
BEGIN
SELECT @query = 'select ccnum from cust where id = ''' + @id + ''''
EXEC @query
END
RETURN
Или еще лучше – потребуйте, чтобы параметр был целым числом:
...CREATE PROCEDURE dbo.doQuery(@id smallint)
В Oracle lOg, как и в Microsoft SQL Server 2005, добавлены совместимые со стандартом POSIX регулярные выражения. Поддержка регулярных выражений реализована также для DB2 и Microsoft SQL Server 2000. В MySQL регулярные выражения поддерживаются с помощью оператора REGEXP. Ссылки на все эти решения вы найдете в разделе «Другие ресурсы».
Дополнительные защитные меры
Есть много других способов уменьшить риск компрометации. Например, в РНР можно задать параметр magic_quotes_gpc=l в файле php.ini. Кроме того, запретите доступ ко всем пользовательским таблицам в базе данных, оставив только право исполнять хранимые процедуры. Это не даст противнику напрямую читать и модифицировать данные в таблицах.
Другие ресурсы
□ Writing Secure Code, Second Edition by Michael Howard and David C. LeBlanc (Microsoft Press, 2002), Chapter 12, «Database Input Issues»
□ Sarbanes–Oxley Act of 2002: www.aicpa.org/info/sarbanes–oxley_summary.htm
□ The Open Web Application Security Project (OWASP): www.owasp.org.
□ «Advanced SQL Injection in SQL Server Applications» by Chris Anley: www. nextgenss.com/papers/advanced_sql_injection.pdf
□ Web Applications and SQL Injections: www.spidynamics.com/whitepapers/ WhitepaperSQLInjection.pdf
□ «Detecting SQL Injection in Oracle» by Pete Finnigan: www.securityfocus.com/ infocus/1714
□ «How a Common Criminal Might Infiltrate Your Network» by Jes–per Johansson: www.microsoft.com/technet/technetmag/issues/2005/01 / AnatomyofaHack/default.aspx
□ «SQL Injection Attacks by Example» by Stephen J. Friedl: www.unixqiz.net/ techtips/sql–injection.html
□ Oracle lOg SQL Regular Expressions: http://searchoracle.techtarget.com/ searchOracle/ downloads/1 Og_sql_regular_expressions.doc
□ «Regular Expressions in T–SQL» by Cory Koski: http://sqlteam.com/ item.asp?itemID= 13947
□ «xp_regex: Regular Expressions in SQL Server 2000» by Dan Farino: www.codeproject.com/managed.cpp/xpregex.asp
□ SQLRegEx: www.krell–software.com/sqlregex/regex.asp
□ «DB2 Bringing the Power of Regular Expression Matching to SQL» www–106.ibm.com/developerworks/db2/library/techarticle/0301stolze/ 0301stolze.html
□ MySQL Regular Expressions: http://dev.mysql.com/doc/mysql/en/Regexp.html
□ Hacme Bank: www.foundstone.com/resources/proddesc/hacmebank.htm
Резюме
Рекомендуется
□ Изучите базу данных, с которой работаете. Поддерживаются ли в ней хранимые процедуры? Как выглядит комментарий? Может ли противник получить доступ к расширенной функциональности?
□ Проверяйте корректность входных данных и устанавливайте степень доверия к ним.
□ Используйте параметризованные запросы (также распространены термины «подготовленное предложение» и «связывание параметров») для построения SQL–предложений.
□ Храните информацию о параметрах соединения вне приложения, например в защищенном конфигурационном файле или в реестре Windows.
Не рекомендуется
□ Не ограничивайтесь простой фильтрацией «плохих слов». Существует множество вариантов написания, которые вы не в состоянии обнаружить.
□ Не доверяйте входным данным при построении SQL–предложения.
□ Не используйте конкатенацию строк для построения SQL–предложения даже при вызове хранимых процедур. Хранимые процедуры, конечно, полезны, но решить проблему полностью они не могут.