Para um desenvolvedor experiente, esse texto pode não ser nada surpreendente, mas acho que muitos artigos que li sobre a configuração CORS no Rails diziam algo como: use rack-cors, permita que qualquer host acesse a API e (opcionalmente): você deve considerar algo diferente (do que permitir qualquer host) em produção.
A proposta código estava sempre perto do que estava por baixo:
config/initializers/cors.rb
Carris.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origens ''
resource '', headers: :any, methods: :any
fim
fim
e, infelizmente, estes textos eram pouco explicativos para nós o que fazer efetivamente na produção.
Não tenho problemas em copiar e colar (Por vezes brinco com o facto de as empresas poderem contratar um copy-paster do Stack Overflow), na medida em que há um momento de "pensar e ajustar" entre "copiar" e "colar". Por isso, gostaria de explicar um pouco melhor o que estamos a fazer aqui e como funciona na vida real.
Espero que não se importem por eu começar com uma breve introdução à teoria da honra e depois passar para os exemplos de Rails.
Introdução
Vamos começar pelo princípio. Para explicar melhor as coisas, dividi a introdução em três partes. A primeira parte descreve o que é uma origem - o termo-chave para o que estamos a discutir aqui. A segunda é sobre o SOP, apenas uma breve descrição. E a última parte fala sobre o CORS por si só.
O que é uma origem?
De acordo com os documentos Web da MDN:
- A origem do conteúdo Web é definida pelo esquema (protocolo), anfitrião (domínio) e porta do URL utilizado para aceder ao mesmo. Dois objectos têm a mesma origem apenas quando o esquema, o anfitrião e a porta coincidem (fonte)
Parece bastante claro, não é? Vamos analisar dois exemplos da MDN, por via das dúvidas.
http://example.com/app1/index.html, http://example.com/app2/index.html
Os 2 acima têm a mesma origem porque:
- os seus esquemas (http) são os mesmos,
- os seus domínios (exemplo.com) são os mesmos,
- os seus portos (implícitos) são os mesmos.
http://www.example.com, http://myapp.example.com
Estes 2 têm origem diferente porque os domínios (www.example.com, myapp.example.com) são diferentes.
Espero que seja suficientemente claro. Se não, por favor, vá para os MDN Web Docs para mais exemplos.
O que é o SOP?
Os documentos Web da MDN dizem (fonte):
- A política de mesma origem é um mecanismo de segurança crítico que restringe a forma como um documento ou script carregado de uma origem pode interagir com um recurso de outra origem. Ajuda a isolar documentos potencialmente maliciosos, reduzindo possíveis vectores de ataque.
- Normalmente, são permitidas escritas entre origens. Exemplos são links, redireccionamentos e submissões de formulários.
- Normalmente, é permitida a incorporação entre origens.
- Normalmente, as leituras entre origens não são permitidas, mas o acesso de leitura é muitas vezes objeto de fuga por incorporação.
Utilização CORS para permitir o acesso entre origens
Bem, como pode ver, há muito sobre comportamento entre origens nas definições de SOP. Não faz mal. Tudo o que devemos saber agora é que a mesma origem tem mais privilégios e podemos afrouxar as regras para origens cruzadas usando CORS. E é aqui que entra a próxima secção.
O que é o CORS?
Com base nas palavras da MDN:
- A partilha de recursos entre origens (CORS) é um mecanismo baseado no cabeçalho HTTP que permite a um servidor indicar quaisquer outras origens (domínio, esquema ou porta) que não a sua, a partir das quais um navegador deve permitir o carregamento de recursos. O CORS também se baseia num mecanismo através do qual os navegadores fazem um pedido de "preflight" ao servidor que aloja o recurso de origem cruzada, a fim de verificar se o servidor permitirá o pedido efetivo. Nesse preflight, o navegador envia cabeçalhos que indicam o método HTTP e os cabeçalhos que serão utilizados no pedido efetivo (fonte).
Isso ainda não é suficiente. O que não foi dito explicitamente é que o cabeçalho mais importante quando se utiliza CORS é Controlo de acesso - Permitir origem:
- O
Controlo de acesso - Permitir origem indica se a resposta pode ser partilhada com o código requerente da origem indicada (fonte).
Bem, deve ser isso. Na vida real, ao configurar o CORS, normalmente configuramos o ACAO cabeçalho primeiro.
Vida real
Isso é tudo quando se trata de definições. Vamos voltar ao Rails e aos exemplos da vida real.
Como configurar o CORS no Rails?
Utilizaremos de certeza os rack-cors (como nos foi dito). Recordemos o primeiro excerto, aquele que é mais frequentemente fornecido noutros artigos:
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origens ''
resource '', headers: :any, methods: :any
fim
fim
O número de opções é vasto ou mesmo infinito, mas consideremos estas duas:
- estamos a construir a API que pode ser utilizada por clientes de browsers de terceiros,
- temos uma separação típica entre frontend e backend e queremos permitir que os nossos clientes de confiança acedam à API.
API de construção acedida por clientes terceiros
Se estiver perante a primeira opção, pode provavelmente optar por origens '*' - quer que outros criem um cliente com base na sua API e não sabe quem são, certo?
Separação típica entre frontend e backend
Se estiver a desenvolver este último, provavelmente não quer que toda a gente faça pedidos de origem cruzada à sua API. O que pretende é:
- permitir que os clientes de produção acedam à API de produção,
- o mesmo para a preparação,
- o mesmo para o localhost,
- poderá querer permitir que as aplicações de revisão FE acedam ao staging.
Continuaremos a utilizar as cintas de cremalheira (como nos disseram) - mas à nossa maneira.
Vamos utilizar 2 variáveis ENV: ORIGENS_PERMITIDAS para definições de origem literais (um asterisco ou URL real) e REGEXPS_DE_ORIGEM_PERMITIDOS para os padrões.
config/initializers/cors.rb
frozenstringliteral: true
toregexp = ->(string) { Regexp.new(string) }
hosts = [
*ENV.fetch('ALLOWEDORIGINS').split(','),
*ENV.fetch('ALLOWEDORIGINREGEXPS').split(';').map(&to_regexp)
]
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origens(*hosts)
recurso '*',
métodos: %i[get post put patch delete options head],
cabeçalhos: :any
fim
fim
O que é que se passa aqui?
- Como pode ver, estamos a dividir os valores definidos nas variáveis ENV com separadores diferentes. Isso acontece porque é menos provável que um ponto-e-vírgula apareça no padrão de definição de URL.
- Os valores literais estão prontos para serem utilizados, mas temos de mapear os padrões para serem instâncias reais de Regexp.
- Depois, estamos a juntar tudo e a permitir que estes anfitriões acedam a qualquer recurso com métodos da lista branca que a nossa API utiliza.
Isto deve dar-lhe flexibilidade suficiente para definir valores adequados nos seus ambientes de desenvolvimento, preparação e produção.
Conclusões
Vamos resumir tudo o que foi dito acima em pontos-chave:
- utilizar variáveis ENV para configurar
CORS,
- utilizar expressões regulares para permitir que diferentes origens acedam à API de preparação (por exemplo, para aplicações de revisão),
- colocar sempre "pensar e ajustar" entre "copiar" e "colar".
É isso aí. Tenham um bom dia! 🙂
Ler mais:
Porque é que deve (provavelmente) utilizar Typescript?
10 Startups de Nova Iorque que vale a pena mencionar em 2021