Почему используется необработанный SQL и насколько это безопасно?
Что проверяют
Отличаете ли вы сырой SQL от склеивания строк. Это разные вещи, и вся безопасность держится на втором различии, а не на первом. Ещё смотрят, можете ли вы назвать случаи, где ORM объективно мешает, без общего «так быстрее».
Как отвечать
Сначала про повод. Сырой SQL берут, когда выразительности ORM не хватает или её накладные расходы заметны: оконные функции, рекурсивные CTE, вставка с разрешением конфликта, массовые обновления одним запросом, точечная оптимизация под план выполнения, отчёты с нетривиальными агрегатами.
Дальше главное про безопасность. Сырой запрос безопасен ровно при одном условии: значения передаются параметрами, а не попадают в текст запроса. Драйвер и сервер разделяют шаблон и данные, поэтому вставить фрагмент запроса через значение невозможно.
cur.execute("select id from users where email = %s", (email,))Так делать нельзя ни при каких обстоятельствах: собирать ту же строку через форматирование или конкатенацию. Отдельно скажите про идентификаторы: имена таблиц, колонок и направление сортировки параметром не передаются, для них нужен белый список допустимых значений или специальный тип для идентификаторов из библиотеки драйвера.
Где спотыкаются
Предлагают экранировать кавычки вручную или чистить ввод регулярным выражением. Это не защита: контексты без кавычек, числовые поля и особенности кодировок обходят такую фильтрацию. Правильный ответ на вопрос про защиту всегда начинается с параметризации.
Считают, что ORM безопасен по определению, а сырой SQL опасен по определению. Опасна конкатенация, в каком бы слое она ни жила, и текстовый фрагмент внутри ORM ничем не лучше.
Упираются в сортировку и фильтры, приходящие от пользователя. Именно там чаще всего появляется форматирование строки, потому что параметр не подходит, и именно там появляется дыра. Рядом полезно упомянуть ограничение ущерба: приложение должно ходить в базу отдельной ролью с минимальными правами, тогда одна ошибка не означает доступ ко всему кластеру.