19 смертных грехов, угрожающих безопасности программ
Шрифт:
Напомним, что ущерб не ограничивается данными, хранящимися в базе. Внедрение SQL может скомпрометировать сам сервер, а не исключено, что и сеть целиком. Для противника скомпрометированный сервер базы данных – это лишь ступень к новым великим свершениям.
Подверженные греху языки
Все языки программирования, применяемые для организации интерфейса с базой данных, уязвимы! Но прежде всего это относится к таким языкам высокого уровня, как Perl, Python, Java, технологии «серверных страниц» (ASP, ASP.NET, JSP и PHP), С# и VB.NET. Иногда
Как происходит грехопадение
Самый распространенный вариант греха совсем прост – атакующий подсовывает приложению специально подготовленные данные, которые тот использует для построения SQL–предложения путем конкатенации строк. Это позволяет противнику изменить семантику запроса. Разработчики продолжают использовать конкатенацию, потому что не знают о существовании других, более безопасных методов. А если и знают, то не применяют их, так как, говоря откровенно, конкатенация – это так просто, а для вызова других функций еще подумать надо. Мы могли бы назвать таких программистов лентяями, но не станем.
Реже встречается другой вариант атаки, заключающийся в использовании хранимой процедуры, имя которой передается извне. А иногда приложение принимает параметр, конкатенирует его с именем процедуры и исполняет получившуюся строку.
Греховность С#
Вот классический пример внедрения SQL:
using System.Data;
using System.Data.SqlClient;
string ccnum = "None";
try {
SqlConnection sql = new SqlConnection(
@"data source=localhost;" +
"user id=sa;password=pAs$w0rd;");
sql.Open;
string sqlstring="SELECT ccnum" +
" FROM cust WHERE id=" + Id;
SqlCommand cmd = new SqlCommand(sqlstring,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) {
// Ой!
}
Ниже приведен по существу такой же код, но SQL–предложение строится с помощью замены подстроки, а не конкатенации. Это тоже ошибка.
using System.Data;
using System.Data.SqlClient;
string ccnum = "None";
try {
SqlConnection sql = new SqlConnection(
@"data source=localhost;" +
"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
И снова тот же дефект, но на этот раз в программе на достопочтенном Perl:
#!/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
Подобный код встречается не так часто, но автор пару раз наталкивался на него в промышленных системах. Показанная ниже хранимая процедура просто принимает строку в качестве параметра и исполняет ее!