Meu chat no NeoCities funciona mesmo sendo bloqueado pelo CSP. Depois de meses, entendi o porquê.
A pouco mais de 1 ano atrás, eu queria arrumar alguns passatempos, mesmo cheio de problemas já. Resolvi explorar um serviço de hospedagem estática chamado "Neocities". A premissa dos cara é te dar um espaço para você criar um site que relembra a "Internet antiga". Mas você não precisa necessariamente usar o neocities só pra isso.
Fui no neocities pra começar, pois a comunidade é bem grande e sempre tem gente respondendo suas dúvidas.. Além de que já ouvi meu irmão reclamando bastante de problemas com CSP (e alguns outros bobos dele que ele queria culpabilizar o neocities).
A coisa que mais via nos fóruns, onde os usuários do Neocities postavam e etc.. era sobre o Content Security Policy (CSP). Diziam que é muito restritivo, não deixava iframes, embeds, fetch etc.. Pra testar, resolvi criar um Chat Online usando o Firebase Realtime Databasee, realmente, a princípio a conexão não firmava de jeito nenhum.
Minha proposta inicial era que qualquer conexão de tempo real, como WebSockets, seria bloqueada na hora.
Quando fui testar usando as versões mais recentes do SDK do firebase, descobri algo estranho e que vai contra alguns conceitos que eu já tinha em mente:
- No Firefox: TUDO deu certo. O chat conectava via
wss://na velocidade da luz, como se o CSP nem existisse. Me deu orgulho e.. um pouco de tédio. - No Chrome: O console gritava exatamente o erro esperado:
Refused to connect to 'wss://...' because it violates the following Content Security Policy directive.... Aí sim deu um frio na barriga.
Até aí é tranquilo. Mas aí veio a parte que fez eu ficar olhando a tela vários minutos, tentando entender: o chat continuava funcionando perfeitamente no Chrome. As mensagens iam e voltavam, mesmo com o console me dizendo que a conexão principal estava bloqueada. "Se tá funcionando, deixa funcionando" pensei.. Mas fiquei curioso, queria saber o porquê (Dor de cabeça só de lembrar).
A Investigação:
Fui caçar esse tráfego "fantasma" na aba Network. Achava que era um fallback para dezenas de requisições Fetch, sei lá.. um "WS" (HTTPS/HTTP - WSS/WS) ou alguma magia antiga, mas... nada. A aba estava estranhamente limpa silenciosa em questão as mensagens que enviava e recebia.
Foi só revirando o F12 e olhando com muita atenção que eu entendi a magia negra do SDK do Firebase. Ele não estava fazendo um polling tradicional. Ele estava usando Long Polling: abria uma conexão HTTPS (permitida pelo CSP) e a deixava "pendurada" por 40 segundos, só esperando o servidor mandar alguma coisa. Era uma única requisição silenciosa, que parecia travada, mas que na verdade funcionava como um túnel de dados improvisado.
A resiliência do SDK, se adaptando de forma diferente em cada navegador, foi a primeira grande lição do projeto. Mesmo que, depois de um tempo, essa conexão tenha trocado, em outros navegadores funciona diferente e tals (sinta-se a vontade para abrir o chat e ver pesquisar como ele está firmando conexão agora). Eu resolvi não ficar muito nessa parte, focar mais em desenvolver o chat, layout, segurança, etc..
Parte 2: O Paradoxo da Moderação sem Backend
Com o chat online, vieram os trolls e pessoas mal educadas, clássico. Eu precisava de moderação, mas tinha duas travas inegociáveis:
- Zero Backend: Sem Cloud Functions, apenas regras de segurança do Firebase. Não queria pagar nada.
- Privacidade Total: Não podia exigir login, nem expor o IP ou qualquer hash identificador dos usuários publicamente.
Isso criou um paradoxo. Para banir um troll via Security Rules, a regra precisa saber quem ele é (receber um identificador). Mas se o cliente envia esse identificador junto com a mensagem pública, eu quebro a privacidade de todos, permitindo a criação de "perfis sombra" dos usuários anônimos.
Como validar uma credencial sem nunca expô-la? Foi bem complicado achar uma lógica que fizesse sentido e desse para implementar.
A Solução: Arquitetura de "Fila de Escrita Cega"
A melhor ideia foi implementar um sistema de transação em duas etapas no frontend, apoiado por regras de segurança estritas.
Criei duas coleções:
/pendentes(A Caixa-Preta): Regra.readéfalse. Ninguém vê o que entra. Regra.writeexige o hash de identificação (gerado localmente via IP + salt) e valida se ele não está banido./publicos(O Chat Visível): Regra.readétrue. Regra.writeproíbe explicitamente a presença do hash de identificação.
O Fluxo:
O cliente honesto escreve primeiro na caixa-preta (enviando sua credencial). Se passar, ele pega o mesmo ID gerado e escreve a mensagem limpa (sem credencial) na área pública, e por fim apaga o rastro da caixa-preta.
Um troll que tentar escrever direto na área pública é barrado porque não tem permissão para enviar dados sem credencial ali. Se ele tentar escrever na caixa-preta estando banido, é barrado pela validação do hash.
Observações
Sobre a segurança, claro que eu não deixei só nas regras do banco e afins, também deixei no Front alguns "gatilhos", envio rápido d e mensagens, blacklist (no Front e no back), um algoritmo levenshtein simples pra bloquear mensagens semelhantes etc..
Esse site já está funcionando a mais ou menos 1 ano e 2 meses e só tive 2 casos de pessoas mal intencionadas. Um foi spam, ceifaram o limite de usuários conectados simultaneamente (100). É o outro foi palavras tóxicas nas mensagens e nos nomes.
Mesmo no Front, o pessoal burlou facilmente a segurança, então apelei um pouco e agora tem até banimento (temporário e permanente).
Além desse chat online, criei um cassino (simulado) com autenticação. Mas, se eu quiser, comento em um próximo post como ele funciona.
Conclusão
Este projeto reforçou a ideia de como as restrições severas podem impulsionar a criatividade arquitetural. Construí um sistema funcional, resiliente a diferentes comportamentos de navegadores e razoavelmente seguro contra abusos, tudo isso sem gastar um centavo com infraestrutura de backend e nem em momento algum.
Para quem quiser ver o resultado (e tentar quebrar a segurança): https://cillsghost.neocities.org/
Documentei todo o processo técnico em um tutorial detalhado aqui: https://cillsghost.neocities.org/tutorial/tuto (Só não tem a segunda parte. Cria aí sua própria segurança kk)