Введение
Поиск небольшого магазина товаров для отдыха должен возвращать только опубликованные товары с точным совпадением имени. Ошибка построения запроса позволяет вводу изменить это правило, а обычный апостроф ломает поиск. Вы проследите реальные запросы, исправите небольшую функцию Python и проверите безопасность и полезные результаты.
Workstation, App Server и Shop Web уже готовы. Достаточно уметь вводить команды и переключать вкладки. SQL, нужная для изменения синтаксическая конструкция Python и работа с редактором объясняются здесь. Данные синтетические, но HTTP-сервис и разбор SQLite настоящие.
Выполните обычный поиск товара
На этом шаге вы воспользуетесь магазином и сопоставите результат со строкой базы. Вы помогаете небольшому магазину товаров для отдыха исправить поиск. Он должен возвращать только опубликованные товары, имя которых точно совпадает с вводом. Все товары и цены — синтетические учебные данные.
Чтобы начать этот шаг, выберите вкладку Shop Web в интерфейсе лабораторной работы.
Таблица хранит записи в строках, описанных именованными столбцами. Ниже подготовленная таблица products: listed равно 1 для публичного товара и 0 для внутреннего прототипа. Цены — целые единицы учебной валюты.
| id | name | price | listed |
|---|---|---|---|
| 1 | Trail Mug | 18 | 1 |
| 2 | Camper's Cup | 22 | 1 |
| 3 | Prototype Mug | 99 | 0 |
| 4 | ORANGE Bowl | 12 | 1 |
Перейдите на вкладку Shop Web. В Product name введите Trail Mug и выберите Search. Результат rows должен содержать [1, "Trail Mug", 18], а error — null (нет ошибки). Страница отправляет запрос приложению Python; приложение запрашивает совпадающие записи у SQLite, затем возвращает результат. Страница, код приложения и база данных — разные части этого пути запроса.

Запрос — вопрос к базе данных. Локальная учебная трассировка показывает SQL, выполненный SQLite:
SELECT id, name, price FROM products WHERE listed = 1 AND name = 'Trail Mug' ORDER BY id
SELECT задаёт возвращаемые столбцы, FROM — таблицу. WHERE ограничивает строки. AND требует выполнения обоих условий: публикации и точного имени. Одинарные кавычки ограничивают текстовое значение SQL. ORDER BY id обеспечивает стабильный порядок. Страница не возвращает столбец listed, но запрос продолжает использовать его для фильтрации.
Терминал Workstation служит клиентом запросов. App Server открывает каталог приложения для последующего исправления кода. Это отдельные сеансы на одном учебном хосте, а не разные машины. Страница показывает эту связь и обслуживается настоящим запущенным приложением.
Перейдите на вкладку Workstation, чтобы выполнить тот же запрос:
Перед выполнением следующей команды перейдите на вкладку Workstation.
python3 probe.py "Trail Mug"
python3 запускает подготовленный клиент probe.py. Его единственный аргумент — искомый текст. Двойные кавычки сохраняют имя с пробелами одним аргументом; оболочка удаляет внешние кавычки. Клиент безопасно кодирует значение в HTTP-запрос — формат обращения к веб-приложению. 127.0.0.1 здесь означает этот учебный хост.
Прочитайте rows, trace и error в JSON. Это структурированный ответ: массивы используют квадратные скобки, null означает отсутствие ошибки. Если страница не соединяется, перезагрузите Shop Web. Ошибка соединения отличается от успешного поиска без результатов. Проверка читает настоящий результат запроса, поэтому отчёт не требуется.
Посмотрите, как ввод меняет запрос
На этом шаге вы наблюдаете один ограниченный тест SQL-инъекции в учебном магазине. SQL-инъекция означает, что ввод меняет структуру запроса вместо того, чтобы оставаться текстовым значением. Используйте эти тесты только в подготовленной лаборатории.
Чтобы начать этот шаг, выберите вкладку Workstation в интерфейсе лабораторной работы.
Перейдите на вкладку App Server. Прочитайте небольшую функцию через cat, выводящую файл без редактирования:
Перед выполнением следующей команды перейдите на вкладку App Server.
cat query.py
def search(db, text): определяет функцию. db — открытое соединение, text — полученное значение. Строки с отступом составляют тело. sql = присваивает переменной строку Python. Двойные кавычки ограничивают эту строку, а одинарные внутри — текст SQL. + text + конкатенирует, то есть напрямую соединяет, пользовательское значение с SQL. db.execute(sql) просит SQLite выполнить его; fetchall() собирает строки; return возвращает их.
Перейдите на вкладку Workstation и отправьте следующее точное значение, включая пробел после двух дефисов:
Перед выполнением следующей команды перейдите на вкладку Workstation.
python3 probe.py "' OR 1=1 -- "
Внешние двойные кавычки относятся к оболочке. Начальная одинарная кавычка попадает в SQLite и закрывает пустой текст в склеенном запросе. OR допускает любое из условий; 1=1 всегда истинно. -- начинает комментарий, поэтому исходные закрывающая кавычка и сортировка игнорируются. Трассировка содержит:
SELECT id, name, price FROM products WHERE listed = 1 AND name = '' OR 1=1 -- ' ORDER BY id
Вы увидите все четыре строки, включая [3, "Prototype Mug", 99]. Этот прототип не опубликован. Запрос нарушил бизнес-границу, хотя только читал данные. Успешный HTTP-ответ не доказывает безопасность поиска.
Попробуйте допустимое имя с апострофом:
python3 probe.py "Camper's Cup"
Уязвимое приложение возвращает rows: [], пустую trace и error с near "s": syntax error. SQLite не смог выполнить некорректный SQL; завершённого запроса для трассировки нет. Этот сбой ожидаем, клиент завершается нормально, и можно продолжать.
Апостроф допустим в данных товара. Его удаление изменит искомое имя, а запрет всех таких вводов отвергнет клиента. Блокировка слов вроде OR нарушит и поиск ORANGE Bowl. Исправление должно сохранять значение, отделяя его от структуры SQL.
Свяжите искомое значение
На этом шаге вы исправите функцию, сохранив столбцы, точное совпадение, ограничение видимости и порядок. Перейдите на вкладку App Server.
Чтобы начать этот шаг, выберите вкладку App Server в интерфейсе лабораторной работы.
Параметризованный запрос разделяет структуру SQL и значения. В SQLite API Python знак ? отмечает место одного значения. Передавайте его отдельно в execute; не заключайте заполнитель в кавычки и не склеивайте значение с SQL. Например, db.execute("SELECT ?", (text,)) возвращает текст как данные, не интерпретируя его как SQL.
(text,) — кортеж из одного элемента, упорядоченная коллекция параметров. Запятая в конце делает его кортежем; (text) — лишь исходное значение в скобках. Внешние скобки execute(sql, (text,)) вызывают функцию с двумя аргументами: шаблоном SQL и кортежем значений. Драйвер связывает значение с заполнителем.
Откройте подготовленный файл в редакторе терминала:
Перед выполнением следующей команды перейдите на вкладку App Server.
nano query.py
Перемещайтесь стрелками, удаляйте Backspace/Delete и вводите текст для редактирования. Замените две строки с отступами, начинающиеся с sql = и return, чтобы функция была точно такой:
def search(db, text):
sql = "SELECT id, name, price FROM products WHERE listed = 1 AND name = ? ORDER BY id"
return db.execute(sql, (text,)).fetchall()
Сохраните четыре пробела перед каждой строкой тела и отсутствие отступа перед def. Python группирует тело отступами. Сохраните Ctrl+O, подтвердите query.py клавишей Enter, выйдите Ctrl+X. Исходный комментарий можно оставить.
Приложение читает и компилирует query.py при каждом запросе. Достаточно сохранения, перезапуск не нужен. Несохранённый буфер не влияет на работу. Опечатка или неверный отступ появятся в поле error следующего ответа. Снова откройте файл, исправьте, сохраните и повторите запрос.
Перейдите на вкладку Workstation и повторите оба значения:
Перед выполнением следующей команды перейдите на вкладку Workstation.
python3 probe.py "Camper's Cup"
python3 probe.py "' OR 1=1 -- "
Настоящий товар теперь возвращает [2, "Camper's Cup", 22]; специальный тест — rows: []. В обоих случаях error: null. Товара с таким точным специальным именем в этой базе нет.
В трассировке может быть Camper''s Cup или весь специальный текст в кавычках. SQLite показывает развёрнутый SQL после связывания, включая экранированные значения, а не исходный шаблон. Наличие значений не означает склеивание в Python: посмотрите шаблон и отдельный кортеж в query.py. Не заменяйте параметры самодельным экранированием.
Сохраните бизнес-функцию поиска
На этом шаге вы вместе проверите безопасность и обычную работу. Отключённый поиск или фиксированный пустой ответ могли бы скрыть прототип, но помешали бы покупателям находить товары.
Чтобы начать этот шаг, выберите вкладку Workstation в интерфейсе лабораторной работы.
Перейдите на вкладку Workstation. Снова отправьте обычное имя:
Перед выполнением следующей команды перейдите на вкладку Workstation.
python3 probe.py "Trail Mug"
По-прежнему ожидается [1, "Trail Mug", 18]. Проверьте, что допустимое слово с OR не отвергается:
python3 probe.py "ORANGE Bowl"
Ожидается [4, "ORANGE Bowl", 12]. Найдите неопубликованный прототип по обычному имени:
python3 probe.py "Prototype Mug"
Ожидаются rows: [] и error: null. Связывание предотвращает инъекцию, а неизменённое listed = 1 продолжает определять публичные товары. Удалять это ограничение при исправлении небезопасно.
Вернитесь на вкладку Shop Web, введите Camper's Cup и выберите Search. Убедитесь, что появилась его строка. Повторите с ' OR 1=1 -- и подтвердите пустой результат без ошибки. Браузер и клиент используют одну и ту же функцию запроса.

Запустите проверку шага. Она делает запросы только для чтения и запускает вашу неизменённую функцию с дополнительными синтетическими строками во временной базе в памяти. Так проверяются точность, пунктуация, публикация и новые имена без изменения рабочей базы. Даже похожее на SQL имя должно оставаться доступным для поиска как обычные данные, если оно точно существует и опубликовано.
Если проверка не проходит, вернитесь на вкладку App Server, снова откройте query.py и сравните заполнитель, запятую кортежа, отступы и бизнес-ограничение с исправлением. Сохраните и проверьте снова. Не меняйте базу данных, клиент и серверный каркас: изменение записей или фиксированные ответы не исправляют запрос.
Резюме
Вы проследили поиск от страницы до реального SQLite-запроса, увидели изменение структуры вводом и исправили вызов Python с заполнителем и кортежем из одного значения. Вы проверили обычные имена, апострофы, ограниченный тест и сохранённое правило публикации. Испытание поиска заказов применяет это разделение к другому бизнес-объекту.


