Защита от тёмных искусств
1. SQLite всегда проверяет входные данные
SQLite никогда не должна аварийно завершать работу, переполнять буфер, допускать утечки памяти или вести себя каким-либо иным образом, способным причинить вред, даже если ей переданы намеренно искажённые SQL-запросы или файлы баз данных. SQLite всегда должна обнаруживать ошибочные входные данные и выдавать ошибку, а не аварийно завершать работу или повреждать память. Любая неисправность, вызванная SQL-запросом или файлом базы данных, считается серьёзной ошибкой и будет оперативно устранена, как только разработчики SQLite узнают о ней. SQLite подвергается обширному фаззинг-тестированию, чтобы обеспечить устойчивость к ошибкам такого рода.
Тем не менее ошибки случаются. Если вы пишете приложение, которое передаёт SQLite недоверенные SQL-запросы или файлы баз данных, можно предпринять дополнительные шаги, чтобы уменьшить поверхность атаки и предотвратить эксплойты нулевого дня, вызванные необнаруженными ошибками.
1.1. Недоверенные SQL-запросы
Приложения, принимающие недоверенные SQL-запросы, должны принять следующие меры предосторожности:
Установите флаг SQLITE_DBCONFIG_DEFENSIVE. Это не позволяет обычным SQL-инструкциям намеренно повреждать файл базы данных. SQLite должна быть защищена от атак, одновременно использующих вредоносные SQL-запросы и намеренно повреждённый файл базы данных. Тем не менее лишение злоумышленника, ограниченного выполнением скриптов, доступа к повреждённым входным данным базы данных обеспечивает дополнительный уровень защиты.
-
Уменьшите ограничения, которые SQLite накладывает на входные данные. Это может помочь предотвратить атаки типа «отказ в обслуживании» и другие вредоносные действия, возможные из-за необычно больших входных данных. Это можно сделать либо во время компиляции с помощью параметров -DSQLITE_MAX_..., либо во время выполнения с помощью интерфейса sqlite3_limit(). В большинстве приложений ограничения можно значительно уменьшить без ущерба для функциональности. В таблице ниже приведены некоторые рекомендации, хотя точные значения зависят от приложения:
Настройка ограничения Значение по умолчанию Значение для повышенной безопасности LIMIT_LENGTH 1,000,000,000 1,000,000 LIMIT_SQL_LENGTH 1,000,000,000 100,000 LIMIT_COLUMN 2,000 100 LIMIT_EXPR_DEPTH 1,000 10 LIMIT_PARSER_DEPTH 2,500 100 LIMIT_COMPOUND_SELECT 500 3 LIMIT_VDBE_OP 250,000,000 25,000 LIMIT_FUNCTION_ARG 127 8 LIMIT_ATTACH 10 0 LIMIT_LIKE_PATTERN_LENGTH 50,000 50 LIMIT_VARIABLE_NUMBER 999 10 LIMIT_TRIGGER_DEPTH 1,000 10 Рассмотрите возможность использования интерфейса sqlite3_set_authorizer(), чтобы ограничить набор обрабатываемых SQL-инструкций. Например, приложение, которому не нужно изменять схему базы данных, может добавить функцию обратного вызова sqlite3_set_authorizer(), которая будет приводить к ошибке при выполнении любой инструкции CREATE или DROP.
Язык SQL очень мощный, поэтому вредоносные или ошибочные SQL-запросы, возникшие из-за ошибки приложения, могут выполняться очень долго. Чтобы это не привело к атаке типа «отказ в обслуживании», рассмотрите возможность использования интерфейса sqlite3_progress_handler(), который будет периодически вызывать функцию обратного вызова во время выполнения каждой SQL-инструкции. Эта функция должна возвращать ненулевое значение, чтобы прервать инструкцию, если её выполнение затянулось. Также можно установить таймер в отдельном потоке и вызвать sqlite3_interrupt() по его срабатывании, чтобы SQL-инструкция не выполнялась бесконечно.
Ограничьте максимальный объём памяти, который SQLite может выделить, с помощью интерфейса sqlite3_hard_heap_limit64(). Это помогает предотвратить атаки типа «отказ в обслуживании». Чтобы узнать, сколько памяти в куче действительно требуется приложению, запустите его на типичных входных данных, а затем измерьте максимальное мгновенное потребление памяти с помощью интерфейса sqlite3_memory_highwater(). Установите жёсткий предел размера кучи равным максимальному измеренному мгновенному потреблению памяти с некоторым запасом.
Рассмотрите возможность задания параметра компиляции SQLITE_MAX_ALLOCATION_SIZE со значением меньше значения по умолчанию 2147483391 (0x7ffffeff). В зависимости от приложения вполне разумным будет значение 100000000 (100 миллионов) или даже меньше.
-
Для встроенных систем рассмотрите возможность компиляции SQLite с параметром -DSQLITE_ENABLE_MEMSYS5, а затем предоставьте SQLite фиксированный блок памяти для использования в качестве кучи через интерфейс sqlite3_config(SQLITE_CONFIG_HEAP). Это не позволит вредоносному SQL-запросу выполнить атаку типа «отказ в обслуживании», используя чрезмерный объём памяти. Если, например, предоставить SQLite 5 МБ памяти, то после исчерпания этого объёма SQLite начнёт возвращать ошибки SQLITE_NOMEM, а не отбирать память, необходимую другим частям приложения. Это также изолирует память SQLite, поэтому ошибка записи после освобождения памяти в другой части приложения не вызовет проблем у SQLite, и наоборот.
-
Чтобы контролировать использование памяти функцией printf() в SQL, скомпилируйте SQLite с параметром "-DSQLITE_PRINTF_PRECISION_LIMIT=100000" или другим столь же разумным значением. Эта директива #define ограничивает ширину и точность подстановок %-формата в функции printf(), предотвращая тем самым чрезмерное потребление оперативной памяти из-за враждебной SQL-инструкции с помощью таких конструкций, как "
printf('%1000000000s','hi')".Обратите внимание, что SQLite использует встроенную функцию printf() для форматирования столбца sql в таблице sqlite_schema. Поэтому определения таблиц, индексов, представлений и триггеров не могут быть значительно длиннее предела точности. Можно установить предел точности меньше 100000, но убедитесь, что выбранное значение не меньше длины самой длинной инструкции CREATE в вашей схеме.
1.2. Недоверенные файлы баз данных SQLite
Приложения, читающие или записывающие файлы баз данных SQLite неизвестного происхождения, должны принять перечисленные ниже меры предосторожности.
Даже если приложение намеренно не принимает файлы баз данных из недоверенных источников, остерегайтесь атак, при которых локальный файл базы данных изменяется. Для наилучшей защиты любой файл базы данных, который когда-либо мог быть доступен для записи субъекту из другой области безопасности, следует считать подозрительным.
-
Если приложение включает какие-либо пользовательские SQL-функции или пользовательские виртуальные таблицы, имеющие побочные эффекты или способные раскрыть привилегированную информацию, приложению следует использовать один или несколько приведённых ниже методов, чтобы не допустить скрытого запуска этих SQL-функций и/или виртуальных таблиц из-за намеренно созданной вредоносной схемы базы данных:
- Вызовите sqlite3_db_config(db,SQLITE_DBCONFIG_TRUSTED_SCHEMA,0,0) для каждого подключения к базе данных сразу после его открытия.
- Выполните инструкцию PRAGMA trusted_schema=OFF для каждого подключения к базе данных сразу после его открытия.
- Скомпилируйте SQLite с параметром компиляции -DSQLITE_TRUSTED_SCHEMA=0.
- Запретите скрытое использование пользовательских SQL-функций и виртуальных таблиц, установив флаг SQLITE_DIRECTONLY для всех пользовательских SQL-функций и флаг SQLITE_VTAB_DIRECTONLY для всех пользовательских виртуальных таблиц.
- Убедитесь, что пользовательские виртуальные таблицы, выполняющие SQL-инструкции на основе аргументов инструкции CREATE VIRTUAL TABLE из схемы базы данных, используют sqlite3_prepare_v3() с параметром SQLITE_PREPARE_FROM_DDL, чтобы предотвратить обход настройки SQLITE_DBCONFIG_TRUSTED_SCHEMA, описанной выше в пункте (9a).
-
Если приложение не использует триггеры или представления, рассмотрите возможность отключения неиспользуемых функций с помощью:
sqlite3_db_config(db,SQLITE_DBCONFIG_ENABLE_TRIGGER,0,0); sqlite3_db_config(db,SQLITE_DBCONFIG_ENABLE_VIEW,0,0);
Если приложение предъявляет особенно высокие требования к безопасности, могут быть оправданы следующие дополнительные уровни защиты. Однако такая защита снижает производительность и поэтому может быть неуместна в некоторых ситуациях:
Выполните PRAGMA integrity_check или PRAGMA quick_check для базы данных в качестве первой SQL-инструкции после открытия файлов базы данных и до выполнения любых других SQL-инструкций. Отклоните и откажитесь обрабатывать любой файл базы данных, содержащий ошибки.
-
Включите настройку PRAGMA cell_size_check=ON.
Не включайте ввод-вывод с отображением файлов в память. Иными словами, убедитесь, что задано PRAGMA mmap_size=0.
2. Итоги
Приведённые выше меры предосторожности не обязательны для безопасного использования SQLite с потенциально вредоносными входными данными. Однако они обеспечивают дополнительный уровень защиты от эксплойтов нулевого дня и рекомендуются для приложений, передающих в SQLite данные из недоверенных источников.
Последнее обновление этой страницы: 2026-02-26 14:36:36Z
SQLite is in the Public Domain.
https://sqlite.org/security.html