19 смертных грехов, угрожающих безопасности программ
Шрифт:
"user id=sa;password=pAs$w0rd;");
sql.Open;
string sqlstring="SELECT ccnum" +
" FROM cust WHERE id=%ID%";
String sqlstring2 = sqlstring.Replace("%ID", id);
SqlCommand cmd = new SqlCommand(sqlstring2,sql);
try {
ccnum = (string)cmd.ExecuteScalar;
} catch (SqlException se) {
Status = sqlstring + " failed\n\r";
foreach (SqlError e in se.Errors) {
Status += e.Message + "\n\r";
}
} catch (SqlException e) {
// Ой!
}
Греховность PHP
Вот та же классическая ошибка, но в программе на языке РНР, часто применяемом для доступа к базам данных.
<?php
$db = mysql_connect("localhost","root","$$sshhh...!");
mysql_select_db("Shipping",$db);
$id = $HTTP_GET_VARS["id"];
$qry = "SELECT ccnum FROM cust WHERE id = %$id%";
$result = mysql_query($qry,$db);
if ($result) {
echo mysql_result($result,0,"ccnum");
} else {
echo "No result! " . mysql_error;
}
?>
Греховность Perl/CGI
И
#!/usr/bin/perl
use DBI;
use CGI;
print CGI::header;
$cgi = new CGI;
$id = $cgi->param(\'id\');
$dbh = DBI->connect(\'DBI:mysql:Shipping:localhost\',
\'root\',
\'$3cre+\')
or print "Ошибка connect : $DBI::errstr";
$sql = "SELECT ccnum FROM cust WHERE id = " . $id;
$sth = $dbh->prepare($sql)
or print "Ошибка prepare : $DBI::errstr";
$sth->execute
or print "Ошибка execute : $DBI::errstr";
# Вывести данные
while (@row = $sth->fetchrow_array ) {
print "@row<br>";
}
$dbh->disconnect; print "</body></html>";
exit;
Греховность Java
Еще один распространенный язык, Java. Подвержен внедрению SQL по той же схеме.
import java.*;
import java.sql.*;
public static boolean doQuery(String Id) {
Connection con = null;
try
{
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
con = DriverManager.getConnection("jdbc:microsoft:sqlserver: " +
"//localhost:1433", "sa", "$3cre+");
Statement st = con.createStatement;
ResultSet rs = st.executeQuery("SELECT ccnum FROM cust WHERE id=" +
Id);
while (rx.next) {
// Полюбоваться на результаты запроса
}
rs.close;
st.close;
}
catch (SQLException e)
{
// Ой!
return false;
}
catch (ClassNotFoundException e)
{
// Не найден класс
return false;
}
finally
{
try
{
con.close;
} catch(SQLException e) {}
}
return true;
}
Греховность SQL
Подобный код встречается не так часто, но автор пару раз наталкивался на него в промышленных системах. Показанная ниже хранимая процедура просто принимает строку в качестве параметра и исполняет ее!CREATE PROCEDURE dbo.doQuery(@query nchar(128))
AS
exec(@query)
RETURN
А вот следующий код распространен куда шире и не менее опасен:
CREATE PROCEDURE dbo.doQuery(@id nchar(128))
AS
DECLARE @query nchar(256)
SELECT @query = \'select ccnum from cust where id = \'\'\' + @id + \'\'\'\'
EXEC @query
RETURN
Здесь
опасная конкатенация строк выполняется внутри процедуры. То есть вы по–прежнему совершаете постыдный грех, даже если процедура вызвана из корректного кода на языке высокого уровня.Стоит поискать и другие операторы конкатенации, имеющиеся в SQL, а именно «+» и «||», а также функции CONCAT и CONCATENATE.
Во всех этих примерах противник контролирует переменную Id. Важно всегда представлять себе, что именно контролирует атакующий, это поможет понять, есть реальная ошибка или нет. В данном случае противник может задать любое значение переменной Id, участвующей в запросе, и тем самым управлять видом строки запроса. Последствия могут оказаться катастрофическими.
Классическая атака состоит в том, чтобы видоизменить SQL–запрос, добавив лишние части и закомментарив «ненужные». Например, если противник контролирует переменную Id, то может задать в качестве ее значения строку 1 or 2>1 – – , тогда запрос примет такой вид:
SELECT ccnum FROM cust WHERE id=1 or 2>1 – –
Условие 2>1 истинно для всех строк таблицы, поэтому запрос возвращает все строки из таблицы cust, другими словами, номера всех кредитных карточек. Можно было бы воспользоваться классической атакой «1 = 1», но сетевые администраторы часто включают поиск такой строки в системы обнаружения вторжений (IDS), поэтому мы применили условие «2>1», которое столь же эффективно, но «проходит под лучом радара».
Оператор комментария – – убирает из поля зрения сервера все последующие символы запроса, которые могла бы добавить программа. В одних базах данных для комментирования применяются символы —, в других – #. Проверьте, что воспринимает в качестве комментария ваша база.
Различных вариантов атак слишком много, чтобы перечислять их здесь, дополнительный материал вы найдете в разделе «Другие ресурсы».
Родственные грехи
Во всех приведенных выше примерах демонстрируются и другие грехи:
□ соединение от имени учетной записи с высоким уровнем доступа;
□ включение пароля в текст программы;
□ сообщение противнику излишне подробной информации в случае ошибки.
Рассмотрим их по порядку. Везде соединение устанавливается от имени административного или высокопривилегированного пользователя, хотя достаточно было бы пользователя, имеющего доступ только к одной базе данных. Это означает, что противник потенциально сможет манипулировать и другой информацией, а то и всем сервером. Короче говоря, соединение с базой данных от имени пользователя с высоким уровнем доступа – скорее всего, ошибка и нарушение принципа наименьших привилегий.
«Зашивание» паролей в код – почти всегда порочная идея. Подробнее см. грех 11 и 12 и предлагаемые там «лекарства».
Наконец, в случае ошибки противник получает слишком много информации. Воспользовавшись ей, он сможет получить представление о структуре запроса и, быть может, даже узнать имена объектов базы. Более подробную информацию и рекомендации см. в грехе 6.
Где искать ошибку
Любое приложение, обладающее перечисленными ниже характеристиками, подвержено риску внедрения SQL:
□ принимает данные от пользователя;
□ не проверяет корректность входных данных;
□ использует введенные пользователем данные для запроса к базе;
□ применяет конкатенацию или замену подстроки для построения SQL–запроса либо пользуется командой SQL exec (или ей подобной).