Prevenção de ataque de força bruta no OJS com o Fail2ban

INTRODUÇÃO
O OJS (Open Journal Systems) é um software gratuito para o gerenciamento de periódicos acadêmicos bastante utilizado por instituições públicas de ensino superior pelo mundo todo. Como é possível notar, nos últimos anos o sites governamentais se tornaram cada vez mais alvos de ataques cibernéticos. Entretanto, não entrarei no mérito sobre a discussão que envolve os motivos de tamanho sucesso dessas investidas. Neste singelo artigo, pretendo apenas apresentar a ferramenta Fail2ban como uma solução para prevenção de ataques de força bruta em sites que utilizam o OJS.

MOTIVAÇÃO
Há algumas semanas, recebi notificações do Zabbix de alto processamento na máquina virtual que hospeda o OJS. Fiz algumas verificações nos registros do servidor HTTP (atualmente utilizamos o Nginx para o OJS) e descobri que haviam milhares de requisições nos formulários de login como essa:

[IP DO ATACANTE] - - [04/Aug/2021:12:53:59 -0500] "POST /index.php/[REVISTA]/login/signIn HTTP/1.1" 200 3383 "https://[PERIÓDICOS]/revista/index.php
/[REVISTA]/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4298.0 Safari/537.36"


À princípio, pesquisei por solução nativa contra ataques de força bruta no fórum oficial do OJS mas encontrei aqui apenas recomendação para adicionar código diretamente em um arquivo do próprio sistema. Outra possibilidade consiste na utilização do CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart), conforme encontrei no TCC do Denys Maciel. Recomendo bastante a leitura pois é raro documentação em português sobre segurança do OJS em específico. Também não entrarei no mérito dessa discussão, mas existe uma vertente de pensamento entre especialistas que considera o CAPTCHA como uma barreira de acessibilidade. Por isso, resolvi utilizar o Fail2ban para esse caso específico.

IMPLEMENTAÇÃO

Com alguns testes, notei que é registrado status HTTP 200 quando a tentativa de login é falha e status HTTP 302 quando a tentativa de login é bem sucedida. Para saber mais sobre status HTTP, acesse aqui.

Registro da falha:

192.168.0.2 - - [13/Aug/2021:11:49:37 -0500] "POST /index.php/revista/login/signIn HTTP/1.0" 200 10728 "https://teste-periodicos/index.php/revista/login" "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0"

Registro do sucesso:

192.168.0.2 - - [13/Aug/2021:11:50:42 -0500] "POST /index.php/revista/login/signIn HTTP/1.0" 302 0 "https://teste-periodicos/index.php/revista/login/signIn" "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0"

Assim, instalei o fail2ban:
# apt install fail2ban

Criei o filtro:
# vim /etc/fail2ban/filter.d/ojs.conf
[Definition]
failregex = ^ .* "POST /index.php/.*/login/signIn HTTP/(1.0|1.1)" 200
ignoreregex =

Copiei o arquivo jail.conf para personalização:
# cp /etc/fail2ban/jail.{conf,local}

Adicionei a jail correspondente no final do arquivo:

# vim /etc/fail2ban/jail.local
[ojs]
enabled = true
port = http,https
filter = ojs
logpath = /var/log/nginx/access.log
maxretry = 5
bantime = -1
ignoreip = [IP DO FIREWALL]

ATENÇÃO! Em [IP DO FIREWALL], adicionei o ip externo do firewall para que tentativas de login da própria instituição não ocasionassem bloqueio ao site. Como exemplo, coloquei o máximo de 5 tentativas (maxretry =5) para um bloqueio permanente (bantime = -1).

TESTE

Após tentativas de login sem sucesso, os registros foram conferidos em tempo real:

# tail -f /var/log/fail2ban.log
2021-08-16 12:25:09,081 fail2ban.database [8517]: INFO Connected to fail2ban persistent database '/var/lib/fail2ban/fail2ban.sqlite3'
2021-08-16 12:25:09,083 fail2ban.jail [8517]: INFO Creating new jail 'ojs'
2021-08-16 12:25:09,087 fail2ban.jail [8517]: INFO Jail 'ojs' uses poller {}
2021-08-16 12:25:09,088 fail2ban.jail [8517]: INFO Initiated 'polling' backend
2021-08-16 12:25:09,091 fail2ban.filter [8517]: INFO maxRetry: 5
2021-08-16 12:25:09,091 fail2ban.filter [8517]: INFO encoding: UTF-8
2021-08-16 12:25:09,092 fail2ban.filter [8517]: INFO findtime: 600
2021-08-16 12:25:09,092 fail2ban.actions [8517]: INFO banTime: -1
2021-08-16 12:25:09,093 fail2ban.filter [8517]: INFO Added logfile: '/var/log/nginx/access.log' (pos = 15040, hash = 3c9847359a91a556c930999d755f9027)
2021-08-16 12:25:09,096 fail2ban.jail [8517]: INFO Jail 'ojs' started
2021-08-16 12:26:16,547 fail2ban.filter [8517]: INFO [ojs] Found [IP ATACANTE] - 2021-08-16 12:26:16
2021-08-16 12:26:21,159 fail2ban.filter [8517]: INFO [ojs] Found [IP ATACANTE] - 2021-08-16 12:26:20
2021-08-16 12:26:25,169 fail2ban.filter [8517]: INFO [ojs] Found [IP ATACANTE] - 2021-08-16 12:26:24
2021-08-16 12:26:29,780 fail2ban.filter [8517]: INFO [ojs] Found [IP ATACANTE] - 2021-08-16 12:26:29
2021-08-16 12:26:33,990 fail2ban.filter [8517]: INFO [ojs] Found [IP ATACANTE] - 2021-08-16 12:26:33
2021-08-16 12:26:34,422 fail2ban.actions [8517]: NOTICE [ojs] Ban [IP ATACANTE]

Depois de alguns dias, parei de receber notificações de alto processamento. Acredito que funcionou do jeito esperado.

CONSIDERAÇÕES FINAIS

Numa era em que ataques são cada vez mais sofisticados, há ainda bastante do rústico que, por sua vez, também merece contestação desprimorosa. Apesar disso, o fail2ban se mostra como uma excelente e flexível ferramenta para prevenir abusos que, por mais que não cheguem a comprometer um sistema por invasão, findam por causar outros prejuízos por indisponibilidade. Sem mais para o momento.

REFERÊNCIAS:
Site do fail2ban: http://fail2ban.org/wiki/index.php/Main_Page

Site do OJS: https://pkp.sfu.ca/ojs/

Blocking WordPress scanners with fail2ban: https://osric.com/chris/accidental-developer/2019/07/block-wordpress-scanners-fail2ban/

How To Protect an Nginx Server with Fail2Ban on Ubuntu 14.04: https://www.digitalocean.com/community/tutorials/how-to-protect-an-nginx-server-with-fail2ban-on-ubuntu-14-04