Pular para o conteúdo
murad.guide

PHP

Capítulo 19

Falhas controladas e proteção do formulário

6 min de leitura 1.122 palavras 1 gravura

Neste capítulo você vai conferir um token de formulário e tratar falhas sem prometer uma gravação inexistente.

Seu ponto de partida

Estude depois do capítulo 18. O exemplo deste capítulo é 19-falhas-protecao, no pacote que acompanha o volume. Copie a pasta inicio para um diretório de prática e preserve o original. A raiz do exemplo é a referência concluída desta etapa. Sua cópia permite experimentar sem perder a referência.

Execute o PHP na raiz da sua cópia com php -S 127.0.0.1:8000 -t public. Abra http://127.0.0.1:8000/. Não abra PHP por duplo clique nem use o servidor de Python. Encerre com Ctrl+C antes de trocar de etapa.

Como praticar

Leia a explicação e digite ou confira o código indicado. Mude uma coisa. Antes de conferir, anote sua previsão. Salve, recarregue e compare. Se não acontecer o esperado, investigue o erro antes de avançar. Cada experiência parte dos valores da referência. Depois de conferir, desfaça somente a mudança desse experimento antes de começar a próxima lição. Você pode parar depois de uma lição e retomar mais tarde.

Confira se o envio veio do formulário desta sessão

Um token é um valor imprevisível gerado pelo programa. O exemplo usa random_bytes para criar esse valor e o guarda na sessão. O formulário o envia em um campo oculto.

No POST, o servidor confere o token com hash_equals. Um valor ausente ou diferente é recusado antes da gravação. Essa conferência ajuda a impedir que outra página envie um formulário usando a sessão deste navegador, situação conhecida como CSRF.

O token não é senha, login ou permissão para publicar o livro de visitas. Ele acrescenta uma proteção específica a este formulário. Toda a implementação está fornecida; acompanhe a criação, o campo e a conferência como três lugares que precisam combinar.

Faça no seu exemplo

Confira o campo oculto no formulário e a condição no início do tratamento POST.

<input type="hidden" name="token" value="<?= escapar($_SESSION["token"]) ?>">

Mude uma coisa e preveja

Numa cópia, retire só o campo oculto, mantendo os demais campos fictícios válidos.

Anote o que você acha que vai acontecer antes de salvar e recarregar.

Recarregue e confira

Envie. O programa deve informar que o formulário precisa ser recarregado e não acrescentar registro. Restaure o campo e refaça a abertura antes de testar sucesso.

Se não acontecer

Se gravar sem token, confira a condição antes da escrita. Não aceite simplesmente qualquer texto no campo token.

Recuse falhas e arquivos quebrados

Uma operação no disco pode falhar. A pasta pode não existir, o arquivo pode estar inacessível ou um registro JSON pode ter sido alterado incorretamente. Nesses casos, a resposta precisa explicar que não concluiu a tarefa.

O código usa try e catch para tratar falhas previstas, chamadas exceções. Ele mostra uma orientação curta na página e deixa o arquivo original disponível para você conferir no editor. Não apaga registros inválidos para parecer que a lista está limpa.

Antes de qualquer experiência que altere dados, use uma cópia separada e encerre o servidor. Guarde a referência original. Não provoque falhas mexendo nas permissões gerais do computador.

Faça no seu exemplo

Numa cópia de teste com o servidor encerrado, faça uma cópia de entradas.jsonl. Acrescente uma linha com o texto isto não é JSON.

Mude uma coisa e preveja

Abra novamente a cópia de teste e preveja se a página deve fingir que a lista está normal.

Anote o que você acha que vai acontecer antes de salvar e recarregar.

Recarregue e confira

A página deve avisar que não conseguiu ler os registros e orientar conferir dados. Um envio não deve acrescentar dados a esse arquivo quebrado. Restaure a cópia do arquivo e confira a leitura.

Se não acontecer

Se a linha for ignorada silenciosamente, confira a função ler_visitas da referência. Se perder o arquivo, recupere a cópia em vez de criar visitas substitutas.

Mantenha o recorte de um exercício local

A etapa limita o arquivo de visitas a 512 KiB antes de aceitar outra escrita. Esse limite ajuda a evitar crescimento sem fim durante a prática. Ele não substitui limites de pedido HTTP, antispam ou um armazenamento próprio para muitos acessos simultâneos.

O servidor embutido é ferramenta de desenvolvimento. Não foi projetado para servir um mural público. O projeto também não inclui contas, moderação ou cópia automática dos dados.

Você conclui com um livro de visitas fictício funcional no computador. Se quiser transformar a ideia em serviço público, isso exige outro projeto com ambiente, política de dados e controles adequados. O próximo volume confere a entrega local que realmente construímos.

Faça no seu exemplo

Confira a constante ou condição de tamanho do arquivo antes da escrita. O valor completo está no index.php.

if (is_file($arquivo) && filesize($arquivo) > 512 * 1024) {
    throw new RuntimeException("O arquivo de prática atingiu o limite.");
}

Mude uma coisa e preveja

Numa cópia, reduza o limite para 10 bytes e tente outra visita após existir um registro.

Anote o que você acha que vai acontecer antes de salvar e recarregar.

Recarregue e confira

O programa deve recusar outra escrita e explicar o limite. Restaure 512 * 1024 depois do teste. A visita anterior permanece no arquivo.

Se não acontecer

Se confirmar sem gravar, confira a mensagem de sucesso somente após a escrita e o tratamento da exceção. Não apague o arquivo para fingir que o teste passou.

Trecho do resultado de referência do capítulo 19

A captura mostra um trecho da referência concluída. Abra e percorra o exemplo para conferir o restante. Experimentos temporários podem produzir outra aparência; restaure os valores indicados antes de comparar.

Atividades para fazer antes das respostas

Use uma nova cópia do exemplo. Tente aplicar o que aprendeu antes de consultar os comentários.

  1. Retire o token numa cópia e confira a recusa.

  2. Provoque uma linha JSON inválida somente numa cópia.

  3. Teste um limite pequeno de arquivo e restaure o original.

Respostas comentadas

Compare sua explicação e o que realmente aconteceu. Outra solução pode funcionar se cumprir o objetivo e preservar o acesso ao conteúdo.

  1. O POST sem token deve ser recusado antes de gravar. Depois restaure e recarregue o formulário.

  2. O programa informa a falha e conserva o arquivo para investigação. Recupere a cópia boa.

  3. A recusa não apaga a visita anterior. A mensagem de sucesso não aparece se não houve escrita.

Quando seguir

Você vê sucesso somente quando a tarefa foi concluída e consegue recuperar uma cópia após os testes. Se ainda precisa de ajuda, repita a experiência pequena antes de avançar.

Consulta opcional

Não é preciso ler os links externos para executar os arquivos fornecidos. Eles servem para consultar detalhes quando você quiser.

O arquivo completo

Referência do exemplo 19-falhas-protecao, reproduzida aqui para que você possa conferir sem sair da página.

public/estilos.css

* { box-sizing: border-box; }
body { margin: 0; background: #f4f7fb; color: #203246;
  font: 1.05rem/1.65 Arial, sans-serif; }
header, main, footer { max-width: 48rem; margin-inline: auto; padding: 1.5rem; }
header { padding-top: 2.5rem; }
h1 { font-size: clamp(1.8rem, 5vw, 2.7rem); line-height: 1.2; }
main { background: #fff; border-radius: .8rem; }
article { border-top: 1px solid #cad4df; padding-block: 1rem; }
a { color: #124e77; text-underline-offset: .2em; }
nav { display: flex; flex-wrap: wrap; gap: 1rem; }
label { display: block; font-weight: bold; }
input:not([type=hidden]), textarea { width: 100%; max-width: 34rem;
  border: 2px solid #718095; border-radius: .35rem; padding: .7rem;
  font: inherit; color: inherit; background: #fff; }
button { font: inherit; background: #124e77; color: white;
  border: 0; border-radius: .35rem; padding: .75rem 1.2rem; cursor: pointer; }
button:hover { background: #103b59; }
.mensagem { white-space: pre-wrap; overflow-wrap: anywhere; }
.erros { color: #8b142c; }
.ok { color: #175c30; font-weight: bold; }
:focus-visible { outline: 3px solid #b04d00; outline-offset: 3px; }
.pular { position: absolute; left: -10000px; }
.pular:focus { position: static; display: block; padding: 1rem; background: white; }
@media (max-width: 35rem) { header,main,footer { padding: 1rem; } }