Como habilitar o CORS com o cookie HTTPOnly para proteger o token?

Neste artigo, vemos como habilitar o CORS (Cross-Origin Resource Sharing) com o cookie HTTPOnly para proteger nossos tokens de acesso.

Atualmente, servidores back-end e clientes front-end são implantados em diferentes domínios. Portanto, o servidor deve habilitar o CORS para permitir que os clientes se comuniquem com o servidor nos navegadores.

Além disso, os servidores estão implementando a autenticação sem estado para melhor escalabilidade. Os tokens são armazenados e mantidos no lado do cliente, mas não no lado do servidor, como na sessão. Por segurança, é melhor armazenar tokens em cookies HTTPOnly.

últimas postagens

Por que as solicitações de origem cruzada são bloqueadas?

Vamos supor que nosso aplicativo front-end foi implantado em https://app.etechpt.com.com. Um script carregado em https://app.etechpt.com.com só pode solicitar recursos da mesma origem.

Sempre que tentamos enviar uma solicitação de origem cruzada para outro domínio https://api.etechpt.com.com ou outra porta https://app.etechpt.com.com:3000 ou outro esquema http://app.etechpt.com.com, o solicitação de origem cruzada será bloqueada pelo navegador.

  Como manter o modo de baixo consumo ativado permanentemente no seu iPhone

Mas por que a mesma solicitação bloqueada pelo navegador ser enviada de qualquer servidor backend usando a solicitação curl ou enviada usando ferramentas como o carteiro sem nenhum problema de CORS. Na verdade, é para a segurança proteger os usuários de ataques como CSRF (Cross-Site Request Forgery).

Vamos dar um exemplo, suponha que algum usuário tenha feito login em sua própria conta do PayPal em seu navegador. Se pudermos enviar uma solicitação de origem cruzada para paypal.com a partir de um script carregado em outro domínio malicioso.com sem nenhum erro/bloqueio de CORS, como enviamos a solicitação de mesma origem.

Os invasores podem facilmente enviar sua página maliciosa https://malicious.com/transfer-money-to-attacker-account-from-user-paypal-account convertendo-a em URL curto para ocultar a URL real. Quando o usuário clica em um link malicioso, o script carregado no domínio malicioso.com enviará uma solicitação de origem cruzada ao PayPal para transferir o valor do usuário para a conta do PayPal do invasor. Todos os usuários que fizeram login em sua conta do PayPal e clicaram neste link malicioso perderão seu dinheiro. Qualquer pessoa pode facilmente roubar dinheiro sem o conhecimento de um usuário de conta do PayPal.

Pelo motivo acima, os navegadores bloqueiam todas as solicitações de origem cruzada.

O que é CORS (Cross-Origin Resource Sharing)?

CORS é um mecanismo de segurança baseado em cabeçalho usado pelo servidor para informar ao navegador para enviar uma solicitação de origem cruzada de domínios confiáveis.
O servidor habilitado com cabeçalhos CORS usados ​​para evitar solicitações de origem cruzada bloqueadas pelos navegadores.

Como funciona o CORS?

Como o servidor já definiu seu domínio confiável em sua configuração CORS. Quando enviamos uma solicitação ao servidor, a resposta informará ao navegador que o domínio solicitado é confiável ou não em seu cabeçalho.

Existem dois tipos de solicitações CORS:

  • Solicitação simples
  • Solicitação de simulação

Pedido Simples:

  • O navegador envia a solicitação para um domínio de origem cruzada com origem(https://app.etechpt.com.com).
  • O servidor envia de volta a resposta correspondente com métodos permitidos e origem permitida.
  • Depois de receber a solicitação, o navegador verificará se o valor do cabeçalho de origem enviado (https://app.etechpt.com.com) e o valor do access-control-allow-origin recebido (https://app.etechpt.com.com) são os mesmos ou curinga

. Caso contrário, ele lançará um erro CORS.

  • Solicitação de simulação:
  • Dependendo do parâmetro de solicitação personalizada da solicitação de origem cruzada, como métodos (PUT, DELETE) ou cabeçalhos personalizados ou tipo de conteúdo diferente, etc. O navegador decidirá enviar uma solicitação OPTIONS de comprovação para verificar se a solicitação real é segura para enviar ou não.

Depois de receber a resposta (código de status: 204, que significa sem conteúdo), o navegador verificará os parâmetros de controle de acesso e permissão para a solicitação real. Se os parâmetros de solicitação são permitidos pelo servidor. A solicitação de origem cruzada real enviada e recebida

  Como os fones de ouvido VR funcionam sem um botão de ação?

Se access-control-allow-origin: *, a resposta é permitida para todas as origens. Mas não é seguro a menos que você precise.

Como habilitar o CORS?

Para habilitar o CORS para qualquer domínio, habilite os cabeçalhos CORS para permitir origem, métodos, cabeçalhos personalizados, credenciais, etc.

  • O navegador lê o cabeçalho CORS do servidor e permite solicitações reais do cliente somente após verificar os parâmetros da solicitação.
  • Access-Control-Allow-Origin: Para especificar domínios exatos (https://app.geekflate.com, https://lab.etechpt.com.com) ou curinga
  • Access-Control-Allow-Methods: Para permitir os métodos HTTP (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) que só precisamos.
  • Access-Control-Allow-Headers: para permitir apenas cabeçalhos específicos (autorização, csrf-token)
  • Access-Control-Allow-Credentials: valor booleano usado para permitir credenciais de origem cruzada (cookies, cabeçalho de autorização).

Access-Control-Max-Age: Diz ao navegador para armazenar em cache a resposta de comprovação por algum tempo.

Access-Control-Expose-Headers: Especifique os cabeçalhos que são acessíveis pelo script do lado do cliente.

Para habilitar o CORS no apache e no servidor web Nginx, siga este tutorial.

const express = require('express');
const app = express()

app.get('/users', function (req, res, next) {
  res.json({msg: 'user get'})
});

app.post('/users', function (req, res, next) {
    res.json({msg: 'user create'})
});

app.put('/users', function (req, res, next) {
    res.json({msg: 'User update'})
});

app.listen(80, function () {
  console.log('CORS-enabled web server listening on port 80')
})

Habilitando CORS no ExpressJS

Vamos dar um exemplo de aplicativo ExpressJS sem CORS:

npm install cors

No exemplo acima, habilitamos o endpoint da API dos usuários para os métodos POST, PUT, GET, mas não para o método DELETE.

Para facilitar a ativação do CORS no aplicativo ExpressJS, você pode instalar o cors

app.use(cors({
    origin: '*'
}));

Acesso-Controle-Permitir-Origem

app.use(cors({
    origin: 'https://app.etechpt.com.com'
}));

Habilitando CORS para todos os domínios

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ]
}));

Habilitando CORS para um único domínio

Se você deseja permitir CORS para origem https://app.etechpt.com.com e https://lab.etechpt.com.com

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST']
}));

Métodos de controle de acesso-permissão

Para habilitar CORS para todos os métodos, omita esta opção no módulo CORS no ExpressJS. Mas para habilitar métodos específicos (GET, POST, PUT).

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token']
}));

Cabeçalhos-Controle-Acesso-Permitir-

Usado para permitir que cabeçalhos diferentes dos padrões sejam enviados com solicitações reais.

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true
}));

Acesso-Controle-Permitir-Credenciais

Omita isso se você não quiser dizer ao navegador para permitir credenciais sob solicitação, mesmo se withCredentials estiver definido como true.

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600 
}));

Acesso-Controle-Max-Idade

Para intimar o navegador a armazenar em cache as informações de resposta de comprovação no cache por um segundo especificado. Omita isso se você não quiser armazenar em cache a resposta.

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600,
    exposedHeaders: ['Content-Range', 'X-Content-Range']
}));

A resposta de comprovação em cache ficará disponível por 10 minutos no navegador.

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600,
    exposedHeaders: ['*', 'Authorization', ]
}));

Cabeçalhos de acesso-controle-exposição

  Como alterar minha imagem do Google para GIF animado

Se colocarmos o curinga

em expostosHeaders, ele não irá expor o cabeçalho Authorization. Então, temos que expor explicitamente como abaixo

O acima irá expor todos os cabeçalhos e cabeçalho de autorização também.

  • O que é um cookie HTTP?
  • Um cookie é um pequeno pedaço de dados que o servidor enviará ao navegador do cliente. Em solicitações posteriores, o navegador enviará todos os cookies relacionados ao mesmo domínio em todas as solicitações.
  • Cookie tem seu atributo, que pode ser definido para fazer um cookie funcionar de forma diferente conforme a necessidade.
  • Nome Nome do cookie.
  • valor: dados do cookie relativos ao nome do cookie
  • Domínio: os cookies serão enviados apenas para o domínio definido
  • Caminho: cookies enviados somente após o caminho do prefixo de URL definido. Suponha que definimos nosso caminho de cookie como path=’admin/’. Cookies não enviados para o URL https://etechpt.com.com/expire/ mas enviados com o prefixo de URL https://etechpt.com.com/admin/
  • Max-Age/Expires(número em segundo): Quando o cookie deve expirar. Uma vida útil do cookie torna o cookie inválido após o tempo especificado. [Strict, Lax, None]HTTPOnly(Boolean): O servidor de backend pode acessar esse cookie HTTPOnly, mas não o script do lado do cliente quando verdadeiro. Secure(Boolean): Cookies enviados apenas por um domínio SSL/TLS quando verdadeiros.mesmoSite(string

): Usado para ativar/restringir cookies enviados em solicitações entre sites. Para saber mais detalhes sobre cookies SameSite consulte

MDN

. Aceita três opções Strict, Lax, None. Valor seguro do cookie definido como true para a configuração do cookie sameSite=None.

Por que cookie HTTPOnly para tokens?

Armazenar o token de acesso enviado do servidor no armazenamento do lado do cliente, como armazenamento local, banco de dados indexado e cookie (HTTPOnly não definido como verdadeiro) é mais vulnerável ao ataque XSS. Suponha que qualquer uma de suas páginas seja fraca para um ataque XSS. Os invasores podem fazer uso indevido de tokens de usuário armazenados no navegador.

Os cookies HTTPOnly são definidos/obtidos apenas pelo servidor/backend, mas não no lado do cliente.

  • Script do lado do cliente restrito para acessar esse cookie HTTPonly. Portanto, os cookies HTTPOnly não são vulneráveis ​​a ataques XSS e são mais seguros. Porque só é acessível pelo servidor.
  • Habilitar cookie HTTPOnly no back-end habilitado para CORS
  • Habilitar Cookie no CORS precisa da configuração abaixo na aplicação/servidor.
  • Defina o cabeçalho Access-Control-Allow-Credentials como true.

Access-Control-Allow-Origin e Access-Control-Allow-Headers não devem ser um curinga

const express = require('express'); 
const app = express();
const cors = require('cors');

app.use(cors({ 
  origin: [ 
    'https://app.geekflare.com', 
    'https://lab.geekflare.com' 
  ], 
  methods: ['GET', 'PUT', 'POST'], 
  allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], 
  credentials: true, 
  maxAge: 600, 
  exposedHeaders: ['*', 'Authorization' ] 
}));

app.post('/login', function (req, res, next) { 
  res.cookie('access_token', access_token, {
    expires: new Date(Date.now() + (3600 * 1000 * 24 * 180 * 1)), //second min hour days year
    secure: true, // set to true if your using https or samesite is none
    httpOnly: true, // backend only
    sameSite: 'none' // set to none for cross-request
  });

  res.json({ msg: 'Login Successfully', access_token });
});

app.listen(80, function () { 
  console.log('CORS-enabled web server listening on port 80') 
}); 

.

O atributo SameSite do cookie deve ser Nenhum.

Para habilitar o valor sameSite como none, defina o valor seguro como true: Habilite o back-end com certificado SSL/TLS para funcionar no nome de domínio.

Vamos ver um código de exemplo que define um token de acesso no cookie HTTPOnly após verificar as credenciais de login.

Você pode configurar cookies CORS e HTTPOnly implementando as quatro etapas acima em seu idioma de back-end e servidor web.

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.etechpt.com.com/user', true);
xhr.withCredentials = true;
xhr.send(null);

Você pode seguir este tutorial para apache e Nginx para habilitar o CORS seguindo as etapas acima.

fetch('http://api.etechpt.com.com/user', {
  credentials: 'include'
});

withCredentials for Cross-Origin request

$.ajax({
   url: 'http://api.etechpt.com.com/user',
   xhrFields: {
      withCredentials: true
   }
});

Credenciais (Cookie, Autorização) enviadas com a solicitação de mesma origem por padrão. Para origem cruzada, temos que especificar o withCredentials como true.

axios.defaults.withCredentials = true

API XMLHttpRequest

Buscar API

JQuery AjaxAxiosConclusão Espero que o artigo acima ajude você a entender como o CORS funciona e habilitar o CORS para solicitações de origem cruzada no servidor. Por que armazenar cookies em HTTPOnly é seguro e como withCredentials usado em clientes para solicitações de origem cruzada.