Recentemente, descobri um sério problema de segurança nos sites de comércio eletrônico Django, onde os usuários podem modificar os preços dos produtos antes de adicioná-los ao carrinho.
Muitos desenvolvedores permitem que os usuários enviem dados de preços do frontend , que podem ser facilmente adulterados usando o Burp Suite ou ferramentas de desenvolvedor do navegador .
Exemplo do problema:
Considere uma visualização simples do Django que adiciona itens ao carrinho:
def add_item(request):
product_id = request.GET.get('product_id')
price = request.GET.get('price') # User-controlled value (security risk)
qty = int(request.GET.get('qty', 1))
cart_item = {
'product_id': product_id,
'qty': qty,
'price': price, # This price comes from the user, not the database!
}
request.session['cart'] = request.session.get('cart', {})
request.session['cart'][product_id] = cart_item
request.session.modified = True
return JsonResponse({'message': 'Added to cart'})
Como um invasor pode explorar isso:
- Um produto custa US$ 500 no banco de dados.
- O usuário clica em "Adicionar ao carrinho" .
- Em vez de enviar o preço original, o invasor intercepta a solicitação usando o Burp Suite .
- O
price
campo é alterado para $1 e a solicitação é encaminhada. - O carrinho agora armazena o preço manipulado , e o usuário pode prosseguir para a finalização da compra com o valor errado.
Por que isso é um risco à segurança?
- O backend confia nos dados do frontend , que podem ser facilmente manipulados .
- A sessão armazena o preço errado , levando a perdas financeiras .
- Os invasores podem comprar produtos caros a preços extremamente baixos modificando os dados da solicitação.
Pontos de discussão para a comunidade:
- Quais são as melhores práticas para evitar isso?
- Os sites de comércio eletrônico devem sempre buscar preços no banco de dados em vez de aceitá-los do frontend?
- De quais outras vulnerabilidades os desenvolvedores devem estar cientes ao manipular dados de carrinho no Django?
Adoraria saber sua opinião sobre isso!
Você não precisa do preço, a visualização deve adicionar o
product_id
ao carrinho, e talvez umquantity
, mas adicionar algo ao carrinho não tem preço envolvido. Isso torna ainda mais complicado aplicar descontos posteriormente, já que o preço é determinado por produto.Não por si só, existem algumas APIs que determinam preços na hora. Bots que, portanto, aumentam o preço se houver mais demanda, ou se um concorrente baixar seus preços.
Isso não tem nada a ver com um carrinho, você sempre pergunta ao usuário o mínimo absoluto que ele precisa saber e, por design, limita os parâmetros, pois isso limita o que você pode alterar.
Vi algumas perguntas no StackOverflow com uma abordagem semelhante. Às vezes, eles definem uma classe
Cart
que opera nos dados da sessão. Mas isso foi realmente um design ruim, e frequentemente não apenas em termos dessa vulnerabilidade de segurança, mas desempenho, integridade referencial, etc.: você adiciona um item a um carrinho com uma solicitação GET, o que não faz sentido; e você pode até mesmo adicionar um produto não existente também.A solicitação GET, portanto, significa que se uma pessoa que faz a solicitação clicar em atualizar, ela será feita uma segunda vez. Mas, mais importante, as solicitações GET devem ser armazenáveis em cache, o que claramente não é o caso, e também coloca o
product_id
(eprice
) na URL, o que significa que ele é pelo menos visível no caminho também, o que também não é uma boa prática.Sempre tive a impressão de que alguém deu um "tutorial de Django eCommerce" no Youtube, e as pessoas copiaram algum código. Mas, aparentemente, alguns realmente moveram isso para produção.