Corrigir a consulta de busca de produtos

SQLiteBeginner
Pratique Agora

Introdução

A busca de uma pequena loja de artigos de aventura deve retornar apenas produtos publicados com nome exatamente igual. Um erro na construção da consulta permite alterar essa regra; até um apóstrofo comum quebra a busca. Você observará solicitações reais, corrigirá uma pequena função Python e testará segurança e resultados úteis.

Workstation, App Server e Shop Web estão prontos. Basta saber usar o terminal e alternar abas. SQL, a sintaxe Python necessária e os controles do editor são apresentados aqui. Os dados são sintéticos; o serviço HTTP e a análise do SQLite são reais.

Faça uma busca normal de produto

Nesta etapa, você usará a loja e relacionará um resultado à sua linha no banco. Você ajuda uma pequena loja de artigos de aventura a corrigir a busca. Ela deve retornar apenas produtos publicados cujo nome corresponda exatamente à entrada. Todos os produtos e preços são dados sintéticos de treinamento.

Selecione a aba Shop Web na interface do laboratório para começar esta etapa.

Uma tabela armazena registros em linhas, descritas por colunas nomeadas. Esta é a tabela products preparada; listed é 1 para um produto público e 0 para um protótipo interno. Os preços são unidades inteiras de uma moeda de treinamento.

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

Mude para a aba Shop Web, digite Trail Mug em Product name e selecione Search. rows deve conter [1, "Trail Mug", 18], com error igual a null (sem erro). A página envia uma solicitação à aplicação Python, que pede ao SQLite os registros correspondentes e retorna o resultado. Página, código e banco são partes diferentes desse caminho.

Shop Web mostrando um resultado normal e seu rastreamento SQL didático

Uma consulta é uma pergunta ao banco. O rastreamento didático local mostra o SQL executado pelo SQLite:

SELECT id, name, price FROM products WHERE listed = 1 AND name = 'Trail Mug' ORDER BY id

SELECT nomeia as colunas retornadas; FROM, a tabela. WHERE restringe linhas. AND exige as duas condições: publicação e nome exato. Aspas simples delimitam um valor textual SQL. ORDER BY id fornece ordem estável. A página não retorna a coluna listed, mas a consulta ainda a usa para filtrar.

O terminal Workstation é o cliente que envia solicitações. App Server abre o diretório da aplicação para a correção posterior. São sessões independentes no mesmo host de treinamento, não máquinas diferentes. A página mostra essa relação e é servida pela aplicação realmente em execução.

Mude para a aba Workstation para fazer a mesma solicitação:

Mude para a aba Workstation antes de executar o comando a seguir.

python3 probe.py "Trail Mug"

python3 executa o cliente preparado probe.py. Seu único argumento é o texto buscado. Aspas duplas mantêm um nome com espaços em um argumento; o shell remove essas aspas externas. O cliente codifica o valor com segurança em uma solicitação HTTP, formato usado pela aplicação web. 127.0.0.1 no cliente significa este host de treinamento.

Leia rows, trace e error na saída JSON, a resposta estruturada da aplicação. Arrays usam colchetes e null significa ausência de erro. Se a página não conectar, recarregue Shop Web; erro de conexão é diferente de busca bem-sucedida sem resultados. A verificação lê o resultado real da solicitação, portanto não é necessário relatório.

Veja a entrada alterar a consulta

Nesta etapa, você observará um teste limitado de injeção SQL nesta loja de treinamento. Injeção SQL ocorre quando a entrada altera a estrutura da consulta em vez de permanecer um valor textual. Use estes testes somente no laboratório preparado.

Selecione a aba Workstation na interface do laboratório para começar esta etapa.

Mude para a aba App Server. Leia a pequena função com cat, que imprime um arquivo sem editá-lo:

Mude para a aba App Server antes de executar o comando a seguir.

cat query.py

def search(db, text): define a função. db é a conexão aberta; text, o valor recebido. As linhas indentadas formam o corpo. sql = atribui uma string Python a uma variável. Aspas duplas delimitam essa string; as simples internas delimitam texto SQL. + text + concatena, ou une diretamente, o valor do usuário ao SQL. db.execute(sql) pede sua execução; fetchall() reúne linhas; return as devolve.

Mude para a aba Workstation e envie exatamente este valor, incluindo o espaço após os dois hífens:

Mude para a aba Workstation antes de executar o comando a seguir.

python3 probe.py "' OR 1=1 -- "

As aspas duplas externas pertencem ao shell. A aspa simples inicial chega ao SQLite e fecha o texto vazio da consulta concatenada. OR permite qualquer das condições; 1=1 é sempre verdadeiro. -- inicia um comentário, fazendo a aspa final e a ordenação originais serem ignoradas. O rastreamento contém:

SELECT id, name, price FROM products WHERE listed = 1 AND name = '' OR 1=1 -- ' ORDER BY id

Você verá as quatro linhas, incluindo [3, "Prototype Mug", 99]. Esse protótipo não está publicado. A solicitação ultrapassou o limite do negócio mesmo lendo apenas dados. Uma resposta HTTP bem-sucedida não demonstra uma busca segura.

Experimente um nome legítimo com apóstrofo:

python3 probe.py "Camper's Cup"

A aplicação vulnerável retorna rows: [], trace vazio e error contendo near "s": syntax error. O SQLite não conseguiu executar o SQL malformado, portanto não há consulta concluída a rastrear. Essa falha é esperada; o cliente termina normalmente para você continuar.

Apóstrofos são dados válidos de produtos. Removê-los alteraria o nome solicitado; bloquear todas as entradas com eles rejeitaria clientes. Bloquear palavras como OR também prejudicaria ORANGE Bowl. A correção deve preservar o valor e separá-lo da estrutura SQL.

Vincule o valor da busca

Nesta etapa, você corrigirá a função preservando colunas, correspondência exata, restrição de publicação e ordenação. Mude para a aba App Server.

Selecione a aba App Server na interface do laboratório para começar esta etapa.

Uma consulta parametrizada separa estrutura SQL e valores. Na API SQLite do Python, ? marca a posição de um valor. Passe o valor separadamente a execute; não ponha aspas no marcador nem concatene o valor ao SQL. Por exemplo, db.execute("SELECT ?", (text,)) retorna o texto como dado, sem interpretá-lo como SQL.

(text,) é uma tupla de um item, uma coleção ordenada de parâmetros. A vírgula final cria a tupla; (text) é apenas o valor entre parênteses. Os parênteses externos de execute(sql, (text,)) chamam a função com dois argumentos: o modelo SQL e a tupla de valores. O driver vincula o valor ao marcador.

Abra o arquivo no editor do terminal:

Mude para a aba App Server antes de executar o comando a seguir.

nano query.py

Mova-se com as setas, apague com Backspace/Delete e digite para editar. Substitua as duas linhas indentadas iniciadas por sql = e return para obter exatamente:

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()

Mantenha quatro espaços antes de cada linha do corpo e nenhum antes de def. Python agrupa o corpo pela indentação. Salve com Ctrl+O, confirme query.py com Enter e saia com Ctrl+X. O comentário original pode permanecer.

A aplicação lê e compila query.py a cada solicitação. Salvar basta; não é preciso reiniciar. Um buffer não salvo não tem efeito. Erros de digitação ou indentação aparecem em error na próxima resposta. Reabra o mesmo arquivo, corrija, salve e repita a solicitação.

Mude para a aba Workstation e repita os dois valores:

Mude para a aba Workstation antes de executar o comando a seguir.

python3 probe.py "Camper's Cup"
python3 probe.py "' OR 1=1 -- "

O produto real agora retorna [2, "Camper's Cup", 22]; o teste especial retorna rows: []. Ambos têm error: null. Não existe um produto com esse nome especial exato neste banco.

O rastreamento pode mostrar Camper''s Cup ou toda a string especial entre aspas. O SQLite exibe SQL expandido após a vinculação, incluindo valores escapados, não o modelo original. Ver valores não significa concatenação no Python: examine query.py para ver o modelo e a tupla separada. Não substitua parâmetros vinculados por escape manual.

Preserve a busca do negócio

Nesta etapa, você testará segurança e funcionamento normal juntos. Desativar a busca ou fixar uma resposta vazia poderia esconder o protótipo, mas impediria clientes de encontrar produtos.

Selecione a aba Workstation na interface do laboratório para começar esta etapa.

Mude para a aba Workstation. Envie novamente o nome normal:

Mude para a aba Workstation antes de executar o comando a seguir.

python3 probe.py "Trail Mug"

Você deve continuar recebendo [1, "Trail Mug", 18]. Confirme que uma palavra válida contendo OR não é rejeitada:

python3 probe.py "ORANGE Bowl"

Espere [4, "ORANGE Bowl", 12]. Busque o protótipo não publicado pelo nome normal:

python3 probe.py "Prototype Mug"

Espere rows: [] e error: null. A vinculação impede injeção; a restrição intacta listed = 1 continua controlando produtos públicos. Removê-la durante a correção seria inseguro.

Volte à aba Shop Web e digite Camper's Cup, selecione Search e confirme a linha. Repita com ' OR 1=1 -- e confirme resultado vazio sem erro. Navegador e cliente usam a mesma função.

Shop Web mostrando que a consulta corrigida trata a entrada como dados

Execute a verificação da etapa. Ela faz solicitações somente de leitura e executa sua função inalterada com linhas sintéticas extras em um banco descartável na memória. Isso testa correspondência exata, pontuação, regras de publicação e nomes desconhecidos sem editar o banco ativo. Mesmo um nome parecido com SQL deve continuar pesquisável como dado se existir exatamente e estiver publicado.

Se falhar, volte à aba App Server, abra query.py e compare marcador, vírgula da tupla, indentação e restrição do negócio com a correção. Salve e teste novamente. Preserve banco, cliente e estrutura do servidor: alterar registros ou retornar respostas fixas não corrige a consulta.

Resumo

Você acompanhou uma busca da página até uma consulta SQLite real, observou a entrada mudar sua estrutura e corrigiu a chamada Python com marcador e tupla de um valor. Testou nomes normais, apóstrofos, a entrada limitada e a regra de publicação preservada. O desafio de pedidos aplica a mesma separação a outro objeto do negócio.