Skip to content

Capítulo 5.4: Trabalhando com Tokens CSRF

Tempo de estudo: 30 minutos


1. O que é um ataque CSRF? O "Sequestro" da sua nave

Imagine que você está logado no painel de controle da sua frota espacial (space-api.test). Em uma aba vizinha, você abre um site inofensivo de gatinhos (evil-cats.com). Neste site, há um formulário oculto que envia automaticamente uma requisição para o seu site no endereço POST /api/planets/1/delete.

Como você já está autenticado em space-api.test, seu navegador gentilmente anexará todos os seus cookies a essa requisição. O servidor Laravel verá uma sessão válida e pensará que foi você quem decidiu desativar o planeta. O planeta será excluído sem o seu conhecimento.

Isso é CSRF (Cross-Site Request Forgery) — um ataque no qual um invasor força o navegador de um usuário autenticado a executar uma ação indesejada em um site confiável.

💡 Analogia espacial:

Você é o capitão da nave e tem um cartão-chave (sessão/cookie). O invasor não pode roubar seu cartão. Mas ele pode, por engano, fazer você passá-lo em um terminal de descarte de recursos enquanto você está distraído. O token CSRF é como um código PIN que precisa ser inserido junto com o cartão. O invasor não sabe o código PIN, e seu ataque falha.


2. Como o Laravel protege contra CSRF?

Por padrão, o Laravel protege todas as requisições web "inseguras" (POST, PUT, PATCH, DELETE) usando um token CSRF.

  1. Ao gerar uma página, o Laravel cria um token único e aleatório para a sessão do usuário.
  2. Este token é incorporado nos formulários HTML.
  3. Ao enviar o formulário, o token é enviado junto com a requisição.
  4. No servidor, o middleware VerifyCsrfToken compara o token da requisição com o token armazenado na sessão.
  5. Se os tokens não coincidirem, o Laravel interrompe a requisição com um erro 419 (Sessão Expirada/Página Expirada).

Importante: As rotas de API em routes/api.php não são protegidas por CSRF, pois elas presumem um mecanismo de autenticação diferente (por exemplo, tokens Sanctum), e não sessões baseadas em cookies. Nosso problema atual refere-se especificamente às rotas web e páginas que criamos em routes/web.php.


3. Usando o Token CSRF em Formulários HTML

Este é o cenário mais simples. O Laravel fornece uma diretiva Blade especial para isso.

Exemplo: Formulário para criar um planeta Vamos criar um formulário simples no arquivo resources/views/planets/create.blade.php:

<h2>Formulário para Lançamento de um Novo Planeta</h2>
<form action="/planets" method="POST">
    @csrf {{-- Aqui está a mágica! --}}

    <label for="name">Nome:</label>
    <input type="text" id="name" name="name" required>

    <label for="solar_system">Sistema Solar:</label>
    <input type="text" id="solar_system" name="solar_system" required>

    {{-- ... outros campos ... --}}

    <button type="submit">Lançar</button>
</form>

A diretiva @csrf gerará automaticamente um campo oculto no formulário:

<input type="hidden" name="_token" value="j2aK3dLf4gH5...token_único...">

Isso é suficiente para proteger formulários HTML padrão.


4. Usando o Token CSRF em Requisições AJAX/Fetch

No capítulo anterior, enviamos uma requisição DELETE usando JavaScript. Agora, o Laravel a bloqueará com um erro 419. Precisamos adicionar o token CSRF aos cabeçalhos da nossa requisição Fetch.

Passo 1: Tornar o token disponível para JavaScript

Adicione uma meta tag com o token na <head> do seu layout mestre resources/views/app.blade.php. Esta é uma prática padrão no Laravel.

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    {{-- Adicionamos o token CSRF na meta tag --}}
    <meta name="csrf-token" content="{{ csrf_token() }}">

    {{-- ... --}}
</head>

A função csrf_token() retorna o token atual.

Passo 2: Modificar o JavaScript para enviar o token

Agora, em nosso public/js/planets.js, podemos ler este token e adicioná-lo aos cabeçalhos de todas as requisições "inseguras".

// ... no arquivo public/js/planets.js ...

document.addEventListener('DOMContentLoaded', () => {
    // Obtenha o token da meta tag
    const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

    const deleteButtons = document.querySelectorAll('.delete-btn');

    deleteButtons.forEach(button => {
        button.addEventListener('click', async (event) => {
            // ... lógica de confirmação ...

            try {
                const response = await fetch(apiUrl, {
                    method: 'DELETE',
                    headers: {
                        'Accept': 'application/json',
                        'X-CSRF-TOKEN': csrfToken // <-- Adicionamos o token nos cabeçalhos!
                    }
                });

                // ... o resto da lógica de processamento da resposta ...
            } catch (error) {
                // ...
            }
        });
    });
});
  • O nome do cabeçalho X-CSRF-TOKEN é um padrão que o Laravel verifica por padrão.

Agora nossas requisições AJAX também estão protegidas. Tente excluir um planeta novamente — desta vez, a requisição será bem-sucedida.


Questionário para Fixação

1. Qual ataque o token CSRF previne?

2. Qual diretiva Blade adiciona um campo oculto com o token CSRF a um formulário?

3. O que acontece se uma requisição POST for enviada para uma rota web sem o token CSRF?

4. Qual cabeçalho HTTP padrão é usado para enviar o token CSRF em requisições AJAX?

5. Por que as rotas de API (`routes/api.php`) não usam proteção CSRF por padrão?


🚀 Resumo do Capítulo:

Você instalou um "sistema de identificação amigo ou inimigo" em sua nave espacial, protegendo-a de ataques CSRF. Você aprendeu a:

  • Compreender a natureza e o perigo dos ataques CSRF.
  • Proteger formulários HTML padrão usando a diretiva @csrf.
  • Passar o token CSRF para JavaScript via meta tag.
  • Incluir o token nos cabeçalhos de requisições AJAX/Fetch para sua execução bem-sucedida.

Suas interfaces web agora não são apenas interativas, mas também seguras. No próximo capítulo, concluiremos a criação de nossa interface web, abordando como organizar corretamente o roteamento para páginas web.