JavaScript

Guia Completo de Autenticação em Node.js: JWT, Bcrypt e Sessões com Express

7 min de leitura

Guia Completo de Autenticação em Node.js: JWT, Bcrypt e Sessões com Express

Autenticação em Node.js: JWT, Bcrypt e Sessões com Express Autenticação é o pilar da segurança em aplicações web. Nesta aula, exploraremos três abordagens fundamentais: JWT (JSON Web Tokens), Bcrypt para hash de senhas e gerenciamento de sessões com Express. Compreender quando e como usar cada uma transformará sua capacidade de construir sistemas seguros e escaláveis. Proteção de Senhas com Bcrypt Por que não armazenar senhas em texto plano? Senhas jamais devem ser armazenadas em texto plano no banco de dados. Se comprometido, todos os usuários estarão em risco. O Bcrypt é uma função de hash adaptativa que torna computacionalmente cara a reversão, implementando "salt" automaticamente (um valor aleatório que previne ataques de rainbow table). O custo 10 (padrão) significa que o algoritmo itera 2^10 vezes. Nunca use custo abaixo de 10 em produção. A função é crucial: ela hash a entrada e compara com o hash armazenado, nunca descriptografando. JWT: Autenticação Stateless Estrutura e fluxo JWT JWT (JSON Web Token)

<h2>Autenticação em Node.js: JWT, Bcrypt e Sessões com Express</h2>

<p>Autenticação é o pilar da segurança em aplicações web. Nesta aula, exploraremos três abordagens fundamentais: JWT (JSON Web Tokens), Bcrypt para hash de senhas e gerenciamento de sessões com Express. Compreender quando e como usar cada uma transformará sua capacidade de construir sistemas seguros e escaláveis.</p>

<h2>Proteção de Senhas com Bcrypt</h2>

<h3>Por que não armazenar senhas em texto plano?</h3>

<p>Senhas jamais devem ser armazenadas em texto plano no banco de dados. Se comprometido, todos os usuários estarão em risco. O Bcrypt é uma função de hash adaptativa que torna computacionalmente cara a reversão, implementando &quot;salt&quot; automaticamente (um valor aleatório que previne ataques de rainbow table).</p>

<pre><code class="language-javascript">const bcrypt = require(&#039;bcrypt&#039;);

// Durante o registro do usuário

async function registrarUsuario(email, senhaPlana) {

const salt = await bcrypt.genSalt(10); // Gera salt com custo 10

const senhaHash = await bcrypt.hash(senhaPlana, salt);

// Salva no banco: { email, senhaHash }

console.log(&#039;Senha armazenada:&#039;, senhaHash);

}

// Durante login, valida a senha

async function validarSenha(senhaPlana, senhaHashArmazenada) {

const valida = await bcrypt.compare(senhaPlana, senhaHashArmazenada);

return valida; // true ou false

}

registrarUsuario(&#039;user@example.com&#039;, &#039;minhaSenha123&#039;);</code></pre>

<p>O custo 10 (padrão) significa que o algoritmo itera 2^10 vezes. Nunca use custo abaixo de 10 em produção. A função <code>compare()</code> é crucial: ela hash a entrada e compara com o hash armazenado, nunca descriptografando.</p>

<h2>JWT: Autenticação Stateless</h2>

<h3>Estrutura e fluxo JWT</h3>

<p>JWT (JSON Web Token) é um padrão aberto que permite transmitir informações seguras entre partes. Composto por três seções separadas por pontos: header.payload.signature. Diferente de sessões, não requer armazenamento no servidor, tornando-o ideal para APIs escaláveis.</p>

<pre><code class="language-javascript">const jwt = require(&#039;jsonwebtoken&#039;);

const express = require(&#039;express&#039;);

const app = express();

const SECRET = process.env.JWT_SECRET || &#039;sua_chave_secreta_super_segura&#039;;

// Gerar token após login bem-sucedido

function gerarToken(usuario) {

const payload = {

id: usuario.id,

email: usuario.email

};

const token = jwt.sign(payload, SECRET, { expiresIn: &#039;24h&#039; });

return token;

}

// Middleware para verificar token

function verificarToken(req, res, next) {

const token = req.headers.authorization?.split(&#039; &#039;)[1]; // Bearer &lt;token&gt;

if (!token) {

return res.status(401).json({ erro: &#039;Token não fornecido&#039; });

}

try {

const decoded = jwt.verify(token, SECRET);

req.usuario = decoded; // Adiciona ao request

next();

} catch (error) {

res.status(401).json({ erro: &#039;Token inválido ou expirado&#039; });

}

}

// Rota protegida

app.get(&#039;/perfil&#039;, verificarToken, (req, res) =&gt; {

res.json({ mensagem: Bem-vindo, ${req.usuario.email} });

});</code></pre>

<p><strong>Quando usar JWT</strong>: APIs RESTful, aplicações mobile, arquiteturas de microsserviços. O cliente armazena o token (localStorage, memory) e o envia a cada requisição. O servidor apenas valida, sem manter estado.</p>

<h2>Gerenciamento de Sessões com Express</h2>

<h3>Sessões no lado do servidor</h3>

<p>Sessões mantêm estado no servidor. O Express, combinado com <code>express-session</code> e um store de sessões, cria um cookie no cliente contendo apenas um identificador, enquanto os dados reais ficam no servidor.</p>

<pre><code class="language-javascript">const session = require(&#039;express-session&#039;);

const RedisStore = require(&#039;connect-redis&#039;).default;

const { createClient } = require(&#039;redis&#039;);

const express = require(&#039;express&#039;);

const app = express();

// Cliente Redis para armazenar sessões

const redisClient = createClient();

redisClient.connect();

// Configurar sessões

app.use(session({

store: new RedisStore({ client: redisClient }),

secret: process.env.SESSION_SECRET || &#039;chave_secreta&#039;,

resave: false,

saveUninitialized: false,

cookie: {

secure: true, // HTTPS apenas

httpOnly: true, // Não acessível por JavaScript

sameSite: &#039;strict&#039;,

maxAge: 24 60 60 * 1000 // 24 horas

}

}));

// Login com sessão

app.post(&#039;/login&#039;, (req, res) =&gt; {

// Após validar credenciais...

req.session.usuarioId = 123;

req.session.email = &#039;user@example.com&#039;;

res.json({ mensagem: &#039;Login realizado&#039; });

});

// Rota protegida

app.get(&#039;/dashboard&#039;, (req, res) =&gt; {

if (!req.session.usuarioId) {

return res.status(401).json({ erro: &#039;Não autenticado&#039; });

}

res.json({ mensagem: Bem-vindo, ${req.session.email} });

});

// Logout

app.post(&#039;/logout&#039;, (req, res) =&gt; {

req.session.destroy((err) =&gt; {

if (err) return res.status(500).json({ erro: err });

res.json({ mensagem: &#039;Logout realizado&#039; });

});

});</code></pre>

<p><strong>Quando usar sessões</strong>: Aplicações monolíticas, single-page applications (SPAs) server-side, quando o servidor é confiável e centralizado. Redis ou banco de dados armazenam dados da sessão; o cliente recebe apenas um cookie seguro.</p>

<h2>Comparação e Escolha</h2>

<blockquote><p><strong>JWT</strong>: Stateless, escalável, ideal para APIs e microsserviços. Desvantagem: revogar token é complexo (mantenha blacklist em cache).</p></blockquote>

<blockquote><p><strong>Sessões</strong>: Stateful, controle total, fácil revogar. Desvantagem: requer infraestrutura de armazenamento.</p></blockquote>

<p>A escolha depende da arquitetura. Em um monólito com um único servidor, sessões são simples. Em uma arquitetura distribuída com múltiplos servidores/microsserviços, JWT reduz overhead. Uma estratégia híbrida é comum: use JWT para autenticação inicial e sessão para dados sensíveis locais.</p>

<h2>Conclusão</h2>

<p>Três pontos-chave dominados nesta aula:</p>

<ol>

<li><strong>Bcrypt protege senhas irreversivelmente</strong> — sempre hash com salt, nunca armazene em texto plano. Use <code>bcrypt.compare()</code> para validar, nunca armazene a senha plana.</li>

</ol>

<ol>

<li><strong>JWT oferece autenticação stateless</strong> — ideal para APIs, mas exige estratégia para revogação. Token é autocontido e escalável entre múltiplos servidores.</li>

</ol>

<ol>

<li><strong>Sessões mantêm controle centralizado</strong> — simples revogar, mas requerem estado no servidor. Escolha baseando-se na arquitetura e requisitos de escalabilidade.</li>

</ol>

<p>Combine essas técnicas conforme necessário. Em aplicações reais, JWT para APIs públicas, sessões para SPAs e sempre Bcrypt para senhas.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://www.npmjs.com/package/jsonwebtoken" target="_blank" rel="noopener noreferrer">Documentação oficial jsonwebtoken (npm)</a></li>

<li><a href="https://www.npmjs.com/package/bcrypt" target="_blank" rel="noopener noreferrer">Documentação oficial bcrypt (npm)</a></li>

<li><a href="https://www.npmjs.com/package/express-session" target="_blank" rel="noopener noreferrer">Express-session documentação</a></li>

<li><a href="https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html" target="_blank" rel="noopener noreferrer">OWASP Authentication Cheat Sheet</a></li>

<li><a href="https://github.com/goldbergyoni/nodebestpractices#6-security-best-practices" target="_blank" rel="noopener noreferrer">Node.js Best Practices - Security</a></li>

</ul>

Comentários

Mais em JavaScript

Estruturas de Controle em JavaScript: if, switch e Tratamento de Fluxo: Do Básico ao Avançado
Estruturas de Controle em JavaScript: if, switch e Tratamento de Fluxo: Do Básico ao Avançado

Estruturas Condicionais: A Base da Lógica em JavaScript As estruturas de cont...

Boas Práticas de ES Modules em JavaScript: import, export e Organização de Código para Times Ágeis
Boas Práticas de ES Modules em JavaScript: import, export e Organização de Código para Times Ágeis

O que são ES Modules? ES Modules (ECMAScript Modules) é o sistema oficial de...

Boas Práticas de Performance em JavaScript: Profiling, Lazy Loading e Web Vitals para Times Ágeis
Boas Práticas de Performance em JavaScript: Profiling, Lazy Loading e Web Vitals para Times Ágeis

Profiling: Entendendo o Desempenho Real O profiling é a base de qualquer otim...