turicas / brasil.io

Backend do Brasil.IO (para código dos scripts de coleta de dados, veja o link na página de cada dataset)
https://brasil.io/
GNU General Public License v3.0
922 stars 145 forks source link

Criar política pra banir requisições abusivas #426

Closed berinhard closed 3 years ago

berinhard commented 4 years ago

Atualmente tanto throttling do DRF quanto o uso do django-ratelimit são amigáveis e permite que a pessoa faça requisições após ter expirado o tempo do bloqueio. Precisamos criar uma política para banir requisições recorrentemente abusivas e evitar assim sobrecarga dos servidores.

lguima commented 4 years ago

não tenho experiência, mas em um curso de segurança de VPS, foi citado o Fail2ban, não sei se é aplicável pra esse cenário

francilioaraujo commented 4 years ago

Se as requisições estiverem sendo feitas em grande volume de um só ip o fail2ban pode resolver. Talvez uma solução mais robusta seja utilizar um Proxy reverso a frente da API e realizar o limiting por ele. Esta abordagem também pode dar uma melhor visibilidade do que está ocorrendo no ataque e dá mais possibilidades de bloqueio

francilioaraujo commented 4 years ago

O dokku usa o nginx como proxy reverso. Neste artigo há algumas configurações de segurança para o mesmo https://docs.nginx.com/nginx/admin-guide/security-controls/controlling-access-proxied-http/

rudaporto commented 4 years ago

Uma opção em geral pra melhorar a performance é usar um proxy reverso com cache.

Como os dados não mudam o tempo todo e podem expirar a cada 5 ou 10 minutos deve ajudar bem.

Não sei se vocês tem um servico de CDN mas eles também podem fazer isso automaticamente.

Outra opção é adicionar o cache na camada do DRF mas nesse caso requer mudanças na aplicação.

rudaporto commented 4 years ago

Sobre cache no DRF achei essa documentação:

https://www.django-rest-framework.org/api-guide/caching/

carloseahenriques commented 4 years ago

Atualmente tanto throttling do DRF quanto o uso do django-ratelimit são amigáveis e permite que a pessoa faça requisições após ter expirado o tempo do bloqueio. Precisamos criar uma política para banir requisições recorrentemente abusivas e evitar assim sobrecarga dos servidores.

* [ ]  Adicionar controle de X vezes em Y minutos que uma pessoa pode ser bloqueada (429);

* [ ]  Criar middleware que fica na frente de todos os outros e checa se o request (IP ou user agent) está banido;

Da uma lida. Vai ajudae muito. https://www.linkedin.com/pulse/quando-pr%C3%B3ximo-finja-estar-longe-arte-da-guerra-sun-tze-henriques/

turicas commented 4 years ago

Pessoal, obrigado pelas sugestões. Gostaria de salientar que as requisições que estão impactando negativamente os servidores são requisições não cacheáveis, pois utilizam a API ou a interface Web para fazer buscas (full-text search) na tabela govbr/auxilio_emergencial.

Pela quantidade de requisições, a pessoa que está fazendo isso terminaria muito antes suas buscas se simplesmente baixasse o dataset completo e fizesse as buscas localmente - provavelmente é alguém que não sabe que é possível baixar o dataset completo, ou é preguiçoso e prefere fazer de maneira não otimizada pela API, ou não se importa com o impacto que as requisições podem causar no servidor. Porém o site (usado principalmente por não programadores) não pode ficar lento ou inutilizável porque alguém fez um programa nada otimizado para consultar nossa API e não se preocupou com os impactos disso; quem deve sofrer sanções nesse caso é o usuário abusivo, não o usuário normal.

Como essa pessoa não está usando a API segundo nossas recomendações, nós não sabemos quem é para alertar e esse comportamento já aconteceu outras vezes (ver #316), decidimos restringir o acesso à API apenas para requisições autenticadas - dessa forma conseguiremos contato com quem eventualmente abusar (para que seja orientado a usar de maneira a não prejudicar outros usuários) e o controle de limitação de requisições poderá ser por usuário/token de autenticação. Será importante termos algum contato dos usuários da API e traçar métricas de uso por usuário para que possamos melhorar o serviço no futuro e definir estratégias melhores de infraestrutura (separação em mais servidores, por exemplo).

Dito isso, seguem algumas considerações sobre nossa infra atual:

GabrielLasso commented 4 years ago

Pelo que eu entendi, a ideia é continuar permitindo as pessoas a usar a API, porem de forma mais controlada, certo?

Uma possível solução que eu tenho em mente é obrigar o uso de uma chave de autenticação para usar a API, e para criar a chave pode ter um processo simples de preencher um formulário com por exemplo CPF/CNPJ, nome, etc, e um captcha. Aí vcs limitam a uma chave por CPF (pode deixar a pessoa criar uma nova e apagar a antiga se ele tiver perdido/vazado), talvez exigindo uma senha para isso.

Isso possibilita vcs monitorarem melhor quem está usando a API e ela continua pública, só com um passo a mais para cadastro. E se alguma chave começar a fazer ataques, é só banir ela. Se a pessoa continuar criando chaves, daria para banir o CPF ou entrar em contato com a pessoa para explicar como é o melhor jeito de consumir a API sem prejudicar o servidor.

Eu nunca consumi a API de vocês, só baixei os dados em csv uma vez pelo site, mas espero que essas ideias ajudem

EDIT: opsss, agora que eu li a sua resposta que você falou que já restringiu só para requests autenticados, foi praticamente o que eu falei

turicas commented 4 years ago

EDIT: opsss, agora que eu li a sua resposta que você falou que já restringiu só para requests autenticados, foi praticamente o que eu falei

@GabrielLasso ainda não restringimos, mas decidimos restringir. Porém não pretendemos pedir o CPF.

rudaporto commented 4 years ago

Achei muito boa a decisão de pedir a identificação pra usar a interface e as APIs de busca.

Não sei se faz sentido mas uma idéia seria pedir a autenticação e como alternativa sugerir o download do dataset inteiro o qual neste caso não precisaria de identificação.

turicas commented 4 years ago

Não sei se faz sentido mas uma idéia seria pedir a autenticação e como alternativa sugerir o download do dataset inteiro o qual neste caso não precisaria de identificação.

@rudaporto é exatamente isso que propomos. ;) O download dos dados completos existe hoje e não necessita de autenticação (e continuará assim).

DaniloNC commented 4 years ago

Edit.: Parece que o problema é com a API e portanto a implementação do captcha não seria possível. De qualquer maneira, é possível configurar o rate-limit no cloudflare antes dele chegar no servidor web/backend

turicas commented 4 years ago

Edit.: Parece que o problema é com a API e portanto a implementação do captcha não seria possível. De qualquer maneira, é possível configurar o rate-limit no cloudflare antes dele chegar no servidor web/backend

Nós consideramos utilizar o rate limit do CloudFlare, mas pela quantidade de requisições que recebemos parecia sair um pouco caro, porém ainda não descartei totalmente, mas precisamos avaliar se resolveria completamente (muitas requisições vêm de IPs completamente diferentes - não vem somente de um IP -, o que dificulta o rate limit).

turicas commented 4 years ago

Pra ficar mais claro a questão dos valores, seguem algumas estimativas:

Nota: considerei o dólar a R$6 para já contabilizar o spread bancário de 4%, IOF de 6,38%, possíveis oscilações e para facilitar as contas.

turicas commented 3 years ago

Como já implementamos a autenticação na API e o bloqueio por quantidade de requisições, vou fechar essa issue. Relacionado a esse assunto, acabei de publicar no blog o artigo Nossa API será obrigatoriamente autenticada.