ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 28.04.2024
Просмотров: 75
Скачиваний: 0
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
В результате может быть собрана строка в виде:
select * from table where column in (:param1, :param2, ...)
Да, я собрал строку из маленьких кусочков, но я собирал ее из параметров, а не из значений, которые передавались пользователем. Данные от пользователя попадают в запрос именно через эти параметры.
Теоретически можно использовать данные от пользователя напрямую в SQL- запросах, но для этого их нужно очищать от одинарных кавычек. Не рекомендую вам это делать. Единственный случай, когда я позволяю себе использовать напрямую в запросе какие-то данные от пользователя — это если я ожидаю число. В таких случаях достаточно просто написать фильтр, который будет убирать из пользовательских данных все, что не является числом: function checknum($var)
{
$var=preg_replace("/[Л0-9]/i", "", $var); return $var;
}
Функция получает в качестве параметра переменную и с помощью регулярного выражения вырезает из этой переменной все, что не является числом, т. е. любые другие символы и буквы. Теперь перед использованием параметра, на который может повлиять пользователь (получаемый через форму, запросом get или из cookie), выполняем следующее:
$id = checknum($id);
Таким образом можно гарантировать, что параметр содержит только цифры и ничего другого и является абсолютно безопасным для SQL-запроса.
Я использую этот подход очень редко и очень аккуратно. В реальности, если посмотреть на мой код, то в 99,99999999% случаев будут именно параметры, и, может, в одном случае на миллион будет отфильтрованное числовое значение.
-
Защита от инъекции в C#
Хотя в этой книге я чаще показываю примеры на языке PHP, потому что он очень простой, некоторые из них я буду переводить и на C#. Все предыдущие уязвимости и проблемы, которые я рассматривал до этого, на C# реализовывались абсолютно так же: просто на другом языке пишем тот же код, который я привел на PHP. Тут же есть своя небольшая специфика, поэтому я посвятил уязвимости SQL-инъекции в C# отдельную главу (см. главу 6).
ГЛАВА 6
SQL-инъекция .NET + MS SQL Server
Да, SQL-инъекция возможна и в сценариях, написанных на языках программирования C# и Java. Есть много разговоров о том, что эти два языка программирования безопасны, но это не относится к SQL-инъекции. Оба языка предоставляют хорошую защиту памяти и предоставляют неплохие средства для защиты от SQL Injection, но не исключают проблемы.
С точки зрения поиска уязвимостей процесс проверки сайтов на C# не отличается от PHP, так что поиск второй раз рассматривать смысла нет. С точки зрения использования отличия есть, потому что PHP-сайты обычно работают на ОС Linux, а C# сайты чаще работают на Windows, хотя сейчас набирает популярность платформа .NET Core, которая позволяет писать C# код.
Если вы найдете ошибку в C# коде и получите доступ выполнять через базу данных команды в ОС, то нужно сначала понять, на какой именно операционной системе работает сайт.
-
Особенности MS SQL Server
Мои исследования показывают, что если web-сайт построен на C#, то, скорее всего, он использует базу данных MS SQL Server.
При переходе от одной web-страницы к другой в ссылке чаще всего передаются числовые значения — например, идентификаторы строк из определенной таблицы, которые нужно отобразить. В ASP достаточно удобно реализована типизация. Программист просто указывает, что определенная переменная должна быть числовой, и никаких проблем: все попытки хакера указать что-то вместо числа будут пресекаться.
В .NET доступ к параметрам, которые передают пользователи на сайт, можно получить через специальный объект Request["id"]. В зависимости от того, какую версию фреймворка вы используете и какой паттерн, доступ к этому объекту мож
но получить через глобальную переменную HttpContext или через объект Page, если вы используете новый подход со страницами.
В любом случае, у нас будет объект Request и мы сможем читать из него данные от пользователя.
int id;
if (int.TryParse(Request["id"], out id)) {
// здесь используем число id
}
else {
// здесь мы можем сгенерировать ошибку
}
В этом коде с помощью метода int.TryParse я попытаюсь превратить строку в число, и если это удастся сделать, то результат будет записан в переменную id. В этой переменной будет число и ничего кроме числа, так что если пользователь попытается передать что-то еще, лишние символы приведут к тому, что TryParse даст нам это понять.
В поле для поиска необходимо вводить слова и любые читаемые символы, а значит, переменная для хранения искомого текста должна быть текстовой. Здесь уже нет возможности что-то убрать автоматически, и C# воспринимает получаемые данные в том виде, в котором указывает их пользователь. И вот тут начинает действовать волшебная одинарная кавычка.
-
Опасные процедуры MS SQL Server
Самое опасное с точки зрения безопасности — использование процедуры xp_cmdshell, которая позволяет выполнять системные команды. Запрос на выполнение команды может выглядеть следующим образом:
'; exec master..xp_cmdshell 'команда' —
Например, если вы хотите проверить связь с определенным компьютером командой ping, то можно выполнить:
'; exec master..xp_cmdshell 'ping servername.com' —
С помощью процедуры xp_cmdshell можно достаточно легко создать какой-нибудь файл, например:
'; exec master..xp_cmdshell '"" >> hack.php' —
Теперь, если у вас установлен интерпретатор PHP, хакер сможет использовать файл hack.php для более удобного выполнения команд. А можно произвести и дефейс. Если главная web-страница — это index.htm, то достаточно выполнить:
'; exec master..xp_cmdshell '"
You a hacked
" >> index.htm' —
Процедура sp_who позволяет просмотреть, кто сейчас подключен к серверу: exec sp_who
Пример результата выполнения этого SQL-запроса:
spid ecid
status
loginame host
1 1
1 1
1 1—1 1
1 Д l
dbname
cmd
9
0
background
sa
0
master
TASK MANAGER
10
0
background
sa
0
master
TASK MANAGER
11
0
background
sa
0
master
TASK MANAGER
51
0
runnable
CYD\flenov
0
Northwind
SELECT
52
0
sleeping
CYD\flenov
0
master
AWAITING COMMAND
Подробную информацию о текущей базе данных можно получить и с помощью процедуры sp_heip: exec sp_help
Пример результата выполнения этой процедуры:
NAME
Имя
Owner
Владелец
Object Type
Тип объекта
Invoices
dbo
view
Order Subtotals
dbo
view
Orders Qry
dbo
view
Quarterly Orders
dbo
view
Sales by Category
dbo
view
Sales Totals by Amount dbo
view
Sysconstraints
dbo
view
Syssegments
dbo
view
Categories
dbo
user table
CustomerCustomerDemo
dbo
user table
CustomerDemographics
dbo
user table
Customers
dbo
user table
Employees
dbo
user table
Syscolumns
dbo
system table
Syscomments
dbo
system table
Sysdepends
dbo
system table
sysfilegroups
dbo
system table
Следующие две процедуры, которые таят в себе опасность, — sp_adduser и sp_grantdbaccess. Для начала рассмотрим первую из них. Процедуре sp_adduser нужно передать три параметра (но только первый параметр является обязательным):
♦ имя пользователя (login);
-
имя учетной записи в СУБД. Если этот параметр не указан, то будет использовано имя из первого параметра;
-
имя группы или роли, в которую автоматически попадает пользователь.
При добавлении пользователя указанное имя уже должно существовать в MS SQL Server или в ОС Windows.
Рассмотрим пример. В ОС Windows уже существует гостевая учетная запись. Давайте выдадим ей права на доступ к текущей базе данных:
EXEC sp_adduser 'notebook\rocTb'
Учетная запись "Гость" присутствует в Windows-системах по умолчанию. Если эта учетная запись не заблокирована, то хакер сможет с помощью хранимых процедур наделить ее правами доступа к СУБД и использовать для своих целей. Но хакеру еще желательно знать сетевое имя компьютера. Это легко сделать с помощью процедуры xp_getnetname.
Процедура sp_adduser считается устаревшей и оставлена только для совместимости со старыми приложениями. В данный момент рекомендуется использовать процедуру sp_grantdbaccess, которой нужно передать следующие параметры:
-
имя пользователя (login), которое зарегистрировано в ОС Windows NT или создано в MS SQL Server;
-
имя учетной записи в СУБД. Если этот параметр не указан, то будет использовано имя из первого параметра.
В итоге, добавление гостевой учетной записи с помощью процедуры sp_grantdbaccess будет выглядеть следующим образом:
EXEC sp_grantdbaccess 'notebook\rocTb'
Для удаления пользователя применяется процедура sp_dropuser, которой нужно передать имя пользователя СУБД (мы его указывали во втором параметре процедуры sp_adduser или sp_grantdbaccess). В следующем примере мы удаляем гостевую учетную запись из текущей базы:
EXEC sp_dropuser 'notebook\guest'
Управление — это хорошо, но нужно уметь определить, какие вообще существуют учетные записи в системе. Для этого предназначена хранимая процедура sp_helpuser. Выполните ее, и перед вами появится таблица с информацией о пользователях текущей СУБД. Результирующая таблица состоит из следующих полей:
-
userName — имя пользователя;
-
GroupName — название роли, в которую входит пользователь;
-
LoginName — имя, используемое для входа на сервер;
-
DefDBName — база данных по умолчанию;
-
useriD — идентификатор пользователя;
-
sid — пользовательский идентификатор безопасности.
Не менее опасной для web-сервера и удобной для хакера может оказаться процедура xp_terminate_process, которая позволяет уничтожить указанный процесс по его идентификатору.
Следующие хранимые процедуры позволяют работать с реестром Windows, что тоже достаточно опасно:
-
xp_regenumkeys;
-
xp_regenumvalues;
-
xp_regread;
-
xp_regwrite;
-
xp_regdeletekey;
-
xp_regdeletevalue.
Честно сказать, я понятия не имею, зачем они добавлены в СУБД?
Для работы с диском можно выделить следующие процедуры:
-
xp_availablemedia;
-
xp_fileexist;
-
xp_dirtree.
Первая из этих процедур возвращает доступные устройства, вторая определяет наличие указанного файла в системе, а третья получает дерево каталогов.
Все рассмотренные процедуры должны быть запрещены для выполнения с правами учетной записи, под которой работают ваши сценарии. И это далеко не полный список, есть еще процедуры создания, управления и удаления ролей, от которых зависят права доступа. Чтобы не ошибиться, вы должны запретить все и разрешить доступ явно только к тем, которые используют ваш сценарий.
-
Распределение прав доступа
Но все запреты будут бессмысленны, если простому пользователю разрешено выполнение операторов grant, revoke или deny. С помощью этих операторов можно давать или снимать права, а также запрещать доступ.
Для назначения разрешающих прав доступа используется оператор grant, вид которого зависит от того, на что выделяются права. Если на операторы, то grant выглядит следующим образом:
GRANT { ALL | оператор [ ,...n ] }
TO пользователь [ ,...n ]
Операторы SQL, на которые вы можете назначать права доступа для пользователя:
-
CREATE database;
-
CREATE DEFAULT;
-
create function;
-
CREATE procedure;
-
CREATE rule;
-
CREATE TABLE;
-
CREATE VIEW;
-
backup database;
-
BACKUP LOG.
Рассмотрим пример, в котором пользователю с именем Mikhail выделяются права на создание таблиц и объектов просмотра:
GRANT CREATE TABLE, CREATE VIEW TO Mikhail
Для упрощения работы с правами доступа можно использовать роли. Допустим, что у нас есть десять учетных записей для работников бухгалтерии и все они объединены в одну роль Buh. Если все работники роли нуждаются в возможности создания таблиц, то можно назначить разрешение всей роли:
GRANT CREATE TABLE, CREATE VIEW TO Buh
Если нужно разрешить выполнение всех перечисленных ранее операторов, то можно воспользоваться ключевым словом all. Следующий пример предоставляет полный доступ роли Buh:
GRANT ALL TO Buh
За более полной информацией советую обратиться к файлу-справке, а также могу порекомендовать мою книгу "Transact-SQL".
При добавлении прав доступа на объекты необходимо указать оператор grant, за которым идет перечисление разрешений на объект. После ключевого слова on пишем имя объекта, а после то — имя пользователя или роли. В упрощенном варианте распределение прав выглядит следующим образом:
GRANT разрешения ON объект TO пользователь
Например, следующей командой мы разрешаем пользователю Hacker выполнять оператор select в таблице tbPeopies:
GRANT SELECT ON tbPeopies TO Hacker
Если пользователю нужно предоставить все права на объект, то, чтобы не перечислять их, можно написать ключевое слово ALL:
GRANT ALL ON tbPeopies TO Buh
Необходимо отметить, что по стандарту надо писать all privileges, но Microsoft разрешила ленивым программистам не писать длинное слово privileges. Я, например, всегда забываю, как оно пишется, поэтому благодарен корпорации Microsoft. Итак, если следовать стандарту, то мы должны были бы написать запрос на изменение привилегий следующим образом:
GRANT ALL PRIVILEGES ON tbPeoples TO Buh
Для задания запретов используется оператор deny, который так же имеет два варианта: для операторов и объектов. Рассмотрим каждый из них.
Общий вид команды deny для операторов выглядит следующим образом:
DENY { ALL | оператор [ ,...n ] }
TO пользователь [ ,...n ]
Операторы, которые могут использоваться, те же, что и у grant. Например, следующий запрос явно запрещает пользователю Hacker создавать таблицы и объекты просмотра:
DENY CREATE TABLE, CREATE VIEW TO Hacker
Если нужно отменить все права на операторы, то можно указать ключевое слово all. В следующем примере отменяются права для бухгалтерии:
REVOKE All FROM Buh
-
Опасные SQL-запросы
Даже не имея прав доступа к выполнению команд, злоумышленник может навредить, используя SQL-запросы. Как мы уже выяснили при рассмотрении MySQL (см. разд. 5.2), к СУБД можно отправлять SQL-запросы на обновление и удаление. В случае с MS SQL Server все рассмотренное остается в силе.
Например, дефейс можно совершить и с помощью запросов. Необходимо только найти таблицу, в которой хранятся данные, отображаемые на главной web- странице — например, новости. После этого с помощью запроса update обновляем новости так, чтобы последняя из них (можно и все) содержали необходимый текст. Например, если новости хранятся в таблице news, и заголовок новости — в колонке Title, то хакер может выполнить следующий запрос:
UPDATE News
SET Title='Hacked by MegaHacker'
Это тоже изменение главной web-страницы, и его можно отнести к дефейсу.
Наиболее интересными для хакера являются имена таблиц. Чтобы обновлять и удалять данные, необходимо знать названия объектов, с которыми вы работаете. Для этого используется таблица tables из information_schema или просто: information_schema. tables. Чтобы получить все имена таблиц, необходимо выполнить запрос:
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
Иногда бывает необходимость получить только одну запись из таблицы. Это легко сделать, ограничив результат с помощью оператора top n, который ставится после select, где n — это количество нужных строк. Так, следующий пример выбирает первые две записи:
SELECT TOP 2 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
Как можно получить следующую запись? Да очень просто, выбрать верхнюю запись из InformatIon_schema. tableS и ограничить запрос так, чтобы известное вам имя отсекалось. Например, вы уже знаете, что в базе данных есть таблица Users. Для получения следующего имени таблицы пишем:
SELECT TOP 2 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME<>'Users'
Когда известно несколько таблиц, можно перечислить их с помощью not in, например, следующим образом:
SELECT TOP 2 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME NOT IN ('Users', 'Passwords', 'Data')
Чтобы получить имена всех колонок, необходимо обратиться к таблице columns из I nformat I on_schema. Например, следующий запрос возвратит имена колонок таблицы Users:
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME='Users'
-
Рекомендации по безопасности MS SQL Server
Безопасность MS SQL Server не является темой данной книги, но раз уж мы рассматриваем взлом и безопасность web-серверов, то обсудим некоторые рекомендации, ведь СУБД — своеобразная часть web-сервера.
Для защиты СУБД от хакеров все сценарии должны выполняться от имени непривилегированного пользователя. Этот пользователь должен ограничиваться только выборкой данных, а вставка и обновление должны быть доступны лишь для тех таблиц, где это действительно нужно. Чем мне нравится MS SQL Server, так это
тем, что он предоставляет очень удобное средство управления — SQL Server Enterprise Manager. С его помощью очень удобно управлять и правами.
Tm SQL Server Enterprise Manager
- [Console Root\Microsoft SQL Servers\SQL Server Group\CYD (Windows .
TJni Консоль Действие Вид Tools
Окно Справка
_ в
^ Haul KnfGg, if
* vs вив m ca
Permit
| Console Root •zl) Microsoft SQL Servers В ^ SQL Server Group -a» CYD (Windows NT)
В Cj Databases В U master
Щ Tables 6V1 Views
Stored Procedures Extended Stored Procedures ijfl Users Roles
| Rules
Hal Defaults
P, User Defined Data Types User Defined Functions @ Q model Ш 0 rnsdb s Q Northwmd Ш й pubs s й tempdb
В Data Transformation Services 1+ C3 Management
3
spid ecid
status
loginame host
1 1
1 1
1 1—1 1
1 Д l
dbname
cmd
9
0
background
sa
0
master
TASK MANAGER
10
0
background
sa
0
master
TASK MANAGER
11
0
background
sa
0
master
TASK MANAGER
51
0
runnable
CYD\flenov
0
Northwind
SELECT
52
0
sleeping
CYD\flenov
0
master
AWAITING COMMAND
NAME
Имя
Owner
Владелец
Object Type
Тип объекта
Invoices
dbo
view
Order Subtotals
dbo
view
Orders Qry
dbo
view
Quarterly Orders
dbo
view
Sales by Category
dbo
view
Sales Totals by Amount dbo
view
Sysconstraints
dbo
view
Syssegments
dbo
view
Categories
dbo
user table
CustomerCustomerDemo
dbo
user table
CustomerDemographics
dbo
user table
Customers
dbo
user table
Employees
dbo
user table
Syscolumns
dbo
system table
Syscomments
dbo
system table
Sysdepends
dbo
system table
sysfilegroups
dbo
system table
имя учетной записи в СУБД. Если этот параметр не указан, то будет использовано имя из первого параметра;
имя группы или роли, в которую автоматически попадает пользователь.
имя пользователя (login), которое зарегистрировано в ОС Windows NT или создано в MS SQL Server;
имя учетной записи в СУБД. Если этот параметр не указан, то будет использовано имя из первого параметра.
userName — имя пользователя;
GroupName — название роли, в которую входит пользователь;
LoginName — имя, используемое для входа на сервер;
DefDBName — база данных по умолчанию;
useriD — идентификатор пользователя;
sid — пользовательский идентификатор безопасности.
xp_regenumkeys;
xp_regenumvalues;
xp_regread;
xp_regwrite;
xp_regdeletekey;
xp_regdeletevalue.
xp_availablemedia;
xp_fileexist;
xp_dirtree.
Распределение прав доступа
CREATE database;
CREATE DEFAULT;
create function;
CREATE procedure;
CREATE rule;
CREATE TABLE;
CREATE VIEW;
backup database;
BACKUP LOG.
Опасные SQL-запросы
Рекомендации по безопасности MS SQL Server
Tm SQL Server Enterprise Manager | - [Console Root\Microsoft SQL Servers\SQL Server Group\CYD (Windows . | |
TJni Консоль Действие Вид Tools | Окно Справка | _ в |
^ Haul KnfGg, if | * vs вив m ca | |
Replication В C3 Security ЁЙ Logins
Users 2 Items | | | |
Name | | Login Name | ! Database Access | г |
Permit
I&guest
Database User Properties - guest
General
User name:
Database role membership:
Permit in Database Role
db_owner db_accessadmin db_securityadmin db_ddladmin db_backupoperator db_datareader db_datawriter [ db_denydatareader db_denydatawriter
Итак, сами рекомендации. Запрещаем все, что не используется, для этого войдите в свойства пользователя, от имени которого работают сценарии, и назначьте ему только те роли, которые действительно необходимы (рис. 6.1).
Properties.
Отмена Примените Справка
Рис. 6.1. Свойства пользователя/управление ролями
По умолчанию все пользователи помещаются в роль public. Будьте осторожны, потому что она разрешает слишком много. А если все же используете, то ограничьте права явно: нажмите кнопку Permissions в окне свойств пользователя, и перед вами откроется окно управления доступом к объектам (рис. 6.2).
Это окно имеет вид таблицы. Строки — это объекты базы данных, а колонки — операторы, на которые можно устанавливать права. Если ячейка на пересечении имени объекта и оператора пустая, то права на нее такие же, как и у роли, которую наследует пользователь. Чтобы добавить право текущему пользователю, щелкните по ячейке один раз, и в ней появится зеленая галочка. Чтобы запретить (даже если у