Pular para o conteúdo

Segurança e operações no pacote Laravel do NextPDF

O pacote define cabeçalhos de resposta fixos para Portable Document Format (PDF), sanitiza nomes de arquivos de download, valida os caminhos de saída da fila no worker e roteia as chamadas Hypertext Transfer Protocol (HTTP) para a autoridade de carimbo de tempo por meio de um cliente atento a falsificação de requisições. Esta página explica o modelo de ameaças e a configuração de implantação exigida por cada controle.

Terminal window
composer require nextpdf/laravel
php artisan vendor:publish --tag=nextpdf-config

O pacote adapta um mecanismo de PDF a um framework web. A requisição HTTP e o transporte da fila definem o limite de confiança. Esses controles abrangem o tratamento de respostas, os payloads de jobs desserializados e o HTTP de saída para uma autoridade de carimbo de tempo.

AtivoAmeaçaControle neste pacoteConfiguração de implantação exigida
Resposta HTTP de PDFSniffing de content-type, clickjacking, indexaçãoConjunto de cabeçalhos fixos em cada fábrica PdfResponse do pacoteNenhuma; os cabeçalhos não são configuráveis
Nome do arquivo de downloadInjeção de cabeçalho, path traversal em Content-DispositionO sanitizador de nomes de arquivo remove separadores, caracteres de controle e bytes nulosNenhuma; o sanitizador sempre é executado
Caminho de saída do job de filaEscrita arbitrária de arquivo via payload serializado adulteradoCaminho validado em handle() no workerRoteie a saída para um caminho de armazenamento controlado
HTTP de saída para a autoridade de carimbo de tempo (TSA)Server-side request forgery, adulteração de texto puroCliente HTTP atento a falsificação de requisições; HTTPS imposto, exceto quando relaxado explicitamenteMantenha tsa.allow_insecure_http = false; fixe (pin) o Subject Public Key Info (SPKI)
Estado compartilhado do workerVazamento de estado entre requisições em workers de longa duraçãoRegistro de fontes bloqueado; cache de imagens limitado; documento vinculado à fábricaDefina preload_fonts; limite a memória no container

Cada fábrica de PdfResponse define um conjunto de cabeçalhos fixos:

  • Cache-Control: private, max-age=0, must-revalidate
  • Pragma: public
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Content-Security-Policy: default-src 'none'
  • X-Robots-Tag: noindex, nofollow
  • Referrer-Policy: no-referrer

Esses valores são constantes em PdfResponse. Eles não são configuráveis. A suíte de testes do pacote verifica cada cabeçalho em cada método de fábrica, incluindo as variantes em streaming.

O nome do arquivo de download passa por um sanitizador antes de chegar ao cabeçalho Content-Disposition. O sanitizador remove separadores de caminho, caracteres de controle e bytes nulos e emite um parâmetro filename*= do Request for Comments (RFC) 5987 para nomes não ASCII. Um nome de arquivo vazio se torna document.pdf.

GeneratePdfJob serializa uma closure no transporte da fila. O worker valida o caminho de saída dentro de handle(), não no momento do dispatch. A validação rejeita:

  • bytes nulos no caminho,
  • esquemas de stream-wrapper (por exemplo php://),
  • .. segmentos de path traversal,
  • qualquer caminho que não termine em .pdf (sem distinção de maiúsculas e minúsculas).

Cada rejeição lança InvalidArgumentException. A validação é executada quando o worker consome o job. Um payload serializado em um transporte Redis ou de banco de dados pode ser alterado antes de ser lido pelo worker. Roteie o caminho de saída para um diretório de armazenamento controlado; não o derive de uma entrada de requisição não validada.

HTTP de saída para uma autoridade de carimbo de tempo

Seção intitulada “HTTP de saída para uma autoridade de carimbo de tempo”

Quando uma autoridade de carimbo de tempo está configurada, o pacote vincula um PHP Standard Recommendation (PSR)-18 Psr\Http\Client\ClientInterface. Um cliente PSR-18 envia uma requisição PSR-7 e retorna uma resposta PSR-7 (PSR-18 §2). O cliente vinculado encapsula um cliente baseado em curl com uma camada atenta a falsificação de requisições. Ele impõe HTTPS, exceto quando tsa.allow_insecure_http for explicitamente true.

A autoridade de carimbo de tempo é um recurso do nível Premium. O pacote Core documentado aqui vincula o cliente HTTP e a integração do cliente de carimbo de tempo; a assinatura em si requer nextpdf/premium. Esta página não documenta o comportamento de baseline das PDF Advanced Electronic Signatures (PAdES) além de B-B; baselines superiores ficam fora do escopo.

Orientação operacional para a autoridade de carimbo de tempo:

  1. Mantenha tsa.allow_insecure_http definido como false em produção.
  2. Defina tsa.pinned_public_keys com os hashes SHA-256 SPKI em base64 do certificado da autoridade de carimbo de tempo (forma do RFC 7469).
  3. Mantenha tsa.warn_on_key_rotation definido como true para que um SPKI alterado seja registrado em log antes da expiração do certificado fixado.
  4. Obtenha tsa.url apenas de configuração confiável. Se um operador puder defini-la a partir de uma área administrativa, aplique um firewall de egresso ou uma política de DNS para reduzir a exposição à falsificação de requisições.

Use Psr\Log\LoggerInterface para diagnósticos. Passe contexto estruturado, não strings interpoladas. O PSR-3 deixa o escape de placeholders a cargo da implementação do logger e instrui os chamadores a não escapar previamente os valores de contexto (PSR-3 §1.2). Registre em log a classe da exceção, não a mensagem nem o trace, para reduzir detalhes internos nos logs.

resource: config/nextpdf.php (tsa hardening) + src/Laravel/NextPdfServiceProvider.php
<?php
declare(strict_types=1);
// .env — production timestamp-authority hardening
// NEXTPDF_TSA_URL=https://tsa.example.test
// NEXTPDF_TSA_ALLOW_INSECURE_HTTP=false
// NEXTPDF_TSA_WARN_ROTATION=true
return [
'tsa' => [
'url' => env('NEXTPDF_TSA_URL'),
'allow_insecure_http' => env('NEXTPDF_TSA_ALLOW_INSECURE_HTTP', false),
'warn_on_key_rotation' => env('NEXTPDF_TSA_WARN_ROTATION', true),
'pinned_public_keys' => [
// base64 SHA-256 SPKI hashes of the TSA certificate
],
],
];
  • O conjunto de cabeçalhos de resposta é fixo. Aplicações que precisam de uma Content Security Policy (CSP) diferente devem pós-processar a resposta depois que a fábrica retorná-la.
  • A validação de caminho é executada no worker. Um caminho inválido passa por dispatch() e falha apenas quando o job é executado.
  • tsa.allow_insecure_http = true remove a imposição de HTTPS e enfraquece a confiança no carimbo de tempo. Restrinja-o ao desenvolvimento local.
  • O registro de fontes é bloqueado após o aquecimento; o pacote rejeita, por design, tentativas de registrar uma fonte em tempo de execução em um worker de longa duração.

Os controles de segurança usam operações de string e array em tempo constante e não adicionam custo mensurável por requisição. A análise (parsing) de fontes no primeiro uso é o custo operacional dominante; pré-carregue as fontes na inicialização do worker para evitar a latência da primeira requisição.

Esta página é a referência de modelo de ameaças do pacote. O código-fonte impõe esses controles, e a suíte de testes os verifica. A tabela de modelo de ameaças e as etapas da autoridade de carimbo de tempo destacam a configuração de implantação que o operador deve fornecer.

AfirmaçãoFonteCláusulareference_id
O cliente PSR-18 envia uma requisição PSR-7 e retorna uma resposta PSR-7PSR-18 HTTP Client§2
O chamador passa contexto de log estruturado sem escapePSR-3 Logger§1.2

O pinning de SPKI do RFC 7469 nomeia a forma usada pela chave de configuração tsa.pinned_public_keys. O pacote consome os valores de pin fornecidos pelo operador e não implementa o próprio RFC.

A assinatura PAdES B-B e a integração com a autoridade de carimbo de tempo requerem nextpdf/premium. Esse recurso opcional de Enterprise não exige nenhuma alteração de código no pacote Core documentado aqui. Consulte https://nextpdf.dev/get-license/?intent=laravel-signing.

  • /integrations/laravel/configuration/ — cada chave de TSA, assinatura e fila
  • /integrations/laravel/production-usage/ — padrões de injeção de dependência (DI) e de tratamento de erros
  • /integrations/laravel/troubleshooting/ — por que as verificações de caminho rejeitam a entrada
  • /integrations/laravel/boot-and-discovery/ — tempos de vida de binding em workers de longa duração