Server-side parte 5
Sessão, cookies, autenticação e autorização
Roteiro
- Cookies
- Sessões
- Autenticação e autorização
Cookies
🍪🍪🍪
- Motivação
- Definição
- Tipos de cookie
- Cookies no Express
Alguns sites/páginas parecem lembrar que você passou por ele. Mas como eles fazem
isso?
- HTTP é um protocolo que não armazena estado (stateless)
- Cada requisição/resposta não armazena nenhum tipo de informação de
quem solicita
- Um recurso dos navegadores chamado cookies possibilita o
armazenamento de informações quando solicitado pelo servidor
- Esse recurso é usado para a criação de sessões de navegação
- Mas o que é um cookie 🍪?
O que é um cookie 🍪?
Como os cookies são enviados
O navegador faz uma requisição
- O servidor pode enviar cookies de volta
- O navegador pode salvar o dado em arquivo (eg, .txt) ou não
- Havendo cookies salvos para um domínio, o navegador os
enviará de volta ao servidor nas requisições seguintes
Modelo alternativo: também é possível criar e usar cookies a partir do lado
cliente, usando JavaScript
Mitos sobre cookies
- Mitos:
- Cookies são como vírus que podem apagar dados do disco
- Cookies são spyware e podem roubar informação pessoal
- Cookies geram popups e spam
- Cookies são usados apenas para propagandas
- Fatos:
- Cookies são apenas dados, e não código
- Cookies não podem apagar ou ler informação do computador
- Cookies são normalmente anônimos - não contêm informação pessoal
- Cookies de um domínio não podem ser lidos por outro (hacking?)
- Cookies podem ser usados para rastrear seus hábitos de visualização
em um site em particular
Um “cookie de rastreamento”
- Uma empresa de anúncios envia um cookie quando você visita um site e o vê
novamente quando você visita outro site que também usa esses anúncios
- Assim, eles sabem que a mesma pessoa visitou os dois sites
- Pode ser corrigido dizendo-se ao navegador para não aceitar “cookies de
terceiros”
Exemplo de uso de cookie (1/5)
- Vamos criar uma página que guarda a informação sobre a língua de exibição
- Que é inglês por padrão, mas pode ser alterada e deve ser mantida quando
o usuário voltar
- Passos:
- (1) Navegador solicita a página inicial:
GET /index.html HTTP/1.1
Host: www.ispeakmanymanylanguages.com
Exemplo de uso de cookie (2/5)
- (2) Servidor responde, definindo um cookie com nome “
lang
” com o
valor (padrão) “english
”:
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: lang=english <---
(conteudo da pagina)
- O cabeçalho HTTP
Set-Cookie
serve para o servidor criar cookies
- O servidor instruiu o navegador a armazenar o dado
lang=english
em um cookie para uso posterior
- O navegador costuma salvar isso em um arquivo (eg,
.txt
)
Exemplo de uso de cookie (3/5)
- (3) Navegador armazenou o cookie. Agora, o usuário navega para outra
página do site e o cookie é enviado na requisição:
GET /promotions.html HTTP/1.1
Host: www.ispeakmanymanylanguages.com
Cookie: lang=english <---
- Todas as páginas subsequentes serão mostradas em inglês, porque em toda
nova requisição, o navegador passa a enviar o cabeçalho
Cookie
, que
contém o cookie criado pelo servidor para armazenar a língua selecionada
- Na verdade contém todos os cookies já salvos e ainda vigentes desse domínio
Exemplo de uso de cookie (4/5)
- (4) Usuário altera a língua para
"portuguese"
. A forma como nosso servidor
de exemplo possibilita isso é por meio de uma requisção GET para
/change-language?l=portuguese
:
GET /change-language?l=portuguese HTTP/1.1
Host: www.ispeakmanymanylanguages.com
Cookie: lang=english
Exemplo de uso de cookie (5/5)
- (5) Recebendo esta mensagem, o servidor responde com a página inicial
já em português e com um novo
Set-Cookie
para sobrescrever
o cookie lang
:
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: lang=portuguese <---
(conteudo da pagina em Portugues)
Quanto tempo um cookie vive?
- Cookie de sessão
- O tipo padrão, temporário, que fica
gravado apenas na memória do navegador
- Quando o navegador fecha, os cookies temporários são apagados
- Mais seguros: nenhum programa (exceto o navegador) pode acessá-los
- Cookie persistente
- Fica armazenado no computador do cliente
- Pode armazenar informação a longo prazo
- Menos seguro: usuários (ou qualquer programa) podem abrir os arquivos dos
cookies, ver/alterar valores etc.
O Chrome, por exemplo, armazena cookies persistentes
com valores criptografados em um banco SQLite (~/.config/chromium/Default/Cookies
)
Consertando nosso exemplo (1/2)
- No nosso exemplo, criamos um cookie de sessão, que é a forma padrão
- Mas queremos criar um cookie persistente para manter a língua
selecionada mesmo depois que o navegador seja fechado
- Para isso, além de
nome=valor
, os cookies possuem outros atributos
que podem ser definidos:
Expires
, Max-Age
, define até quando o cookie deve persistir
Domain
, Path
Secure
HttpOnly
Consertando nosso exemplo (2/2)
- Ao incluir o atributo
Expires
ou Max-Age
no cabeçalho Set-Cookie
,
dizemos ao navegador para criar um cookie persistente
Expires
define a data em que o navegador deve excluir o cookie
Max-Age
define um valor em milissegundos a partir de quando o navegador
deve excluir o cookie
- Para consertar, o servidor deve enviar o
Set-Cookie
do passo 5:
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: lang=portuguese; Expires=Wed, 01-Jan-2020 00:00:00 GMT
(conteudo da pagina)
Cookies no Express.js
Definindo cookies no Express.js (1/2)
- O servidor insere um
Set-Cookie
para instruir o navegador a criá-lo:
// rota para ALTERAR LÍNGUA
app.get('/change-language', (req, res) => {
// pega parâmetro com nome "lang" da querystring
let desiredLang = req.params.lang
// inclui o cabeçalho Set-Cookie "lang=%%%;Expires=Wed..."
res.cookie('lang', desiredLang, { expires: 'Wed...' }) // <--- Set-Cookie
// redireciona de volta para a própria URL
res.redirect('back')
})
// rota da PÁGINA INICIAL
app.get('/', (req, res) => { // usa cookie ou 'english' (padrão)
res.render('index', { lang: req.cookies.lang || 'english' })
})
Definindo cookies no Express.js (2/2)
- Como o Express.js é extremamente modular (por meio dos middlewares),
para que um servidor utilize os cookies enviados nas requisições HTTP,
é necessário usar o middleware
cookie-parser
:
import express from 'express'
import cookieParser from 'cookie-parser'
const app = express()
app.use(cookieParser())
- Ele processará o cabeçalho
Cookie
das requisições e populará
res.cookies
com os cookies presentes
Adendo: web storage vs cookies
- local storage, session storage e cookies servem para armazenar
informações no navegador
- Contudo, apenas os cookies são enviados nas requisições HTTP para o
servidor
- web storage é um recurso puramente do navegador e o protocolo HTTP não
tem nenhum acesso a ele
- Portanto, para manter uma sessão entre cliente e servidor, ainda precisamos
dos cookies ;)
- Ademais, cookies surgiram 1994-1997 (Netscape) e Web Storage é uma API
do HTML5 (2009+)
Sessões
Servidores usando estado
- O que é sessão
- Como estabelecer sessão
- Sessão no Express
O que é uma sessão?
- sessão: conceito abstrato que representa uma série de requisições
e respostas HTTP entre um navegador e um servidor
- O protocolo HTTP não conhece o conceito de sessão (é stateless),
mas frameworks Web sim (Express.js, PHP, ASP .NET, Java etc.)
- sessões vs. cookies:
- Um cookie é um dado armazenado no cliente
- Os dados de uma sessão são armazenados no servidor
- Sessões são normalmente criadas usando cookies
- A única informação guardada pelo cliente é um cookie contendo um
identificador único de sessão
- A cada requisição, o cliente envia o cookie de sessão e o servidor o
utiliza para recuperar as informações da sessão
Como sessões são estabelecidas
- O navegador faz requisição inicial ao servidor
- Servidor guarda o endereço IP/navegador do cliente, gera um identificador
único de sessão e envia um cookie de volta
Em PHP, esse cookie tem o nome PHPSESSID
- Em Java,
JSESSIONID
- Express.js,
connect.sid
- O cliente envia o mesmo ID da sessão de volta ao servidor
- Servidor usa o ID recebido para recuperar os dados da sessão do cliente
Sessões no Express.js (1/2)
- Assim como com cookies, vamos dizer ao Express.js para usar sessões
- Para isso, incluímos o middleware de sessões:
import express from 'express'
import session from 'express-session'
const app = express()
app.use(session({
secret: 'octocats and octodogs'
}));
- A opção
secret
é uma string usada para criar um hash do valor do
cookie de sessão, como uma medida de segurança
Sessões no Express.js (2/2)
- Para armazenar algum dado referente a uma sessão, usamos o objeto
req.session
:
app.get('/', (req, res) => {
if (req.session.views) { // um contador de visualizações nesta sessão
req.session.views++
res.setHeader('Content-Type', 'text/html')
res.end(`<p>views: ${req.session.views}</p>`)
} else {
req.session.views = 1
res.end('welcome to the session demo. refresh!')
}
})
Autenticação e Autorização
Identificação e permissões
- O que é
- Como funciona
- Tipos
- HTTP básica
- Via sessão
- No Express
Autenticação e autorização
Autenticação
~ Quem é você? Prove!
~ Tipicamente: usuário e senha
~ Mas também: one-time passwords, 2 fatores
Autorização
~ Ok, e o que você tem permissão pra fazer?
~ Tipicamente, definido em “papéis de usuário”
- Vamos ver 3 tipos de autenticação:
- HTTP básica
- Com sessão
- Com tokens
Autenticação HTTP básica
Cliente desconhecido requisita um recurso protegido
- Servidor responde
401 Unauthorized
com cabeçalho HTTP WWW-Authenticate: Basic
- Cliente lê esse cabeçalho e mostra janelinha pedindo usuário e senha
- Usuário digita e navegador inclui cabeçalho
Authorization: Basic dcdvcmQ=
dcdvcmQ=
está ilustrando que o navegador codifica os
valores digitados usuario:senha
em Base64
Bom: simples. Ruim: muito inseguro, péssima experiência de uso, difícil fazer “logout”.
Cliente desconhecido provê suas credenciais
- Servidor as valida (autentica) apenas uma vez e cria sessão
- Cliente inclui identificação da sessão nas requisições subsequentes
- Servidor apenas avalia se a sessão ainda está válida a cada requisição
- Convém expirar a sessão com o tempo
Bom: ainda simples, boa UX. Ruim: mantém estado (dificulta distribuição), cookies enviados em toda requisição, suscetível a session hijacking.
- O cabeçalho tipicamente contém 2 dados: tipo do token e algoritmo de assinatura
{
"alg": "HS256",
"typ": "JWT"
}
- Esse conteúdo é codificado em Base64 para formar a primeira parte do JWT
- Neste exemplo ficaria:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Partes do JWT: (2) Payload
- O payload contém as reivindicações (claims), tipicamente com dados sobre o usuário autorizado
-
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
- Há 3 tipos de claims:
- Registradas são as padronizadas e recomendadas (eg,
sub
de subject)
- Públicas vêm de uma lista mais ampla
- Privadas são da aplicação
- Codificado em Base64:
eyAgInN1YiI6ICIxMjM0NTY3ODkwIiwgICJuYW1lIjogIkpvaG4gRG9lIiwgICJhZG1pbiI6IHRydWV9
Partes do JWT: (3) Signature
Funcionamento do JWT
Cliente provê suas credenciais
- Servidor valida e retorna um JWT
-
Cliente envia o cabeçalho para próximas requisições
Authorization: Bearer <token>
- Servidor recebe requisição para recurso protegido, verifica se
está conforme esperado e responde normalmente
Bom: sem estado, bom para serviços. Ruim: ainda suscetível a session hijacking, tokens não podem ser excluídos (devem expirar), não armazenar dados sensíveis.
Referências
- Seções 7.1, 7.2 e 9.1 do livro “Node.js in Action”
- Introdução a JWT em jwt.io
- Passport: pacote para autenticação e autorização p/ Express
- Boas práticas usando JWT e GraphQL em hasura.io