A06 Insecure Design
Uma implementação perfeita de um design ruim continua sendo uma vulnerabilidade. A segurança precisa ser pedida antes da primeira linha de código, como requisito, e não encontrada depois como um bug.
O que é
Insecure design é uma fraqueza em como uma funcionalidade é concebida: um controle ausente, uma suposição de confiança que não se sustenta ou um fluxo de negócio que pode ser abusado exatamente como foi escrito. Difere de um bug de implementação. Um bug é código que não faz o que o design diz; um design inseguro é código que faz exatamente o que o design diz, e o problema é o design.
O :2025 o mantém como A06. A correção é mais uma prática do que um patch: faça threat modeling cedo, escreva casos de abuso ao lado dos casos de uso e reutilize padrões de design seguro.
Como o ataque funciona
- Uma redefinição de senha que pergunta "qual foi o seu primeiro bichinho?" permite que qualquer pessoa capaz de pesquisar sobre você assuma a conta.
- Um endpoint de cupom sem limite por cliente permite que um script esgote a promoção em minutos.
- Um fluxo de venda de ingressos que reserva assentos sem expiração permite que um bot bloqueie o local inteiro.
- Um checkout em várias etapas que confia no preço enviado na última etapa permite que os usuários paguem o que quiserem.
- Um formulário "esqueci meu usuário" que revela se um e-mail está cadastrado permite a enumeração de contas.
Design vulnerável, em código
O código abaixo é limpo e não tem bugs. O design permite que um atacante teste códigos promocionais sem limite e confia em um preço fornecido pelo cliente.
@app.post("/api/checkout")
@login_required
def checkout():
data = request.get_json()
total = data["total"] # preço decidido pelo cliente
code = data.get("promo")
if code and Promo.query.filter_by(code=code).first():
total = total * 0.5 # tentativas ilimitadas, reuso ilimitado
return charge(current_user, total)A correção
Projete os controles dentro do fluxo: o servidor calcula o preço a partir de dados confiáveis, um código promocional é de uso único por conta e as tentativas sofrem , e os limites são regras de negócio escritas antes de codificar. O "enumerar códigos promocionais" gerou o requisito.
@app.post("/api/checkout")
@login_required
@rate_limit("10/minute", key=lambda: current_user.id)
def checkout():
cart = Cart.for_user(current_user) # fonte da verdade no servidor
total = cart.total() # os preços vêm do catálogo
code = request.get_json().get("promo")
if code:
promo = Promo.redeem(code, user=current_user) # uso único por usuário, atômico
if promo is None:
abort(400, "invalid promo")
total = promo.apply(total)
return charge(current_user, total)Prove a correção
Um controle de design precisa de um teste escrito a partir do caso de abuso, não do caminho feliz. Cada caso de abuso que você listou vira um teste automatizado: o cliente não pode definir o preço, um código não pode ser reutilizado e uma rajada de tentativas é limitada.
def test_client_cannot_choose_the_price(client, alice, cart_of_100):
r = client.post("/api/checkout", json={"total": 1}, auth=alice)
assert charged_amount(r) == 100
def test_promo_is_single_use(client, alice, promo_half_off):
assert client.post("/api/checkout", json={"promo": promo_half_off}, auth=alice).status_code == 200
assert client.post("/api/checkout", json={"promo": promo_half_off}, auth=alice).status_code == 400
def test_promo_guessing_is_throttled(client, alice):
codes = [client.post("/api/checkout", json={"promo": f"TRY{i}"}, auth=alice).status_code for i in range(30)]
assert 429 in codesComo atacante, quero adivinhar códigos promocionais para obter descontos que não deveria.
-> controle: rate limit por conta e por IP, códigos longos e aleatórios, uso único
-> teste: test_promo_guessing_is_throttled
Como cliente, quero alterar o preço na requisição para pagar menos.
-> controle: o preço é calculado no servidor a partir do catálogo
-> teste: test_client_cannot_choose_the_priceChecklist
- Faça threat modeling de novas funcionalidades antes de construí-las: o que estamos protegendo, quem ataca, o que os detém ( é um bom começo).
- Escreva casos de abuso ao lado dos casos de uso e transforme cada um em um requisito e um teste.
- Reutilize padrões comprovados (autenticação, rate limiting, gestão segura de sessão) em vez de inventá-los.
- Separe tenants e níveis de confiança por design; limite o uso de recursos por usuário.
- Use um padrão como o para transformar "seja seguro" em requisitos verificáveis.
- Revise o modelo quando o design mudar, não uma vez por ano.
conexões no mapa
- A06 Insecure Designdefine as regras deA01 Broken Access Control
- A06 Insecure Designdefine o fluxo de recuperação deA07 Authentication Failures
atualizado em 14/10/2026 · fontes para aprofundar · reportar erro
em outros domínios
- Threat modeling - pensar como o atacante antes de codarappseco método que encontra a falha antes do código
Estude antes
teste o que entendeu
Carregando perguntas…