IBM Cloud Docs
O CORS e as solicitações do CORS por meio de seu CDN

O CORS e as solicitações do CORS por meio de seu CDN

O Cross Origin Resource Sharing (CORS) é um mecanismo que é usado pelos navegadores, principalmente para validar permissões para acesso ao conteúdo de uma origem diferente.

O que é CORS?

Quando um navegador carrega uma página da web, ele aplica a Política de mesma origem, o que significa que ele só permite que o conteúdo seja buscado da mesma origem que o da página da web. No entanto, em alguns casos, uma página da web pode precisar de acesso a ativos de diversas origens que confiam nesse website. Aqui é onde o CORS entra.

Esse mecanismo de segurança existirá apenas se um aplicativo tiver ou for um cliente HTTP e se implementar o CORS. Quase todos os navegadores modernos, como Chrome, Firefox e Safari, implementam o CORS.

Para esclarecer, uma origem com relação ao CORS não precisa ser a mesma que uma origem de CDN. Uma origem no CORS é definida por um esquema de URI, um domínio e qualquer número de porta possível. Por exemplo, https://www.example.com:1443 é uma origem diferente de http://www.example.com. E assim, um CDN também pode ser considerado uma origem do CORS na perspectiva de um navegador.

Como o CORS funciona

O CORS pode lidar com dois tipos de solicitações:solicitações simples e solicitações simuladas, que são mais complexos.

Solicitações simples

Primeira solicitação (acesso ao recurso):

cors-simple
Figura 1: Primeira solicitação (acesso ao recurso)
.

Solicitações simples por meio de CORS são solicitações GET ou POST da página da Web de uma origem que está tentando obter acesso à URL de outra origem para recursos

Ao fazer essa solicitação, o navegador automaticamente configura os cabeçalhos de solicitação do CORS. Principalmente, ele configura a origem da página da web fazendo a solicitação no cabeçalho HTTP Origin automaticamente. A solicitação CORS também pode conter um conjunto de determinados cabeçalhos HTTP padrão, enquanto mantém seu status como uma solicitação CORS simples, a partir da perspectiva do navegador.

O servidor que recebe a solicitação do CORS processa a solicitação e pode enviar um conjunto de cabeçalhos de resposta do CORS de volta para o navegador, com o conteúdo solicitado. Esses cabeçalhos de resposta do CORS contêm valores que especificam se a página da web atual tem permissão de acesso a esses recursos, se os cabeçalhos enviados são aceitáveis para essa solicitação e assim por diante.

Se o navegador não conseguir ver sua solicitação do CORS atendida pelos cabeçalhos de resposta do CORS, ele evitará automaticamente o acesso e o carregamento do conteúdo. Caso contrário, ele verá que a origem do CORS está fornecendo permissão para usar o recurso e permitirá o acesso e o carregamento do conteúdo solicitado.

Solicitações simuladas

Primeira solicitação (simulação):

cors-preflight
Figura 2: primeira solicitação (preflight)

Segunda solicitação (acesso ao recurso):

cors-after-preflight
Figura 3: Segunda solicitação (acesso ao recurso)
.

Para uma comunicação CORS mais complexa entre o navegador e uma origem CORS diferente da página da Web solicitante, uma solicitação de simulação é necessária antes de um acesso de recurso real. Certas situações poderão requerer solicitações do CORS de simulação, como métodos de HTTP que não são métodos GET ou POST, ou o uso de cabeçalhos de HTTP não padrão com a solicitação - mesmo se for uma solicitação GET ou POST, etc.

Caso uma solicitação de simulação seja necessária, eis como os eventos se desdobram:

  • O navegador envia uma solicitação usando o método HTTP OPTIONS para o servidor com todos os cabeçalhos de solicitação de CORS desejados.
  • O servidor processa os cabeçalhos de solicitação do CORS e pode responder com cabeçalhos de resposta do CORS não contendo dados reais do conteúdo.
  • O navegador verifica os cabeçalhos de resposta do CORS para certificar-se de que a solicitação do CORS seja permitida.
  • Se o navegador perceber que a solicitação de recurso desejada deve ser permitida pelo servidor, ele fará uma segunda solicitação para o navegador com o método HTTP desejado, seja GET, POST, PUT, e assim por diante, com os mesmos cabeçalhos de solicitação do CORS.

Posteriormente, a comunicação entre o navegador e a origem do CORS (diferente daquele da página da web) continuará como se fosse uma solicitação simples do CORS. Semelhante a uma solicitação simples do CORS, o conteúdo e os recursos estarão acessíveis e poderão ser carregados se essa segunda solicitação do CORS for permitida.

Configurando o CORS na sua origem

Conforme mostrado nos diagramas anteriores, o CORS é iniciado pelo cliente HTTP solicitante. No entanto, os efeitos dependem da origem solicitada. Para que seu conteúdo esteja pronto para solicitações do CORS, sua origem deve ser configurada corretamente para responder com os cabeçalhos de resposta do CORS corretos e as permissões de acesso corretas.

O exemplo a seguir mostra uma configuração básica do CORS para um servidor Nginx:

http {
    # some http context configs

    server {
        # some server context configs

        # URI path to some content
        location /my-static-content {

            # some location context configs

            # Handle simple requests
            #
            # Consider only "HTTP GET" requests (content fetching)
            if ($request_method = 'GET') {

                # Allows the browser to access data from this server during CORS,
                # only if the request comes from a web page from the following origin
                add_header 'Access-Control-Allow-Origin' 'https://www.example.com';
            }

            # Handle preflight requests
            if ($request_method = 'OPTIONS') {

                # Allows the browser to access data from this server during CORS,
                # only if the request comes from a web page from the following origin
                add_header 'Access-Control-Allow-Origin' 'https://www.example.com';

                # Allows only GET requests
                add_header 'Access-Control-Allow-Methods' 'GET';

                # Allows the following headers in the browser's request headers
                # that may have been added by anything other than what the browser had added automatically
                add_header 'Access-Control-Allow-Headers' 'pragma';

                # Specifies to the browser how long it should cache this preflight response
                add_header 'Access-Control-Max-Age' 1728000;

                # HTTP 204 response code means success,
                # but also that it should expect no content from this (preflight) response
                return 204;
            }

            # more location context configs

            # If it is not a complex CORS situation requiring a preflight response,
            # then finally attempt to serve the file whose path falls under this location block's URI path match
            #
            # If no such file is found, then present an HTTP 404 code (not found)
            try_files $uri =404;
        }

        # more server context configs
    }

    # more http context configs
}

Geralmente, o navegador deve carregar livremente o conteúdo quando vir um Access-Control-Allow-Origin: * nos cabeçalhos de resposta CORS do servidor de acordo com a especificação w3 referente a esse valor curinga. No entanto, nem todos os navegadores suportam Access-Control-Allow-Origin: *.

Se o servidor tiver que suportar o acesso de diversas páginas da web, cada uma entregue de uma origem diferente, um único valor de origem para Access-Control-Allow-Origin deverá ser gerado dinamicamente por solicitação. Veja a seguir um exemplo básico de tal caso de uso para um servidor Nginx:

http {
    # some http context config

    ######
    # Input from $http_origin,          the value in Origin HTTP header
    # Output to $cors_allowed_origin,   a string
    #
    # Full string match on the Origin header value
    #
    # Attempt to find a match for the value from $http_origin using regex on the left column.
    # If a match is found, then evaluate $cors_allowed_origin to the same value as $http_origin
    # Else, default evaluate $cors_allowed_origin to an empty string, so browsers will disallow the CORS request
    ######
    map $http_origin $cors_allowed_origin {
        default '';
        ~^http(s)?://(www|www2|cdn|dev)\.example\.com$ $http_origin;
    }

    server {
        # some server context configs

        # URI path to some content
        location /my-static-content {

            # some location context configs

            # Handle simple requests
            if ($request_method = 'GET') {
                add_header 'Access-Control-Allow-Origin' $cors_allowed_origin;
            }

            # Handle preflight requests
            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' '$cors_allowed_origin;
                add_header 'Access-Control-Allow-Methods' 'GET';
                add_header 'Access-Control-Allow-Headers' 'pragma';
                add_header 'Access-Control-Max-Age' 1728000;

                return 204;
            }

            # more location context configs

            try_files $uri =404;
        }

        # more server context configs
    }

    # more http context configs
}

O exemplo anterior usa a diretiva map para evitar o uso excessivo da instrução do Nginx if. Agora, quando uma solicitação do CORS é feita para esse servidor e corresponde a esse caminho de URI, o servidor responde com o cabeçalho Access-Control-Allow-Origin contendo o valor http://www.example.com, https://cdn.example.com ou http://dev.example.com, quando o conteúdo é solicitado de http://www.example.com, https://cdn.example.com, http://dev.example.com e assim por diante.

Configurando o CORS para CDN

cors-through-cdn
Figura 4: CORS para CDN

O CDN é basicamente transparente para a configuração do CORS da origem, portanto, ele não requer uma configuração específica do CDN. Se o servidor de borda do CDN não puder localizar uma resposta em cache para a primeira solicitação de algum conteúdo, ele encaminhará a solicitação para o host de origem. Se o host de origem for configurado para tratar das solicitações do CORS e essa solicitação tiver o cabeçalho Origin, ele deverá responder de volta à borda com um cabeçalho do CORS de Access-Control-Allow-Origin, além do valor associado. A resposta geral, incluindo esse cabeçalho e o valor, será armazenada em cache no CDN. Qualquer solicitação subsequente para o objeto no mesmo caminho de URI é entregue por meio do cache e inclui o valor do cabeçalho Access-Control-Allow-Origin recebido originalmente da origem.

Suporte para diversas origens do CORS

Em alguns casos, é possível permitir uma lista de origens específicas (não todas) para acessar os conteúdos do CDN e precisar do CDN para entregar cabeçalhos de resposta Access-Control-Allow-Origin diferentes para origens diferentes (não curinga * para qualquer origem). No entanto, o CDN armazena em cache os cabeçalhos junto com os conteúdos, portanto, ele pode entregar o cabeçalho em cache Access-Control-Allow-Origin para a solicitação, que pode não corresponder.

É possível utilizar a Otimização de consulta da chave de cache para incluir parâmetros diferentes nas URLs de origens diferentes. Dessa maneira, o conteúdo e os cabeçalhos são armazenados em cache de forma diferente.

Por exemplo, suponha que você tenha configurado o CDN cdn.example.com para entregar conteúdos para as duas origens (abc.com e 123.com) e queira incluir parâmetros diferentes para cada origem, por exemplo, https://cdn.example.com/test.json?domain=abc.com e https://cdn.example.com/test.json?domain=123.com. O CDN retornaria cabeçalhos Access-Control-Allow-Origin diferentes que são entregues por seu servidor de back-end:

A solicitação da origem abc.com retorna access-control-allow-origin: https://abc.com:

# curl -H "Origin: https://abc.com" -H "Referer: https://abc.com/" -i https://cdn.example.com/test.json?domain=abc.com
HTTP/2 200
access-control-allow-origin: https://abc.com
access-control-allow-methods: GET
access-control-allow-credentials: true
...

A solicitação da origem 123.com retorna access-control-allow-origin: https://123.com:

# curl -H "Origin: https://123.com" -H "Referer: https://123.com/" -i https://cdn.example.com/test.json?domain=123.com
HTTP/2 200
access-control-allow-origin: https://123.com
access-control-allow-methods: GET
access-control-allow-credentials: true
...

Resolução de problemas de CORS e de solicitações de CORS

Se seu servidor de origem estiver configurado para o CORS e você não vir o cabeçalho Access-Control-Allow-Origin retornado à solicitação do navegador, possivelmente o cabeçalho de resposta armazenado em cache no CDN era destinado a uma solicitação que não tinha o cabeçalho de origem na solicitação. O CDN armazena em cache os cabeçalhos de resposta do host de origem. No entanto, os cabeçalhos em cache se baseiam na solicitação que acionou a solicitação para a origem. Nesse caso, os cabeçalhos de resposta podem não incluir os cabeçalhos do CORS. Limpe o cache no CDN para esse caminho usando a funcionalidade de limpeza do CDN e tente a solicitação por meio do cliente novamente.

O cabeçalho de resposta Vary do seu servidor de origem também pode causar um comportamento inesperado ao buscar conteúdo por meio do CDN. Diferentemente de uma exceção específica, o CDN não armazenará em cache o conteúdo (e o respectivo cabeçalho de resposta associado) de seu servidor de origem se o servidor responder com um cabeçalho Vary. Atualmente, nosso serviço removerá o cabeçalho Variar da origem para renderizar o objeto armazenável em cache, se o objeto for um dos tipos de arquivo a seguir: aif, aiff, au, avi, bin, bmp, cab, carb, cct, cdf, class, css, doc, dcr, dtd, exe, flv, gcf, gff, gif, grv, hdml, hqx, ico, ini, jpeg, jpg, js, mov, mp3, nc, pct, pdf, png, ppc, pws, swa, swf, txt, vbs, w32, wav, wbmp, wml, wmlc, wmls, wmlsc, xsd, zip, webp, jxr, hdp, wdp, pict, tif, tiff, mid, midi, ttf, eot, woff, otf, svg, svgz, jar, woff2, json. Se o objeto a ser armazenado em cache não for um desses tipos de arquivo, remova o cabeçalho Vary da resposta do servidor de origem para esse objeto e tente novamente depois de usar as funções de limpeza do CDN.

Informações adicionais