OpenCyber Academy
Tecnólogo · Capítulo 3 · Português
Código do curso: OCA-089Capítulo 03: OCA-089-C003

Capítulo 3 — Fundamentos de programação

Ensino gratuito e aberto para quem deseja proteger pessoas e tecnologias. Não é preciso criar uma conta para estudar ou consultar as respostas.

Mapa da aula

Aula 03 publicada. Mapa, exemplo, EX007.

flowchart TD
  N0["3.1 Programas em torno dos modelos"]
  N1["3.2 A saída do modelo não é confiável"]
  N2["3.3 Valide a saída estruturada"]
  N3["3.5 Lendo o laboratório"]
  N4["Síntese"]
  N0 --> N1 --> N2 --> N3 --> N4
  N4 --> EX["Exemplo resolvido"]
  EX --> Q["Exercício EX007"]

Desenho lógico

flowchart LR
  N0["3.1 Programas em torno dos modelos"]
  N1["3.2 A saída do modelo não é confiável"]
  N2["3.3 Valide a saída estruturada"]
  N3["3.5 Lendo o laboratório"]
  N4["Síntese"]
  N0 --> N1 --> N2 --> N3 --> N4 --> Q["EX007"]
[3.1 Programas em torno dos modelos]
    |
    v
[3.2 A saída do modelo não é confiável]
    |
    v
[3.3 Valide a saída estruturada]
    |
    v
[3.5 Lendo o laboratório]
    |
    v
[Síntese]
    |
    v
[Exemplo resolvido] --> [Exercício EX007]

Objetivos de aprendizagem

Você vai (1) escrever Python simples para chamar e testar serviços de IA com segurança; (2) validar entradas não confiáveis e saídas de modelos; (3) tratar erros e segredos corretamente; e (4) explicar por que a saída de um modelo deve ser tratada como dado não confiável.

3.1 Programas em torno dos modelos

A maior parte do trabalho de segurança de IA é programação comum: montar prompts, interpretar respostas, chamar ferramentas, escrever testes. A biblioteca padrão do Python basta para muitas tarefas: json para interpretar saídas estruturadas, re para padrões, hashlib para verificações de integridade, unittest para testes. O trabalho de Jonas no Ask Brightwell é quase todo desse tipo: escapar a saída no console da equipe, validar o JSON da ferramenta de devoluções e escrever testes que falham quando uma consulta entre usuários diferentes dá certo. Nada disso exige mudar o modelo. Cada item é Python simples, que um revisor consegue ler em minutos e conferir contra a ameaça que ele trata.

3.2 A saída do modelo não é confiável

A resposta de um modelo pode conter texto que um atacante plantou por meio de um documento. Se o código insere esse texto em HTML, SQL ou num comando de shell sem escapá-lo, o atacante consegue injeção nesses sistemas — OWASP LLM05 Tratamento Inadequado da Saída (Improper Output Handling). Trate a saída exatamente como uma entrada do usuário: escape-a para HTML, use consultas parametrizadas, nunca a passe a um shell. A avaliação plantada por Jonas no console da equipe mostrou isso diretamente: o assistente citou uma etiqueta de imagem de uma avaliação, e o console a renderizou, executando o script no navegador de um funcionário. A correção foi o escape, não um modelo melhor.

3.3 Valide a saída estruturada

Quando um modelo precisa devolver uma ação, peça JSON e valide-o com rigor: interprete-o, verifique se existem apenas as chaves esperadas, verifique os tipos, verifique os valores permitidos (por exemplo, action em {"lookup", "return"}) e rejeite todo o resto. Nunca "conserte" uma saída inválida por adivinhação. No Ask Brightwell, o orquestrador aceita apenas lookup e return como ações e verifica se o número do pedido é uma sequência de dígitos. Qualquer outra coisa, inclusive chaves extras que o modelo invente, faz a requisição ser rejeitada e registrada, de modo que uma proposta malformada nunca chega à ferramenta de devoluções. Em seguida, o serviço de políticas faz sua própria verificação sobre a ação validada.

3.4 Erros e segredos

Falhe de forma fechada: se a validação falhar ou uma ferramenta der erro, não execute a ação. Guarde as chaves de API em variáveis de ambiente ou num gerenciador de segredos, nunca no código nem nos registros.

"""Lab E: evasion of a toy linear classifier (synthetic data, stdlib only).
Trains logistic regression on two synthetic clusters, then applies a
fast-gradient-sign (FGSM-style) perturbation of size eps to every test point
and reports clean vs adversarial accuracy. Teaching model only."""
import math, random
rng = random.Random(7)
def point(label):
    cx = 1.0 if label else -1.0
    return [rng.gauss(cx, 0.7), rng.gauss(cx, 0.7)], label
train = [point(i % 2) for i in range(400)]
test = [point(i % 2) for i in range(200)]
w, b = [0.0, 0.0], 0.0
sig = lambda z: 1 / (1 + math.exp(-z))
for epoch in range(200):
    gw, gb = [0.0, 0.0], 0.0
    for x, y in train:
        p = sig(w[0]*x[0] + w[1]*x[1] + b)
        gw[0] += (p - y)*x[0]; gw[1] += (p - y)*x[1]; gb += p - y
    w = [w[i] - 0.1*gw[i]/len(train) for i in range(2)]; b -= 0.1*gb/len(train)
def acc(data, eps):
    ok = 0
    for x, y in data:
        # gradient of the loss w.r.t. x is (p - y) * w; FGSM steps along its sign
        p = sig(w[0]*x[0] + w[1]*x[1] + b)
        xa = [x[i] + eps*math.copysign(1, (p - y)*w[i]) for i in range(2)]
        ok += (sig(w[0]*xa[0] + w[1]*xa[1] + b) >= 0.5) == bool(y)
    return ok / len(data)
print(f"weights w=({w[0]:.3f}, {w[1]:.3f}) b={b:.3f}")
for eps in (0.0, 0.25, 0.5, 0.75, 1.0):
    print(f"eps={eps:.2f} accuracy={acc(test, eps):.3f}")

Saída esperada:

weights w=(1.795, 1.756) b=-0.080
eps=0.00 accuracy=0.985
eps=0.25 accuracy=0.950
eps=0.50 accuracy=0.840
eps=0.75 accuracy=0.660
eps=1.00 accuracy=0.505

No Ask Brightwell, se a ferramenta de pedidos excede o tempo-limite, o assistente diz ao cliente que não consegue consultar o pedido agora, em vez de adivinhar. A chave de API do fornecedor fica no gerenciador de segredos, e o filtro de registros do orquestrador a remove de qualquer mensagem de erro antes de gravá-la. Jonas acrescentou um teste que falha se uma sequência com formato de chave aparecer numa linha de registro, de modo que a regra é verificada a cada compilação.

3.5 Lendo o laboratório

O laboratório treina um classificador linear minúsculo e mostra sua acurácia caindo à medida que as entradas são empurradas por eps na pior direção: 0,985 sem perturbação, 0,505 com eps 1,0. É um lembrete de que a decisão de um modelo pode ser dirigida por mudanças pequenas e deliberadas na entrada; o código em torno dos modelos não deve supor que suas saídas estejam corretas. Para a Brightwell, o laboratório sustenta uma escolha de projeto, e não um fato sobre o ReturnGuard: decisões importantes não devem depender de uma única pontuação sem verificações. No laboratório, as perturbações são escolhidas pelo atacante; atacantes reais enfrentam limites sobre o que podem mudar, e é por isso que o laboratório é um lembrete, não uma medição.

3.6 Exemplo resolvido (caso sintético da Brightwell)

O console da equipe do Ask Brightwell exibia as respostas do modelo como HTML bruto. Jonas planta numa cópia de teste uma avaliação de produto contendo <img src=x onerror=...>; o assistente a cita e o script roda no navegador da equipe. Ele altera o console para escapar a saída e acrescenta um teste de unidade. A correção fica onde os dados saem do sistema, no console, e por isso vale para qualquer coisa que o modelo diga. O teste de unidade planta a mesma etiqueta de imagem e falha se o console voltar a renderizá-la como HTML, o que protege contra uma mudança futura que reintroduza a falha.

3.7 Erros e limites

Três erros são comuns: inserir texto do modelo em HTML, SQL ou shell; consertar JSON inválido por adivinhação; e segredos no código-fonte. Cada um desses erros transforma o texto de um modelo numa ação em outro sistema. O console do Ask Brightwell mostrou o primeiro; a validação rigorosa do JSON da ferramenta de devoluções evita o segundo; e manter a chave do fornecedor no gerenciador de segredos evita o terceiro. Testes para cada um garantem que uma mudança futura não traga a fraqueza de volta em silêncio, e é justamente para isso que eles são escritos como código.

Exercícios

Síntese

Programar em torno de modelos significa validar e codificar tudo o que passa para outros sistemas — sobretudo a saída do modelo — e falhar de forma fechada.

Continue estudando — capítulo 03 de 30

← Capítulo 02 (rascunho) · Índice de capítulos do curso · Capítulo 04 (rascunho) →

Respostas comentadas

EX007.

parse JSON → fail → reject
keys == {"action","order_id"} → else reject
type(action) is str and action in {"lookup","return"} → else reject
order_id matches ^[0-9]{6}$ → else reject
pass to tool service (which rechecks ownership)

EX008. HTML: escape sensível ao contexto. SQL: consultas parametrizadas. Shell: evite totalmente o shell e passe os argumentos como uma lista a um programa fixo, com uma lista de valores permitidos.

EX009. Com eps 1,0, a acurácia (0,505) fica perto do acaso: mudanças pequenas e escolhidas deliberadamente invertem cerca de metade das decisões. É um modelo linear de brinquedo, mas mostra que as saídas não são confiáveis diante de entradas adversariais.