Como os Cookies Funcionam?
Pra que serve um Cookie?
O cookie é um pequeno texto (um conjunto de caracteres, letras, números, símbolos) armazenado em seu navegador em nome de determinado website. Cada vez que tal website for acessado o navegador enviará automaticamente todos os cookies armazenados para ele, sem necessidade de intervenção humana ou conscentimento.
Se este website estiver lhe enviado um comando para que você armazene um cookie, seu navegador começará a armazená-lo. A partir daí toda a interação do navegador com este mesmo website ficará enviando este cookie à ele.
Isso é extremamente útil em duas situações:
- rastrear quais páginas uma pessoa ou dispositivo está acessando;
- permitir acesso a conteúdos restritos no website após realização de login.
Exemplo de rastreamento:
- uma pessoa acessa o website exemplo-cookie.com através de seu navegador pela primeira vez,
- ao receber a solicitação de visualização da página o servidor do website, percebendo que a pessoa nunca acessou o site antes, cria internamente um código identificador único e exclusivo que representa aquela pessoa
- o servidor do website responde ao navegador da pessoa com a página e uma ordem de que um cookie seja criado com o código identificador recém gerado
- a partir daí toda interação da pessoa, neste navegador, neste website, enviará o cookie recém criado
- cada página acessada pelo usuário em exemplo-cookie.com será monitorada já que toda interação o mesmo cookie é enviado.
Quantos cookies um site pode carregar?
Cada website (nome de domínio) pode conter até 4093 bytes de cookies no total. Considerando que 1 byte equivale a um caracter, como desenvolvedor tome cuidado para que todos seus cookies não passem de 4093 caracteres no total.
De que um Cookie é composto?
Um Cookie é composto por:
- Uma chave (key) do tipo texto (string), que permite o Cookie ser buscado e identificado individualmente, já que vários Cookies podem existir juntos, mas cada um com sua chave diferente e única. Pode conter qualquer caractere US-ASCII exceto separadores de caractere como: ( ) < > @ , ; : \ " / [ ] ? = { } e caracteres de controle (CTLs), espaços, ou tabulações (TAB).
- Um valor (string), que contem o conteúdo do cookie em si, seja um conjunto de caracteres com algum sentido para um humano (o nome de um usuário, um número, etc) ou uma série de caracteres sem muito sentido aparente, sendo eles um hash, algo criptografado ou um texto aleatório.
- Um domínio ou IP onde ele se aplica
Ele é enviado numa request header no seguinte formato:
cookie: session=Afjli34Gjdgjp4lg_gfj:FA0f.JGodo4G; __Secure-id=123456; __Secure-device=34t=324fgdsg5fdanjhg5hf:; qualquer-coisa=A90f0fgjeDILOJ49jgdis
Perceba que ele sempre segue o padrão:
cookie:
Além destas duas, o navegador também armazena outras propriedades internamente chamadas diretivas, apenas acessíveis no próprio navegador, sendo elas:
Diretiva "Secure"
Indica que o cookie só será enviado para websites com TLS (Transport Layer Security) ativos, em outras palavras, que utilizem criptografia HTTPS "https://..." (TLS, Transport Layer Security).
Websites que solicitem a criação de cookies com essa diretiva também precisam estar em HTTPS.
E em sua ausência?
O cookie será enviado tanto para websites "http://..." quanto para "https://..."
Limitações
An active network attacker can supply a Set-Cookie header over a HTTP connection to the same hostname as the site and install either a Secure or a non-Secure cookie of the same name. When the browser sends the cookie back to the site over HTTPS, the site has no mechanism for determining whether the cookie has been overwritten by the attacker.
Diretiva "HttpOnly"
Indica que o cookie não pode ser manipulado pelo JavaScript através da API dos browsers Document.cookie.
Qual o benefício?
Se o website utiliza códigos Javascript de fontes externas e possivelmente não confiáveis, eles não conseguirão ter acesso a qualquer cookie HttpOnly, o que impede que eles sejam "roubados", visualizados, por códigos Javascript maliciosos.
E em sua ausência?
O cookie pode ser manipulado (lido, editado, removido) por qualquer código JavaScript carregado na página.
Diretiva "SameSite" Strict, Lax & None
Esta diretiva corrige (em seus modos Strict e Lax) um comportamento perigoso dos navegadores que acabam enviando cookies em qualquer circunstância a um back-end, independente de qual página tenha a requisitado.
Imagine a seguinte situação: um website de armazenamento de fotos "www.suas-fotos.com" armazena fotos de usuários. Para acessar o website cada usuário deve se cadastrar (login e senha) e realizar login. Ao fazê-lo o browser onde o usuário está recebe um cookie para se identificar ao longo de sua interação sem precisar informar login e senha a todo momento. O website permite fazer upload de novas fotos no endereço "/uploadFotos" e apagar todas as fotos já armazenadas no endereço "/apagarTodasFotos" com o método HTTP GET. Um site terceiro de endereço "www.terceiro.com" coloca em suas páginas links para o "www.suas-fotos.com/apagarTodasFotos". Qualquer usuário de "www.suas-fotos.com" que estiver com um cookie ativo, ao clicar nestes links, terá todas suas fotos apagadas. Perigoso, não é mesmo? Essa vulnerabilidade se chama Cross-Site Request Forgery (CSRF).
Para proteger os usuários deste tipo de perigo basta utilizarmos a diretiva SameSite=Strict e até certo ponto a SameSite=Lax.
None - implica que o navegador enviará o cookie mesmo que ela tenha se originado a partir de um website de terceiro.
Lax - implica que o navegador não enviará o cookie em uma requisição que tenha se originado a partir de um website de terceiro. Entretanto se o usuário clicar em um link que o leve para a página originária do website, ele será enviado. O Lax permite que o cookie seja enviado em primeiras interações do tipo GET, HEAD, OPTIONS e TRACE.
Strict - implica que o navegador não enviará o cookie em uma requisição AJAX que tenha se originado a partir de um website de terceiro ( e nem de um link recém aberto. O cookie só é enviado em uma segunda interação dentro da página de origem do cookie.
Qual o benefício?
A diretiva SameSite com os parâmetros Strict e Lax dão mais segurança à um website, impedindo que cookies sejam enviados após a interação em um website de terceiro. O terceiro não tem acesso aos cookies, entretanto pode forçar que ações sejam realizadas, como por exemplo realizar exclusões de dados em nome do usuário autenticado com cookie.
E em sua ausência?
Este comportamento depende do navegador e da sua versão.
Navegadores mais novos (principalmente os baseados no Chrome) consideram que a ausência desta diretiva significa "SameSite=Lax" mas sem a diretiva Secure.
Navegadores em suas versões antigas consideram que independentemente da origem de quem estiver requisitando um endereço HTTP via GET ou POST, cookies relativos a este website serão enviados.
Referências
Ótimo artigo sobre a diretiva SameSite
https://web.dev/samesite-cookies-explained/
Outro ótimo artigo sobre a diretiva SameSite
https://www.treinaweb.com.br/blog/cross-site-request-forgery-csrf-e-abordagens-para-mitiga-lo
Formas de mitigar CSRF
https://brightsec.com/blog/csrf-mitigation/
Artigo no Google Developers sobre o SameSite
https://developers.google.com/search/blog/2020/01/get-ready-for-new-samesitenone-secure?hl=pt-br
Mudanças sobre o SameSite cookie
https://blog.heroku.com/chrome-changes-samesite-cookie
Como mudanças nos navegadores sobre o SameSite cookie podem quebrar seu website?
https://medium.com/swlh/how-the-new-chrome-80-cookie-rule-samesite-none-secure-affects-web-development-c06380220ced
Diretiva "Expires"
Contém a data e hora de expiração do cookie, ou seja, quando ele deixará de existir no navegador. As vezes este valor é ignorado pelo navegador e o cookie continua existindo mesmo vencido. Esta data não é enviada ao servidor, permanece apenas no navegador.
A data e hora deve estar no formato HTTP Date, um padrão não universal mas que infelzimente deve ser seguido:
Wed, 21 Oct 2015 07:28:00 GMT
Obtido no JavaScript pelo método .toUTCString() de um objeto do tipo Date.
E em sua ausência?
Se a diretiva "Max-Age" também não estiver definida este cookie é chamado de "Session Cookie", ele é removido assim que o navegador for fechado, sendo um cookie temporário/volátil. Mas atenção, se o usuário utiliza um navegador que permite reabrir as últimas páginas abertas após ter sido fechado o cookie continuará ali.
Artigo sensacional sobre a diretiva SameSite
Diretiva "Max-Age"
A diretiva "Max-Age" define qual a validade do cookie em segundos a partir de seu momento de criação. Se este valor é zero ou negativo o cookie será expirado imediatamente. Se o Expires também estiver definido, o Max-Age tem preferência e o Expires é ignorado.
E em sua ausência?
Se a diretiva "Max-Age" também não estiver definida este cookie é chamado de "Session Cookie", ele é removido assim que o navegador for fechado, sendo um cookie temporário/volátil. Mas atenção, se o usuário utiliza um navegador que permite reabrir as últimas páginas abertas após ter sido fechado o cookie continuará ali.
Diretiva "Domain"
Essa diretiva define sob qual domínio ou subdomínio o cookie será enviado.
Exemplos válidos de domínio:
- seu-site.com
- .seu-site.com
- www.seu-site.com
- .www.seu-site.com
- abc.dfg.ijk.seu-site.com
Exemplos inválidos de domínio:
- http://seu-site.com
- https://www.seu-site.com
- www.seu-site.com:8080
Uma página pode criar um cookie para seu próprio domínio ou qualquer domínio pai, desde que o domínio pai não seja um sufixo público (public suffix).
Em quais casos o cookie é enviado ao servidor?
Esta diretiva sofreu mudanças de especificação e o comportamento varia de acordo com a versão do navegador. A especificação mais antiga, RFC 2109 se aplica para navegadores antigos e a especificação mais nova RFC 6265 se aplica navegadores mais novos.
A escolha de para qual site o cookie é enviado ou não passa pelo algoritmo de "domain match" https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3
E em sua ausência?
Em sua ausência o navegador considerará que o Domain do cookie é exatamente o do servidor que o respondeu.
Qual seu comportamento em casos específicos?
- Se não for especificado, será usado o host do URL do documento atual, não incluindo subdomínios.
- Ao contrário das especificações anteriores, pontos de prefixo em nomes de domínio (.example.com) são ignorados.
- Se um domínio for especificado, subdomínios estarão sempre incluídos.
Esta diretiva é enviada ao web server?
Não, uma vez criado o cookie a informação de domínio permanece apenas no navegador.
Quais suas limitações e restrições?
Esta diretiva sofreu mudanças de especificação. A especificação mais antiga, RFC 2109 e a especificação mais nova RFC 6265 trazem alguns conflitos.
Na especificação antiga RFC 2109, cookies de Domain ".seu-site.com" não são enviados para "seu-site.com", entretanto são para "abc.seu-site.com" e "xyz.seu-site.com"...
Na especificação nova RFC 6265, cookies de Domain ".seu-site.com" tem o ponto ignorado e funcionam equivalente à "seu-site.com". Cookies são enviados tanto para "seu-site.com" quanto para "abc.seu-site.com" (sub domínio) quanto para "aaa.abc.seu-site.com" (sub sub domínio)".
Segundo a RFC 6265, Domains que forem de um "public suffix" são inválidos e não serão salvos, como ".com" ou "github.com". A lista de quais domínios são considerados public suffix está aqui https://publicsuffix.org/
Um website com domínio "www.seu-site.com" não pode criar cookies com Domain em "abc.www.seu-site.com" nem em "xyz.seu-site.com"
Um website com domínio em "www.seu-site.com" pode criar cookies com Domain em "seu-site.com".
Diretiva "Path"
Um caminho (path) para o qual o cookie será enviado.
O caminho se refere à esta parte em negrito de uma URL:
https://www.seudominio.com.br/caminho/desejado/123
Quando um cookie foi criado com um caminho definido "/caminho" por exemplo, o navegador sempre verificará antecipadamente
esta informação (sobre em quais paths, caminhos, o cookie é enviado) não é enviada ao servidor, permanece apenas no navegador.
E em sua ausência?
Em sua ausência será considerado o caminho raíz "/" e o cookie será enviado em todas as requisições ao domínio, independende do caminho.
Referências
Conheça a documentação do Set-Cookie na Mozilla Developer Network para entender detalhadamente todos os possíveis parâmetros de criação de um Cookie.
Prefixo __Host-
este cookie deve:
- Ter o valor de Path=/
- Não ter um Domain atribuído
- Ser marcado como Secure
Quais são seus benefícios?
- Não pode ser sobrescrito por subdomínios
- Não pode ser sobrescrito por conexões HTTP
- Não é enviado em conexões HTTP
- Não é enviado para subdomínios
Another solution for this problem is use of Cookie Prefixes for cookie with CSRF token. If cookie has __Host- prefix e.g. Set-Cookie: __Host-token=RANDOM; path=/; Secure then the cookie:
- Cannot be (over)written from another subdomain.
- Must have the path of /.
- Must be marked as Secure (i.e, cannot be sent over unencrypted HTTP).
- As of July 2020 cookie prefixes are supported by all major browsers except Internet Explorer.
See the Mozilla Developer Network and IETF Draft for further information about cookie prefixes.
Previnem cookies de serem sobrescritos
https://www.sjoerdlangkemper.nl/2017/02/09/cookie-prefixes/
Prefixo __Secure-
This feature adds a set of restrictions upon the names which may be used for cookies with specific properties. These restrictions enable user agents to smuggle cookie state to the server within the confines of the existing "Cookie" request header syntax, and limits the ways in which cookies may be abused. In a nutshell: __Secure-
cookies have to have the Secure
flag, and __Host-
cookies have to have Path=/
, can't have Domain
, and might require Secure
(depending on the setter).
From: https://www.sjoerdlangkemper.nl/2017/02/09/cookie-prefixes/
Cookie prefixes make it possible to flag your cookies to have different behavior, in a backward compatible way. It uses a dirty trick to put a flag in the name of the cookie. When a cookie name starts with this flag, it triggers additional browser policy on the cookie in supporting browsers.
The __Secure- prefix makes a cookie accessible from HTTPS sites only. A HTTP site can not read or update a cookie if the name starts with __Secure-. This protects against the attack we earlier described, where an attacker uses a forged insecure site to overwrite a secure cookie.
Esse prefixo corrige a vulnerabilidade da Diretiva Secure, que permite um subdomínio tanto em HTTP como HTTPS de substituir cookies sem a autorização do usuário (Cookie Overwriting).
The __Secure- prefix makes a cookie accessible from HTTPS sites only. A HTTP site can not read or update a cookie if the name starts with __Secure-. This protects against the attack we earlier described, where an attacker uses a forged insecure site to overwrite a secure cookie.
https://www.sjoerdlangkemper.nl/2017/02/09/cookie-prefixes/
Como um cookie é criado?
O website fará uma "solicitação"/"pedido" para que o navegador crie um cookie. Cabe então ao navegador seguir esta instrução e criá-lo. O pedido de criação está no cabeçalho Set-Cookie, enviado numa resposta "response" HTTP.
Que tamanho um cookie pode ter?
Cada navegador tem liberdade para escolher estes limites, mas não são muito altos, conforme mostrarei logo mais.
O Google Chrome segue a seguinte diretiva:
https://chromestatus.com/feature/4946713618939904
Nele fica claro que o nome do cookie + seu valor não pode ultrapassar 4096 bytes (4kb), mas não tenho certeza com relação a limitação de 1024 bytes (1kb), se ela se aplica ao nome do cookie ou ao seu valor.
Tendo isso em mente, arquitete seu sistema de forma a não armazenar valores nos cookies que ocupem mais de 1kb.
Previnindo CSRF ao usar Cookies
Padrão Double Cookie
Artigo super bom explicando o Double Cookie
https://medium.com/@kaviru.mihisara/double-submit-cookie-pattern-820fc97e51f2
Referencias
Testing Cookies - OWASP
https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/06-Session_Management_Testing/02-Testing_for_Cookies_Attributes
Documentação Set-Cookie - Mozilla Developer Network (MDN)
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
Lições aprendidas sobre cookies e seu comportamento em subdomínios
https://fwielstra.github.io/2017/03/13/fun-with-cookies-and-subdomains/