░░░ ░░░ ▀▄
▄ ▀▀▄ ░░░ ■ ▒ ░░░
▄ ▒ ▄▄ ▒ ▄▀ ▒ ▄ ▀▄▄
▄▄ ▄ ▄█ █ ▄▀ ▄█▀▀▀ ▀▀ ▄
▀▄▀█▄▄▄▄▄▄██▀ ▀▄▀███▄▄▄ ▄▄█▄▄▄ █▄▄▄ ▄▀▄▄▀ ▄▄▄▄▄ ▀▄▀ ▒ ▄▄▄▄▀
▀▀▄▄ █▄▀▀▄ ▄▀ ▄▄ ▀▄ ▄▀▄▀▀▀▀▄ ▀▄▀▀▄██▀█ █▀▌ ▐▄ ▄█▀▀▀▀▄▄
█▀ ▄▄ ▒ ░░ █ █▀ ▄▄▀ ■▀ ▀▄ ▒ ▀▄▀▄▀ █ ▀▄ ██▄ ▓ ▄█ ▄▀▀ ██▀▄
▄ ▒ ██ ▄ ▓▓ ▒ █▄ ██ █ ██ █▄ ▀ ▀▒ ██ ▐▌██ █ ▒ ▄█ ▓ ██ ▄
▀ ▒ ▀█ ▄ ██ █ ░░ ▒ ▓▓ █▄ ██ ▓▓ █ ░█ ▄ ▄ ██ ▒ ▀▄ ▀
▀▄ ▓ ▓█ ▀▀█▀▄▄▄▄▀ ▒▒ ░░▀ ▒▒ ░ ▒▒ ░░ ▐▌ ██ █ ▒ ▀▀ ▄▄▄▄▄ ▄ ▄▀
░█▀ █ █▓ ▒ ▓ ▀▀▄ ▄▄▄▄▄▓▓▀▄█ ░▓ ▒ ▓░▐ █▓ ▓ ▌▓ ░▒░ ▄▀██ ▄▄▄▀▀ ▀█░
▄╬▄ ▓ ▓▒ █ █▓ ▀▒▄ ▀░▀▀ █▓ ▓█ █ █▓ ▓█ ▐▌ ▓▓ ▓▓█ ▄▀█▓ ▄█ ▒ ▄╬▄
, ▌█▌ , ▓ ░▀ ░ ▓█ ▀ ▀░░▄▄ ░▓ █▓ █ ▒█▐ ▌▓ █ ▌▒ █▓█ ░█ ▒ █▓ █ , ▐█▐ ,
▄ ▄ ═▌█▌═ ▄ ▄ ░░▀▀▀▀ ██ ░ █▒ ▀▄ ▀▓░░░▄▄ █░ ▓░ ▓ ▀█▐ █░▐▌ ▌█ ░░░ ▀░ ▓ ░█ ▀ ▄ ▄ ═▐█▐═ ▄ ▄
▀▀█▓▄▓█▓█▀▀ ▀▄ ▀░░░░ ▒ ▀▄▓▀▒ █ █▓▄▀▀▀░▄▀░░█ █ ░░█▀▀▓█ █ ░▓█ ▀▄▓█ ▓ ░ ▄▄ ▀▀█▓█▓▄▓█▀▀
▀▓▄% ▀ ▀▄ ▀░░▀ ▄ ▄▀ ▄▀ █▀ ▀█ ▓ ░▀▄▄ ▀ ▄▄▀ ▒▀ █▄ ▀█ █ ▓▄▀ ▀ %▄▓▀
▒ •▀▒ ▀▄ ▄▄▀ ▒ ▄▀ ▀▄ ▓ ▄▀ ▀ ▄▄ ▀ ▄▀ ■▄ ░ ▀ ▒▀• ▒
▄▀▀▀▀▄▄▄█▄▄▒ ■ ░ ▒ ▀█ ■ ▄▀ ▀▄░ ▒▄▄█▄▄▄▀▀▀▀▄
░ ░ •▀ ▀ ▄▄▀ ▒ ▀ ▀• ░ ░
█ ▒▄▀ [ Arte por L.Ayres ] ▀▄▒ █
█ ▒ ▒ █
█ █ █ █
█ ▒ xxx xxx xxx xxx ▒ █
█▒▒ x x x x x xx x ▒▒█
█ ▒ x x x xx xx x ▒ █
█ █ x x x xx x x x █ █
█ ▒ xxx x x x xx x x x ▒ █
█▒▒ xx x x x x xx x x x ▒▒█
█ ▒ xx xx x x x xx x x x ▒ █
█ █ xx xx x x x xx x x x o efeito dominó causado █ █
█ ▒ xx xx x x x xx x x x pelas frameworks fullstack ▒ █
█▒▒ xx xx x x x xx x x x ▒▒█
█ ▒ xx x x x x xx x x x ▒ █
█ █ xx xx x x x xx x x x █ █
█▒▒ ¡ xx x x x x xx x x x ▒▒█
█ ▒,█▄ xx x x x x xx x x x ▒ █
█ █▀▀° xx xx x x x xx x x x █ █
█▒▒ xx xx x x x xx x x x ▒▒█
█ ▒ xxxx xxxx xxx xxxx xxx ▒ █
█ █ █ █
█ ▒ Como Next.js e Remix estão fazendo uma ▒ █
█▒▒ geração esquecer que existe diferença ▒▒█
█ ▒ entre client-side e server-side ▒ █
█ █ █ █
█ ▒ -- [ /apêndice ▒ █
█▒▒ -- #0. intro; ▒▒█
█ ▒ -- #1. perigos à espreita; ▒ █
█ █ -- #2. e agora? o que fazer?; █ █
█ ▒ -- #3. conclusão; ▒ █
█▒▒ -- #4. agradecimentos e referências; ¡ ▒▒█
█ ▒ ▄█,▒ █
█ █ ================================================================================ °▀▀█ █
█▒▒ 0. intro ▒▒█
█ ▒ ================================================================================ ▒ █
█ █ █ █
█ ▒ O MITRE documenta a CWE-602: Client-Side Enforcement of Server-Side Security [1] como: ▒ █
█▒▒ ▒▒█
█ ▒ "Quando o servidor depende de validações ou controles no cliente, ▒ █
█ █ um atacante pode modificar o comportamento do cliente para contornar █ █
█ ▒ essas proteções." ▒ █
█▒▒ ▒▒█
█ ▒ esse padrão é mais comum que parece em aplicações fullstack onde desenvolvedores ▒ █
█ █ compartilham código entre client/server sem considerar que dados █ █
█ ▒ sensíveis vão para o bundle do JavaScript. ▒ █
█▒▒ ¡ ▒▒█
█ ▒,█▄ dentre as diversas "ondas" e hypes do mercado, surgiu a febre das ▒ █
█ █▀▀° frameworks fullstacks, afinal, nada mais atrativo que uma tecnologia que te dá o poder de █ █
█▒▒ fazer aplicações web fácil fácil, trazendo praticidade, velocidade, features, economia de ▒▒█
█ ▒ infra, dinheiro...O que poderia dar errado? ▒ █
█ █ Eu te conto: tudo. █ █
█ ▒ ▒ █
█▒▒ Já adianto que não, não tô dizendo que o problema são necessariamente essas frameworks, que ▒▒█
█ ▒ elas são horríveis e inúteis, o que eu quero falar aqui é sobre o potencial de destruição ▒ █
█ █ que elas tem, se usadas da forma errada. █ █
█ ▒ Pensa no seguinte: Você quer construir uma casa, pra isso normalmente e naturalmente você ▒ █
█▒▒ quer economizar dinheiro construindo ela, então, você fica sabendo que, invés de contratar ▒▒█
█ ▒ dois pedreiros bons, que fazem todo o alicerce manualmente, você pode simplesmente contratar ▒ █
█ █ um pedreiro que tem uma máquina que facilita a construção desse alicerce e que pode █ █
█ ▒ potencialmente deixar ele melhor que alicerces básicos...Você geralmente escolheria a ▒ █
█▒▒ segunda opção, correto? Afinal, faz sentido, a máquina pode realmente ser uma boa adição, ¡ ▒▒█
█ ▒ fazer seu alicerce ser muito bom... ▄█,▒ █
█ █ O detalhe que você não sabia era o seguinte: esse pedreiro não sabe mexer muito bem na °▀▀█ █
█▒▒ máquina, ele consegue fazer o alicerce mas sem saber que certas configurações da máquina ▒▒█
█ ▒ criam pontos de frágeis na estrutura. ▒ █
█ █ O primeiro problema pequeno se torna uma rachadura, que se vira um buraco, que derruba uma █ █
█ ▒ parede, que compromete a estrutura inteira. ▒ █
█▒▒ Criando um efeito dominó. ▒▒█
█ ▒ ▒ █
█ █ Tanto nessa alegoria quanto nos cenários reais, uma coisa é presente: profissionais que não █ █
█ ▒ se importam com a real arquitetura do que estão fazendo. ▒ █
█▒▒ E então, vai crescendo uma tendência perigosa de desenvolvedores tratando aplicacoes ▒▒█
█ ▒ inteiras como se fossem apenas uma extensão do frontend. ▒ █
█ █ O resultado? Apps inseguros mascarados de "arquitetura moderna". █ █
█ ▒ ▒ █
█▒▒ ¡ A questão não é que esses frameworks sejam ruins. Eles podem ser ótimos! O ▒▒█
█ ▒,█▄ problema é o a mentalidade que se desenvolveu em torno deles. ▒ █
█ █▀▀° Quando voce pode escrever tanto código de servidor quanto de cliente no █ █
█▒▒ mesmo escopo/projeto, é fácil esquecer que algumas coisas VÃO rodar no navegador do usuário. ▒▒█
█ ▒ ▒ █
█ █ ================================================================================ █ █
█ ▒ 1. perigos à espreita ▒ █
█▒▒ ================================================================================ ▒▒█
█ ▒ ▒ █
█ █ A fronteira entre o que é executado no servidor e o que roda no navegador do usuário, antes █ █
█ ▒ muito bem definida, hoje se encontra perigosamente turva. Frameworks fullstack, ao unificar ▒ █
█▒▒ o desenvolvimento em um único ecossistema, criaram uma falsa sensação de que tudo é "a mesma ▒▒█
█ ▒ coisa". Essa abstração, embora poderosa, esconde armadilhas sutis que podem levar a falhas ▒ █
█ █ de segurança catastróficas, muitas vezes interligadas. █ █
█ ▒ ▒ █
█▒▒ O perigo mais imediato dessa fusão de ambientes é a exposição de informações sensíveis. ¡ ▒▒█
█ ▒ Mesmo que a regra de ouro "não coloque segredos no frontend" seja conhecida, a realidade de ▄█,▒ █
█ █ ter server components e client components lado a lado torna o deslize quase inevitável, °▀▀█ █
█▒▒ dessa forma, uma variável de ambiente ou uma chave de API, que deveria viver e morrer no ▒▒█
█ ▒ servidor, pode acidentalmente acabar no bundle enviado ao navegador, ficando completamente ▒ █
█ █ exposta para qualquer um inspecionar. █ █
█ ▒ ▒ █
█▒▒ Essa mentalidade de "código unificado" gera uma confiança excessiva no que acontece no ▒▒█
█ ▒ cliente, levando à um dos erros mais críticos: a validação de dados apenas no lado do ▒ █
█ █ navegador. █ █
█ ▒ A lógica é tentadora: ▒ █
█▒▒ "Já validei no React Hook Form, não preciso validar no servidor!" ▒▒█
█ ▒ - famosas últimas palavras ▒ █
█ █ Essa é uma aposta perigosa, já que ignora que qualquer proteção no cliente é, na melhor das █ █
█ ▒ hipóteses, uma sugestão. ▒ █
█▒▒ ¡ E o resto é história conhecida, desabilitar o JavaScript, manipular a requisição com ▒▒█
█ ▒,█▄ ferramentas como o Postman ou curl, enviar dados manipulados diretamente pra API, ▒ █
█ █▀▀° contornando todas as regras que só existiam na interface. █ █
█▒▒ ▒▒█
█ ▒ Como consequência direta dessa validação falha, vem a manipulação dos estados que existem na ▒ █
█ █ aplicação...Tratar useState ou useContext como a "fonte da verdade" para lógica de █ █
█ ▒ negócio, descontos em um carrinho de compras, perfis de autorização ou qualquer lógica ▒ █
█▒▒ crítica é um prato cheio esperando para ser devorado. ▒▒█
█ ▒ ▒ █
█ █ Pra sair do teórico e ir mais pro prático, podemos ver abaixo o exemplo de um painel de █ █
█ ▒ admin que só é renderizado se user.role === 'admin', que quando tem seu estado ▒ █
█▒▒ manipulado, pode ser facilmente exibido. Da mesma forma, que um desconto de VIP pode ser ▒▒█
█ ▒ ativado alterando uma flag no estado. ▒ █
█ █ Se o servidor confia nos dados que recebe do cliente, como o preço final de um carrinho ou o █ █
█ ▒ nível de permissão do usuário, ele está, na prática, permitindo que o cliente dite as regras ▒ █
█▒▒ do negócio. ¡ ▒▒█
█ ▒ ▄█,▒ █
█ █ Exemplo prático: °▀▀█ █
█▒▒ ▒▒█
█ ▒ // src/app/page.tsx ▒ █
█ █ const ShoppingCart = () => { █ █
█ ▒ const [user, setUser] = useState({ ▒ █
█▒▒ role: 'customer', ▒▒█
█ ▒ vip_level: 1, ▒ █
█ █ discount: 0 █ █
█ ▒ }) ▒ █
█▒▒ const [cart, setCart] = useState([]) ▒▒█
█ ▒ const [total, setTotal] = useState(0) ▒ █
█ █ █ █
█ ▒ const addToCart = (product) => { ▒ █
█▒▒ ¡ // erro #1: Cálculo de desconto no cliente ▒▒█
█ ▒,█▄ const finalPrice = user.vip_level >= 5 ▒ █
█ █▀▀° ? product.price * 0.8 // 20% desconto VIP █ █
█▒▒ : product.price ▒▒█
█ ▒ ▒ █
█ █ setCart([...cart, { ...product, finalPrice }]) █ █
█ ▒ setTotal(total + finalPrice) ▒ █
█▒▒ } ▒▒█
█ ▒ ▒ █
█ █ return ( █ █
█ ▒ <div> ▒ █
█▒▒ {/* erro #2: Renderização condicional baseada em estado */} ▒▒█
█ ▒ {user.role === 'admin' && <AdminPanel />} ▒ █
█ █ <ProductList onAdd={addToCart} /> █ █
█ ▒ <div>Total: ${total}</div> ▒ █
█▒▒ </div> ¡ ▒▒█
█ ▒ ) ▄█,▒ █
█ █ } °▀▀█ █
█▒▒ ▒▒█
█ ▒ ▒ █
█ █ O que qualquer um pode fazer: █ █
█ ▒ ▒ █
█▒▒ 1. DevTools → Components → ShoppingCart ▒▒█
█ ▒ 2. Encontra o hook state do user ▒ █
█ █ 3. Modifica: user.role = 'admin' ou user.role = 'vip' █ █
█ ▒ 4. Resultado: Painel admin aparece + desconto VIP ativado ▒ █
█▒▒ ▒▒█
█ ▒ ou até mesmo direto no console: ▒ █
█ █ █ █
█ ▒ // Muda role para admin ▒ █
█▒▒ ¡ $r.setState({ user: { role: 'admin', discount: 50 } }) ▒▒█
█ ▒,█▄ ▒ █
█ █▀▀° // Ou manipula o total diretamente █ █
█▒▒ $r.setState({ total: 1.00 }) ▒▒█
█ ▒ ▒ █
█ █ █ █
█ ▒ daí o servidor faz exatamente isso: ▒ █
█▒▒ ▒▒█
█ ▒ // src/app/api/checkout/route.ts ▒ █
█ █ export async function POST(request: NextRequest) { █ █
█ ▒ try { ▒ █
█▒▒ const { cart, total, discount, userRole, vipLevel } = await request.json() ▒▒█
█ ▒ ▒ █
█ █ // Confia no total calculado no cliente █ █
█ ▒ const finalTotal = total * (1 - discount) ▒ █
█▒▒ ¡ ▒▒█
█ ▒ // Confia no role enviado pelo cliente ▄█,▒ █
█ █ if (userRole === 'admin') { °▀▀█ █
█▒▒ finalTotal = 0.01 // Admin paga quase nada! ▒▒█
█ ▒ } ▒ █
█ █ █ █
█ ▒ // Confia no VIP level do cliente ▒ █
█▒▒ if (vipLevel >= 10) { ▒▒█
█ ▒ finalTotal = finalTotal * 0.1 // 90% desconto! ▒ █
█ █ } █ █
█ ▒ ▒ █
█▒▒ // Processa pagamento com valores manipulados ▒▒█
█ ▒ await processPayment(finalTotal) ▒ █
█ █ █ █
█ ▒ return NextResponse.json({ ▒ █
█▒▒ ¡ success: true, ▒▒█
█ ▒,█▄ total: finalTotal ▒ █
█ █▀▀° }) █ █
█▒▒ } catch (error) { ▒▒█
█ ▒ return NextResponse.json({ error: error.message }, { status: 500 }) ▒ █
█ █ } █ █
█ ▒ } ▒ █
█▒▒ ▒▒█
█ ▒ +------------------+ ▒ █
█ █ | NAVEGADOR | █ █
█ ▒ | | ▒ █
█▒▒ | role: 'admin' | usuário muda ▒▒█
█ ▒ | total: $1.00 | <-- o estado React ▒ █
█ █ | | █ █
█ ▒ | POST /checkout | envia dados ▒ █
█▒▒ +------------------+ manipulados ¡ ▒▒█
█ ▒ | ▄█,▒ █
█ █ v °▀▀█ █
█▒▒ +------------------+ ▒▒█
█ ▒ | SERVIDOR | ▒ █
█ █ | | █ █
█ ▒ | Cobra $1.00 | <-- servidor processa sem questionar ▒ █
█▒▒ | (deveria ser | ▒▒█
█ ▒ | $150.00) | ▒ █
█ █ +------------------+ █ █
█ ▒ ▒ █
█▒▒ ▒▒█
█ ▒ As implicações disso acabam não se limitando só ao estado do React, já que esse princípio se ▒ █
█ █ estende a qualquer informação controlada pelo cliente: feature flags que habilitam █ █
█ ▒ funcionalidades, dados em localStorage/sessionStorage que controlam o estado da interface, e ▒ █
█▒▒ ¡ até mesmo parâmetros na URL. ▒▒█
█ ▒,█▄ Tudo pode ser manipulado. ▒ █
█ █▀▀° !! Sempre precisamos de mais de uma layer de verificação. !! █ █
█▒▒ ▒▒█
█ ▒ ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ▒ █
█ █ █ █
█ ▒ Além desses problemas fundamentais, existem nuâncias específicas dependendo do framework ▒ █
█▒▒ usado. Cada um introduz funcionalidades específicas que, quando não implementadas do jeito ▒▒█
█ ▒ certo, podem piorar ainda mais a situação. ▒ █
█ █ █ █
█ ▒ ---/ Next.js ▒ █
█▒▒ ▒▒█
█ ▒ O Next.js oferece diferentes estratégias de renderização [2], cada uma com ▒ █
█ █ suas próprias implicações de segurança: █ █
█ ▒ ▒ █
█▒▒ - SSR (getServerSideProps): Dados são buscados no servidor a cada request, podendo expor ¡ ▒▒█
█ ▒ dados sensiveis se não validados corretamente. ▄█,▒ █
█ █ °▀▀█ █
█▒▒ - SSG (getStaticProps): Dados são gerados no build time, podendo "congelar" dados ▒▒█
█ ▒ sensíveis no bundle estático. ▒ █
█ █ █ █
█ ▒ - ISR (Incremental Static Regeneration): Combina SSG com revalidação, podendo acarretar ▒ █
█▒▒ em dados desatualizados ou vazamento durante revalidação. ▒▒█
█ ▒ ▒ █
█ █ ---/ Remix.js █ █
█ ▒ ▒ █
█▒▒ - Loaders sem autorização: Loaders rodam no servidor para buscar dados [3], mas ▒▒█
█ ▒ desenvolvedores esquecem de validar SE o usuário atual pode acessar aqueles dados. O loader ▒ █
█ █ executa e retorna tudo, expondo informações no HTML inicial mesmo que o usuário não tenha █ █
█ ▒ permissão: ▒ █
█▒▒ ¡ ▒▒█
█ ▒,█▄ // routes/users.$userId.tsx ▒ █
█ █▀▀° export async function loader({ params }) { █ █
█▒▒ const userId = params.userId ▒▒█
█ ▒ // Qualquer um pode acessar /users/123 e ver os dados ▒ █
█ █ const user = await getUserById(userId) █ █
█ ▒ return json({ user }) // <- Dados vão para o HTML ▒ █
█▒▒ } ▒▒█
█ ▒ ▒ █
█ █ █ █
█ ▒ - Actions e CSRF: Remix tem proteção CSRF quando você usa o componente <Form>, mas se ▒ █
█▒▒ você faz fetch direto para actions ou permite CORS aberto, essa proteção pode ser ▒▒█
█ ▒ contornada. É importante validar a origem das requisições em actions críticas. ▒ █
█ █ █ █
█ ▒ - Client-side Data Hydration: Os dados dos loaders são automaticamente serializados e ▒ █
█▒▒ injetados no HTML como window.__remixContext [4]. Isso cria três riscos: (1) **Data ¡ ▒▒█
█ ▒ Exposure** - qualquer dado sensível retornado pelo loader fica visível no código-fonte da ▄█,▒ █
█ █ página para qualquer usuário; (2) State Tampering - esses dados podem ser manipulados °▀▀█ █
█▒▒ via DevTools/console antes da hidratação (remixData.user.role = 'admin'), e se componentes ▒▒█
█ ▒ confiam neles para renderização condicional ou cálculos, a app fica comprometida; (3) ▒ █
█ █ Hydration Mismatch - inconsistências entre estado servidor/cliente podem ser exploradas █ █
█ ▒ via timing attacks ou manipulação de URL parameters. ▒ █
█▒▒ ▒▒█
█ ▒ - Form Data Tampering: Campos de formulário (incluindo hidden inputs) podem ser ▒ █
█ █ manipulados via DevTools antes do submit. Desenvolvedores confiam em valores como preços, █ █
█ ▒ roles ou flags admin que vêm do cliente (formData.get('price')), sem validar no servidor ▒ █
█▒▒ se aquele usuário pode realmente definir aqueles valores. ▒▒█
█ ▒ ▒ █
█ █ ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── █ █
█ ▒ ▒ █
█▒▒ ¡ QUANDO CONEXÕES PERSISTENTES E CACHE AMPLIFICAM O CAOS ▒▒█
█ ▒,█▄ ▒ █
█ █▀▀° Os problemas já são graves em requisições HTTP normais, mas dois fatores podem piorar ainda █ █
█▒▒ mais o que já tá ruim: ▒▒█
█ ▒ ▒ █
█ █ WebSockets: O Amplificador em Tempo Real █ █
█ ▒ ▒ █
█▒▒ Conexões persistentes criam uma falsa sensação de segurança. Desenvolvedores pensam: "Estou ▒▒█
█ ▒ conectado ao servidor, logo está seguro". Na prática, WebSockets podem se tornar canais de ▒ █
█ █ transmissão contínua de dados manipulados: █ █
█ ▒ ▒ █
█▒▒ // Cliente envia estado React como "autenticação" ▒▒█
█ ▒ websocket.send(JSON.stringify({ ▒ █
█ █ type: 'auth', █ █
█ ▒ userRole: userRole, // <- Manipulável via DevTools! ▒ █
█▒▒ userId: localStorage.getItem('userId') ¡ ▒▒█
█ ▒ })) ▄█,▒ █
█ █ °▀▀█ █
█▒▒ // Servidor confia cegamente ▒▒█
█ ▒ if (message.userRole === 'admin') { ▒ █
█ █ broadcastToAll(message.data) // <- Executa ação admin! █ █
█ ▒ } ▒ █
█▒▒ ▒▒█
█ ▒ ▒ █
█ █ O problema? Se o servidor não valida CADA mensagem contra sua própria fonte de verdade █ █
█ ▒ (banco de dados, sessão real), o WebSocket vira apenas um megafone para dados maliciosos. Em ▒ █
█▒▒ frameworks fullstack, onde estado cliente e servidor parecem "conectados", esse erro é ainda ▒▒█
█ ▒ mais comum. ▒ █
█ █ █ █
█ ▒ Cache Poisoning: O Propagador Silencioso ▒ █
█▒▒ ¡ ▒▒█
█ ▒,█▄ Você corrige tudo no servidor. Validações perfeitas, sanitização robusta. Mas tem um CDN na ▒ █
█ █▀▀° frente. Aí vem o golpe: █ █
█▒▒ ▒▒█
█ ▒ // API reflete header sem sanitizar ▒ █
█ █ export default function handler(req, res) { █ █
█ ▒ const host = req.headers['x-forwarded-host'] || req.headers.host ▒ █
█▒▒ return res.json({ apiUrl: `https://${host}/api` }) ▒▒█
█ ▒ } ▒ █
█ █ █ █
█ ▒ ▒ █
█▒▒ Um atacante envia X-Forwarded-Host: evil.com. O servidor processa e retorna `apiUrl: ▒▒█
█ ▒ "https://evil.com/api"`. O cache armazena essa resposta envenenada. Agora TODOS os ▒ █
█ █ usuários legítimos recebem a URL maliciosa do cache, sem nunca tocar o backend "seguro". █ █
█ ▒ Seus dados vão direto para o atacante. [5] ▒ █
█▒▒ ¡ ▒▒█
█ ▒ O framework facilita tanto que você não percebe estar refletindo dados do cliente na ▄█,▒ █
█ █ resposta. O cache propaga esse erro para toda sua base de usuários. °▀▀█ █
█▒▒ ▒▒█
█ ▒ ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ▒ █
█ █ █ █
█ ▒ -- /O caos toma conta ▒ █
█▒▒ ▒▒█
█ ▒ E é assim que cada "pequena" decisão errada detona tudo: ▒ █
█ █ █ █
█ ▒ 1. Confiança no estado React pra controlar permissões; ▒ █
█▒▒ 2. Compartilhamento de código entre client/server sem separação clara; ▒▒█
█ ▒ 3. Secrets vazando para o bundle JavaScript; ▒ █
█ █ 4. APIs confiando em dados que o cliente envia; █ █
█ ▒ 5. + fatores bônus específicos de cada aplicação; ▒ █
█▒▒ ¡ ▒▒█
█ ▒,█▄ Cada peça derruba a próxima. A questão não é "se", mas "quando" — a menos que você entenda ▒ █
█ █▀▀° exatamente onde cada peça roda e quem controla cada dado. █ █
█▒▒ ▒▒█
█ ▒ ================================================================================ ▒ █
█ █ 2. e agora? o que fazer? █ █
█ ▒ ================================================================================ ▒ █
█▒▒ ▒▒█
█ ▒ Agora que vimos como tudo pode desmoronar, a questão é: como construir diferente? A resposta ▒ █
█ █ não está em uma única ferramenta ou técnica mágica, mas em entender e aplicar princípios █ █
█ ▒ fundamentais de forma consistente [6]. ▒ █
█▒▒ ▒▒█
█ ▒ ── /princípios fundamentais ▒ █
█ █ █ █
█ ▒ - **1. Servidor como fonte de verdade: ▒ █
█▒▒ ¡ ▒▒█
█ ▒ A regra de ouro: tudo que é crítico acontece no servidor. Validação, autorização, ▄█,▒ █
█ █ cálculos de preços, regras de negócio - tudo. O cliente? Só UI e feedback visual. °▀▀█ █
█▒▒ ▒▒█
█ ▒ → Separação clara: Server components para lógica sensível. Client components para ▒ █
█ █ interatividade. █ █
█ ▒ → Validação em camadas: Client-side para UX ("email inválido"). Server-side para ▒ █
█▒▒ segurança (única verdade). ▒▒█
█ ▒ → Autorização sempre: Renderização condicional por roles? Servidor decide. URL com ▒ █
█ █ /admin/999? Servidor valida acesso. █ █
█ ▒ ▒ █
█▒▒ Se envolve dinheiro, permissões ou dados sensíveis: servidor. ▒▒█
█ ▒ Se é animação ou toggle de menu: cliente. Simples assim. ▒ █
█ █ █ █
█ ▒ // Cliente envia só identificadores ▒ █
█▒▒ ¡ POST /checkout { productIds: [1, 2, 3] } ▒▒█
█ ▒,█▄ ▒ █
█ █▀▀° // Servidor decide TUDO - preços, descontos, total █ █
█▒▒ const user = await getUserFromToken(request) ▒▒█
█ ▒ const products = await db.products.findMany({ where: { id: { in: productIds }}}) ▒ █
█ █ const discount = await getUserDiscount(user.id) // Do banco, não do cliente █ █
█ ▒ const total = calculateTotal(products, discount) ▒ █
█▒▒ ▒▒█
█ ▒ // Valida autorização antes de retornar ▒ █
█ █ if (!canAccessUser(currentUser, params.id)) { █ █
█ ▒ return Response.json({ error: 'Forbidden' }, { status: 403 }) ▒ █
█▒▒ } ▒▒█
█ ▒ ▒ █
█ █ █ █
█ ▒ Nunca confie que o dado chegou sanitizado porque validou no React Hook Form. Dá pra ▒ █
█▒▒ contornar com um curl direto no endpoint. ¡ ▒▒█
█ ▒ ▄█,▒ █
█ █ - 2. Principle of Least Privilege: °▀▀█ █
█▒▒ ▒▒█
█ ▒ O cliente recebe apenas o mínimo necessário. Não envie o objeto user completo com SSN e ▒ █
█ █ admin_secret só porque é conveniente. Filtre antes de enviar: █ █
█ ▒ ▒ █
█▒▒ // Servidor decide o que vai ▒▒█
█ ▒ return json({ ▒ █
█ █ id: user.id, █ █
█ ▒ username: user.username, ▒ █
█▒▒ canEdit: user.role === 'admin' // Boolean, não o role completo ▒▒█
█ ▒ // SSN? Secrets? Ficam aqui. ▒ █
█ █ }) █ █
█ ▒ ▒ █
█▒▒ ¡ ▒▒█
█ ▒,█▄ - 3. Nunca confie no que vem do cliente: ▒ █
█ █▀▀° █ █
█▒▒ React state, URL params, localStorage, cookies sem httpOnly, headers customizados - tudo ▒▒█
█ ▒ isso pode ser manipulado. ▒ █
█ █ █ █
█ ▒ Exemplo prático do erro comum: ▒ █
█▒▒ ▒▒█
█ ▒ // Errado: Confia em dados do cliente ▒ █
█ █ export async function POST(request) { █ █
█ ▒ const { userId, isAdmin, discount } = await request.json() ▒ █
█▒▒ ▒▒█
█ ▒ if (isAdmin) { // <- cliente envia isso ▒ █
█ █ await grantAdminAccess(userId) █ █
█ ▒ } ▒ █
█▒▒ ¡ ▒▒█
█ ▒ const total = price * (1 - discount) // <- cliente define o desconto ▄█,▒ █
█ █ await processPayment(total) °▀▀█ █
█▒▒ } ▒▒█
█ ▒ ▒ █
█ █ // Certo: Busca TUDO no servidor █ █
█ ▒ export async function POST(request) { ▒ █
█▒▒ const { productIds } = await request.json() ▒▒█
█ ▒ ▒ █
█ █ const user = await getUserFromToken(request) // token httpOnly █ █
█ ▒ const isAdmin = await checkAdminRole(user.id) // checa no banco ▒ █
█▒▒ ▒▒█
█ ▒ if (isAdmin) { ▒ █
█ █ await grantAdminAccess(user.id) // usa ID validado █ █
█ ▒ } ▒ █
█▒▒ ¡ ▒▒█
█ ▒,█▄ const discount = await getUserDiscount(user.id) // checa no banco ▒ █
█ █▀▀° const total = calculateTotal(productIds, discount) █ █
█▒▒ await processPayment(total) ▒▒█
█ ▒ } ▒ █
█ █ █ █
█ ▒ ▒ █
█▒▒ O cliente nunca dita regras de negócio. Ele sugere, o servidor decide. ▒▒█
█ ▒ ▒ █
█ █ ── /defesas complementares █ █
█ ▒ ▒ █
█▒▒ - **Content Security Policy (CSP): Uma layer adicional de proteção, mas não a salvação. CSP ▒▒█
█ ▒ bem configurado dificulta XSS e injeção de scripts. Mal configurado ('unsafe-inline', ▒ █
█ █ 'unsafe-eval'), é inútil. █ █
█ ▒ ▒ █
█▒▒ Implementação em Next.js [7]: ¡ ▒▒█
█ ▒ ▄█,▒ █
█ █ // next.config.js °▀▀█ █
█▒▒ const nextConfig = { ▒▒█
█ ▒ async headers() { ▒ █
█ █ return [{ █ █
█ ▒ source: '/:path*', ▒ █
█▒▒ headers: [{ ▒▒█
█ ▒ key: 'Content-Security-Policy', ▒ █
█ █ value: [ █ █
█ ▒ "default-src 'self'", ▒ █
█▒▒ "script-src 'self' 'unsafe-eval' 'unsafe-inline'", // Next.js ▒▒█
█ ▒ precisa disso ▒ █
█ █ "style-src 'self' 'unsafe-inline'", █ █
█ ▒ "img-src 'self' blob: data:", ▒ █
█▒▒ ¡ "font-src 'self'", ▒▒█
█ ▒,█▄ "object-src 'none'", ▒ █
█ █▀▀° "base-uri 'self'", █ █
█▒▒ "form-action 'self'", ▒▒█
█ ▒ "frame-ancestors 'none'", ▒ █
█ █ "upgrade-insecure-requests" █ █
█ ▒ ].join('; ') ▒ █
█▒▒ }] ▒▒█
█ ▒ }] ▒ █
█ █ } █ █
█ ▒ } ▒ █
█▒▒ ▒▒█
█ ▒ ▒ █
█ █ !!! importante: CSP >>não<< substitui validação server-side. Auxilia na defesa █ █
█ ▒ client-side mas não é bala de prata. [8] ▒ █
█▒▒ ¡ ▒▒█
█ ▒ - **Auditoria do bundle: Use bundle-analyzer periodicamente para ver o que está sendo ▄█,▒ █
█ █ exposto. Se aparecer API_SECRET_KEY, DATABASE_URL ou qualquer secret, é porque deu ruim. °▀▀█ █
█▒▒ ▒▒█
█ ▒ # Analise seu bundle Next.js ▒ █
█ █ npx @next/bundle-analyzer █ █
█ ▒ ▒ █
█▒▒ # Ou webpack bundle analyzer ▒▒█
█ ▒ npx webpack-bundle-analyzer .next/static/chunks/*.js ▒ █
█ █ █ █
█ ▒ - **Rate limiting e proteção contra brute force: Endpoints de login, APIs públicas, Server ▒ █
█▒▒ Actions - tudo precisa de rate limiting. ▒▒█
█ ▒ ▒ █
█ █ // Middleware simples de rate limit █ █
█ ▒ const rateLimits = new Map() ▒ █
█▒▒ ¡ ▒▒█
█ ▒,█▄ export function rateLimit(identifier, maxRequests = 5, windowMs = 60000) { ▒ █
█ █▀▀° const now = Date.now() █ █
█▒▒ const userLimit = rateLimits.get(identifier) || { count: 0, resetTime: now + ▒▒█
█ ▒ windowMs } ▒ █
█ █ █ █
█ ▒ if (now > userLimit.resetTime) { ▒ █
█▒▒ userLimit.count = 1 ▒▒█
█ ▒ userLimit.resetTime = now + windowMs ▒ █
█ █ } else { █ █
█ ▒ userLimit.count++ ▒ █
█▒▒ } ▒▒█
█ ▒ ▒ █
█ █ rateLimits.set(identifier, userLimit) █ █
█ ▒ ▒ █
█▒▒ if (userLimit.count > maxRequests) { ¡ ▒▒█
█ ▒ throw new Error('Too many requests') ▄█,▒ █
█ █ } °▀▀█ █
█▒▒ } ▒▒█
█ ▒ ▒ █
█ █ // Uso em API route █ █
█ ▒ export async function POST(request) { ▒ █
█▒▒ const ip = request.headers.get('x-forwarded-for') || 'unknown' ▒▒█
█ ▒ rateLimit(ip, 5, 60000) // 5 requests por minuto ▒ █
█ █ █ █
█ ▒ // ... resto da lógica ▒ █
█▒▒ } ▒▒█
█ ▒ ▒ █
█ █ █ █
█ ▒ ================================================================================ ▒ █
█▒▒ ¡ 3. conclusão ▒▒█
█ ▒,█▄ ================================================================================ ▒ █
█ █▀▀° █ █
█▒▒ A famosa e velha frase "Grandes poderes vêm com grandes responsabilidades" nesse caso nunca ▒▒█
█ ▒ fez tanto sentido, frameworks javascript fullstack tem um potencial enorme, mas isso se ▒ █
█ █ usadas corretamente. █ █
█ ▒ A linha entre servidor e cliente pode parecer borrada durante o desenvolvimento, mas ela é ▒ █
█▒▒ bem clara pra quem sabe ver. ▒▒█
█ ▒ ▒ █
█ █ No geral eu diria, se for pra colocar em um cenário ideal, que a gente poderia dividir em █ █
█ ▒ três camadas de validação: ▒ █
█▒▒ ▒▒█
█ ▒ - Client: UX e feedback (estados de carregamento, validação visual) ▒ █
█ █ - Server: Validação real (regras de negócio, autorização, cálculos críticos) █ █
█ ▒ - Database: Constraints (última linha de defesa, integridade dos dados) ▒ █
█▒▒ ¡ ▒▒█
█ ▒ A regra é clara: se roda no navegador, o usuário tem acesso, se usuário tem acesso, pode ser ▄█,▒ █
█ █ comprometido. °▀▀█ █
█▒▒ Desenvolva com essa mentalidade e suas aplicacoes serão muito mais seguras. ▒▒█
█ ▒ ▒ █
█ █ A conveniência do desenvolvimento nunca deve comprometer a segurança dos usuários. █ █
█ ▒ E no final das contas, lidar com um incidente de segurança vai trazer muito mais dor de ▒ █
█▒▒ cabeça do que fazer a validação certa desde o início. ▒▒█
█ ▒ ▒ █
█ █ ================================================================================ █ █
█ ▒ 4. agradecimentos e referências ▒ █
█▒▒ ================================================================================ ▒▒█
█ ▒ ▒ █
█ █ [1] CWE-602: Client-Side Enforcement of Server-Side Security █ █
█ ▒ https://cwe.mitre.org/data/definitions/602.html ▒ █
█▒▒ ¡ ▒▒█
█ ▒,█▄ [2] Next.js Security Guidelines ▒ █
█ █▀▀° https://nextjs.org/docs/going-to-production#security █ █
█▒▒ ▒▒█
█ ▒ [3] Remix Security Considerations - Remix Team ▒ █
█ █ https://remix.run/docs/en/main/guides/security █ █
█ ▒ ▒ █
█▒▒ [4] Client-Side Data Hydration Security - React Security ▒▒█
█ ▒ https://react.dev/learn/keeping-components-pure ▒ █
█ █ █ █
█ ▒ [5] Web Cache Poisoning - PortSwigger Web Security Academy ▒ █
█▒▒ https://portswigger.net/web-security/web-cache-poisoning ▒▒█
█ ▒ ▒ █
█ █ [6] "The State of JS Security" - Liran Tal █ █
█ ▒ https://snyk.io/blog/nodejs-security-release-summary/ ▒ █
█▒▒ ¡ ▒▒█
█ ▒ [7] Next.js Security Best Practices - Vercel ▄█,▒ █
█ █ https://nextjs.org/docs/advanced-features/security-headers °▀▀█ █
█▒▒ ▒▒█
█ ▒ [8] OWASP Content Security Policy Cheat Sheet ▒ █
█ █ https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html █ █
█ ▒ ▒ █
█▒▒ ================================================================================ ▒▒█
█ ▒ = ▒ █
█ █ :===:======:== = █ █
█ ▒ ================ == ▒ █
█▒▒ ================ = ▒▒█
█ ▒ ====@@@@@@==== = ▒ █
█ █ =====@@@@@@===== = █ █
█ ▒ =====@@@@@@===== = ▒ █
█▒▒ ¡ ============== = ▒▒█
█ ▒,█▄ ================ = ▒ █
█ █▀▀° === ===== == = █ █
█▒▒ = ▒▒█
█ ▒ = ▒ █
█ █ = █ █
█ ▒ = ▒ █
█▒▒ = ▒▒█
█ ▒ = ▒ █
█ █ ============== = █ █
█ ▒ ==%@@@@@@@@@@@== = ▒ █
█▒▒ =%@@@@@@@@@@@= = ▒▒█
█ ▒ ==%@@@@@@@@@@@== = ▒ █
█ █ ==%@@@@@@@@@@@== = █ █
█ ▒ ==%@@@@@@@@@@@== = ▒ █
█▒▒ =%@@@@@@@@@@@= = ¡ ▒▒█
█ ▒ ================ == ▄█,▒ █
█ █ === ===== == = °▀▀█ █
█▒▒ = ▒▒█
█ ▒ ▒ █
█ █ // anakan █ █
█ ▒ ▒ █
█▒▒ ▒▒█
█▒▒▒▒▒ ░░ ▒▒▒▒▒▒▒▒█
█ ▒ ▓▄█
█ █ T R A M O I A · Z I N E · 2 0 2 6 gld ██
▀▄▄▄▄ ▒▒▄▄▀