Segurança
- Revisão de segurança de todo o código, e correção das falhas graves que encontrou:
- Cópias de segurança: os arquivos de cópia de segurança (uma cópia da base de dados e da pasta
config/, que contém as palavras-passe) já não podem ser transferidos a partir de um simples endereço web, nem com Apache nem com nginx; só a administração (Monitorização) os entrega. O seu nome inclui agora uma parte aleatória, impossível de adivinhar. A pasta dos registos (logs/) está protegida da mesma forma. - Tokens secretos: os identificadores de sessão, os tokens dos formulários e as ligações enviadas por e-mail (palavra-passe esquecida, validação da conta) saem de um gerador aleatório seguro; eram previsíveis. Estas ligações expiram ao fim de uma hora, e um novo pedido anula o anterior.
- Início de sessão: a autenticação de dois fatores (2FA) e os banimentos são verificados seja qual for a forma de iniciar sessão — palavra-passe, conta Discord, Google ou GitHub, ligação «palavra-passe esquecida» —, e já não só com a palavra-passe.
- Ligações nos e-mails: o endereço do site usado nas ligações enviadas por e-mail, no regresso de um início de
sessão pelo Discord, Google ou GitHub e no regresso de um pagamento Stripe é o guardado na instalação
(
config/url.php), e já não o que o pedido anuncia: um atacante já não consegue fazer enviar uma ligação que leve ao seu próprio site. - Páginas de administração: um nome de anexo armadilhado (numa denúncia de moderação) ou um endereço IP falso
(cabeçalho
X-Real-IP, no histórico das sessões) podiam injetar código nelas; são agora verificados e neutralizados. A mensagem de boas-vindas de um servidor de jogo, fornecida por um serviço externo, é limpa antes de ser mostrada. - Ações de administração: eliminar, ativar ou desativar, fechar, aprovar, restaurar, purgar… exigem agora um token de segurança nos 23 módulos que não o tinham. Eram simples ligações ou formulários sem token: uma ligação armadilhada, clicada por um administrador com sessão iniciada, bastava para desencadear a ação.
- Ferramentas: os scripts da pasta
tools/só correm a partir da linha de comandos, nunca a partir de um endereço web; a cada alteração, a integração contínua verifica que nenhuma dependência PHP tem uma falha conhecida (composer audit). - Botões silenciados pela política de segurança (CSP): a CSP estrita do site bloqueia as ações escritas no HTML
(
onclick="…"), por isso estes botões não faziam nada: banner de cookies, número de linhas das tabelas de administração, confirmações da reciclagem e das revisões, nome do ficheiro anexado nas mensagens, ações em lote das menções do fórum, seleção de um campo com um clique, botão da janela de eliminação. Estão ligados de outra forma e funcionam também no conteúdo carregado sem recarregar a página. A escolha do número de linhas chamava além disso o jQuery, que já tinha sido retirado.
- Cópias de segurança: os arquivos de cópia de segurança (uma cópia da base de dados e da pasta
- Política de segurança do conteúdo (CSP) estrita: servida pelo site com um token próprio de cada página, recusa os
scripts escritos na página sem esse token (
unsafe-inline), o código construído em tempo de execução (unsafe-eval) e os objetos incorporados (object-src 'none'); os scripts só vêm agora do site e de uma curta lista de endereços, já que o TinyMCE e o CodeMirror passam a ser alojados pelo próprio site. - Sessão renovada ao iniciar sessão: o identificador de sessão muda quando se inicia sessão; um identificador imposto antes do início de sessão já não dá acesso à sessão aberta.
- Segredos cifrados na base de dados: a palavra-passe SMTP e a chave da autenticação de dois fatores (2FA) estão cifradas (AES-256-GCM); uma cópia da base de dados já não as entrega em claro. Um valor existente é cifrado da próxima vez que for guardado.
- Webhooks: um webhook já não pode visar a rede interna do servidor (endereços privados, locais ou reservados recusados, redirecionamentos não seguidos).
Adicionado
- Ficheiros CSS e JavaScript sempre atualizados: cada ficheiro é pedido com a sua data de modificação (
?v=…), de modo que o navegador o recarrega assim que muda — incluindo um ficheiro substituído por um tema ou enviado à mão —, sem ter de aumentarnf_version_css(que continua a ser usado quando o ficheiro não é encontrado). - Marketplace: a janela «Atualizações» da página Temas & Addons indica que está disponível uma nova versão do NeoFrag Reborn; a atualização do núcleo propriamente dita continua a ser manual.
- Atualizações dos addons: a mesma janela compara os addons instalados com o catálogo do marketplace e instala as novas versões, com as suas migrações de base de dados.
- Instalador: novo aspeto — logótipo do NeoFrag Reborn, banner «Fork não oficial do NeoFrag», crédito a Michaël BILCOT e Jérémy VALENTIN (licença LGPLv3) no rodapé, tipos de letra Inter e Space Grotesk, cores turquesa.
- Ficheiros: um gestor de ficheiros (árvore de pastas, envio, permissões de leitura por ficheiro ou por pasta), portado do HiddenCMS, da HiddenBlob (LGPLv3). Incluído no núcleo.
- Emojis personalizados (módulo Emojis, incluído no núcleo): a administração acrescenta imagens com nome, que
:nome:mostra nas mensagens e assinaturas do fórum, nas notícias, nas páginas, nos eventos e no recrutamento, entre outros. - Newsletter: envio agendado na data escolhida, em pequenos lotes tratados pela tarefa agendada do site (cron) em vez de enviar tudo de uma vez; acompanhamento das aberturas (taxa de abertura por campanha, medida por uma imagem invisível); modelos de e-mail reutilizáveis; escolha dos destinatários: todos os subscritores, só os membros ou um grupo.
- Eventos: um evento pode repetir-se todos os dias, todas as semanas ou todos os meses, o número de vezes escolhido — cada data torna-se um evento de pleno direito. Os participantes, exceto os que recusaram, recebem um lembrete no site (sino das notificações) antes do evento, 24 horas antes por predefinição; os lembretes passam pela tarefa agendada do site (cron).
- Reações com emoji: seis reações à escolha (👍❤️😂😮😢😡) em vez de um só coração, como no Discord ou no Facebook, cada uma com a sua contagem; os antigos «gosto» passam a ❤️.
- Widget «Estado ao vivo» (o antigo widget Twitch): segue vários canais ao mesmo tempo, no Twitch e no YouTube (credenciais Twitch e chave de API do YouTube necessárias), com jogo, espectadores, título, miniatura e leitor integrado.
- Páginas compostas: por baixo do seu texto, uma página pode mostrar blocos de outros módulos (últimas notícias,
notícias de uma categoria, últimos artigos, transferências populares), colocados pela ordem pretendida e configurados
um a um ao editar a página. O código curto aceita também definições, por exemplo
[block:news.latest count=2]. - Tema Extend, de Chewbaka, portado para o NeoFrag Reborn (Bootstrap 5, modos dia e noite), com licença CC BY-NC-SA 4.0; disponível no marketplace.
- Pesquisa instantânea: o widget de pesquisa propõe resultados a partir da segunda letra escrita (notícias, fórum, páginas).
- Ordenação das listas da administração: dez listas (notícias, artigos, anúncios classificados, transferências, FAQ, livro de visitas, ligações, multimédia, sondagens, wiki) ordenam-se segundo o critério escolhido.
- Avatares animados: um GIF animado enviado como avatar continua animado — sempre, se o servidor tiver Imagick, e caso contrário desde que não ultrapasse 250 × 250 píxeis.
- Permissões: a grelha das permissões abre-se numa janela, sem sair da página.
- Gestor de ficheiros do webmaster (Monitorização, botão «Gerir ficheiros»): percorrer os ficheiros do site,
editá-los num editor (CodeMirror), criá-los, mudar-lhes o nome ou eliminá-los. As pastas
config/,logs/ebackups/ficam fora de alcance; cada gravação guarda uma cópia.nfbake entra no registo de auditoria. Qualquer escrita exige a palavra-passe de webmaster, distinta da da conta e guardada fora da base de dados (definida na instalação ou na Monitorização, cartão «Segurança webmaster»); uma vez definida, é também pedida de novo para eliminar ou purgar cópias de segurança e para mudar a chave da tarefa agendada. - Notas de versão a partir de uma única fonte:
tools/changelog-section.phpextrai uma versão deste registo de alterações (CHANGELOG.md), em Markdown ou em HTML; as notas das releases do GitHub saem daí, e os seus textos já não podem divergir.
Alterado
- Instalador «tudo incluído»: o assistente passa a ter quatro passos (Requisitos → Base de dados → Administrador → Concluído). Todos os módulos, widgets e temas incluídos são instalados e ativados no passo «Base de dados», e a página inicial mostra as notícias assim que a instalação termina; o passo «Módulos» e os seus perfis desaparecem.
- Bootstrap 4.6.2 → 5.3.8, e o jQuery já não é carregado: o JavaScript do site apoia-se numa pequena ferramenta
própria (
window.NF). Nove extensões jQuery ou Bootstrap 4 são substituídas (notificações → toasts do Bootstrap 5, selectize → Tom Select, seletor de data → flatpickr, FullCalendar 3 → 6, seletores de cor e de ícone, árvore e medidores → código próprio, barras de deslocamento → deslocamento nativo), e o jQuery UI pelo SortableJS. Quatro scripts esquecidos ainda chamavam o jQuery: foram reescritos na 1.2.0. - Modo escuro: tabelas, cartões, menus pendentes e o editor de texto adotam as cores escuras do tema em vez de ficarem brancos, em todos os temas.
- Definições dos widgets e disposição das páginas: guardadas em JSON em vez do formato de serialização do PHP, o que fecha uma via de injeção de código; as definições antigas continuam a ser lidas.
- Gráficos das estatísticas: o Chart.js (licença MIT) substitui o Highstock, uma biblioteca comercial que impedia redistribuir livremente o CMS.
Corrigido
- MySQL 8: abrir um tópico ou responder no fórum, dividir ou juntar tópicos, criar uma conversa nas mensagens, reordenar as imagens do Slider… falhavam com um erro fatal no MySQL 8, que recusava a forma como o site abria as suas transações. Passam agora pelas funções do mysqli previstas para isso.
- Emojis: um emoji — ou qualquer carácter de 4 bytes — num texto provocava um erro de base de dados. A ligação e
todas as tabelas passam a
utf8mb4(colaçãoutf8mb4_unicode_ci, reconhecida tanto pelo MySQL 8 como pelo MariaDB 10; as colações próprias do MariaDB 11, que faziam falhar a instalação noutros servidores, desaparecem). where('col', [])gerava uma condição vazia (→DELETE/UPDATEsobre toda a tabela): produz agora1 = 0(conjunto vazio).- Datas posteriores a 2038: o envio agendado de uma newsletter, a publicação agendada de uma notícia, de um artigo, de uma galeria ou de uma página, e as datas de um evento não podiam ultrapassar 19 de janeiro de 2038: para lá disso, o MariaDB recusava a gravação, sem nenhuma mensagem. Estas datas vão agora até ao ano 9999; as instalações existentes são atualizadas por uma migração.
/user/logine/user/registrationrespondiam «página não encontrada»: os botões «Entrar» e «Registo» dos temas levam lá quando o JavaScript está desativado ou a ligação é aberta num novo separador. Estes endereços abrem agora a janela de início de sessão ou de registo.- Nebula: o botão «Registo» continuava visível quando as inscrições estavam fechadas, e levava a uma página não encontrada. Passa a estar escondido nesse caso.
- Widget Discord: indica a verdadeira causa de uma falha (widget desativado no servidor, identificador desconhecido, rede) em vez de uma mensagem genérica, mostra o verdadeiro logótipo do servidor (lido no convite público) e conta corretamente os membros online — a lista fornecida pelo Discord para nos 100, o que falseava a contagem dos servidores grandes.
- Widget TeamSpeak, vista em árvore: falhava no PHP 8 e recorria ao cartão simples. A árvore dos canais e dos clientes é agora desenhada pelo próprio widget, com ícones Font Awesome (já não é preciso um pacote de ícones); um erro de rede mostra uma mensagem genérica, sem revelar o endereço nem a porta do servidor.
- Widgets Discord, Twitch e Servidor de jogo: mostravam «inacessível» mesmo quando o serviço respondia (a resposta era descodificada duas vezes). Os pedidos do site enviam além disso um identificador de navegador (User-Agent): sem ele, os serviços protegidos pela Cloudflare respondiam com uma recusa (erro 403).
- Cópias de segurança e Monitorização: criar uma cópia de segurança falhava nos servidores com PHP-FPM, e falhava
com nginx ou Plesk, que respondiam «página não encontrada» aos endereços em
.json; eliminar ou transferir uma cópia de segurança levava a uma página não encontrada; a árvore da Monitorização mostrava um erro na primeira atualização. Tudo corrigido. - Marketplace inacessível: URL predefinido passado para sem www (
https://neofrag-reborn.xyz/marketplace). - Envio de vários ficheiros: o primeiro ficheiro de um envio múltiplo fazia falhar o envio («página não
encontrada») — era o que bloqueava o envio no módulo Ficheiros. Para os programadores:
uploaded_file()tomava o índice0pela ausência de índice. - Webhooks: o registo de um membro (
user.registered), um novo comentário (comment.created) e um novo tópico do fórum (forum.topic) desencadeiam finalmente o seu webhook; estes eventos eram propostos mas nunca emitidos.
Documentação
- Documentação (guias da wiki): para os programadores, os formulários (
form()eform2()) e a validação das definições de um widget; para os administradores, a resolução de problemas da instalação, um exemplo de criação de um cargo e o que fazer quando o marketplace está inacessível. Uma nota explica como configurar o nginx ou o Plesk, que respondiam «página não encontrada» aos endereços em.json.