AWS API Gateway — CORS access-control-allow-origin — несколько записей

У меня есть экземпляр AWS Lambda, который подключается к определенному шлюзу API AWS. Если я включу CORS и дам access-control-allow-origin определение http://example.com, то я смогу получить доступ к экземпляру Lambda из http://example.com. Однако, если я использую https://example.com, это не сработает.

Итак, как в AWS определить использование нескольких значений access-control-allow-origin без использования подстановочного знака? Я пытался использовать что-то вроде *.example.com, но это не работает.

РЕДАКТИРОВАТЬ: Если я использую '*' в качестве значения на шлюзе API, но устанавливаю правила CORS в своей корзине S3, будет ли это безопасным? Пример правил корзины:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>http://example.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
    <CORSRule>
        <AllowedOrigin>https://example.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
    <CORSRule>
        <AllowedOrigin>https://www.example.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

person Wes    schedule 22.09.2016    source источник
comment
Та же проблема здесь. Моя ситуация заключается в том, что я должен использовать опцию withCredentials(), поэтому подстановочные знаки не разрешены. Возможно, мне придется обрабатывать заголовки cors самостоятельно, вместо того, чтобы позволить apigw обрабатывать их. Это так странно, что они предоставляют правила CORS для s3, но не для apigatway.   -  person Taichi    schedule 23.09.2016


Ответы (4)


К сожалению, сегодня это невозможно. Спецификация CORS не допускает частичных подстановочных знаков, и в настоящее время шлюз API допускает только одно статическое значение для заголовка.

Вы можете перегрузить свой метод OPTIONS, чтобы динамически возвращать это значение на основе входящего заголовка хоста.

person Bob Kinney    schedule 22.09.2016
comment
Спасибо, Боб. Я знаю, что не рекомендуется использовать * для значения (это работает, поскольку все допустимо), но для низкопрофильного сайта вы считаете, что это вызовет какие-либо проблемы? - person Wes; 22.09.2016
comment
@Wes, это зависит от уровня риска, который вы готовы принять. Если это просто вопрос http против https, вы, вероятно, можете решить эту проблему, всегда перенаправляя клиентов на https. С помощью Firefox и Chrome отказывается от поддержки http, это имеет смысл и не должно это слишком сложно. - person Bob Kinney; 22.09.2016

Это всегда раздражало CORS, если вы хотите включить несколько источников.

Обычный обходной путь в других системах (например, express/nginx и т. д.) заключается в следующем:

  • проверить заголовок Origin, отправленный браузером
  • проверьте его по белому списку источников
  • если он совпадает, вернуть входящий Origin в качестве заголовка Access-Control-Allow-Origin, иначе вернуть заполнитель (источник по умолчанию)

Это невозможно при использовании автоматической поддержки CORS AWS-Gateway, поскольку используется фиктивная интеграция, однако это возможно, если вы напишете собственный код для обработки запроса OPTIONS.

Ниже приведен пример кода, написанного с интеграцией лямбда-прокси:

const allowedOrigins = [
    "http://example.com",
    "http://example.com:8080",
    "https://example.com",
    "https?://[a-z]*.?myapp.com",
    "http://localhost:[0-9]*"
];

exports.handler = (event, context) => {
    const origin = event.headers.Origin || event.headers.origin;
    var goodOrigin = false;

    if (origin) {
        allowedOrigins.forEach( allowedOrigin => {
            if (!goodOrigin && origin.match(allowedOrigin)) {
                goodOrigin = true;
            }
        });
    }

    context.succeed({
        headers: {
            "Access-Control-Allow-Headers": "Accept,Accept-Language,Content-Language,Content-Type,Authorization,x-correlation-id",
            "Access-Control-Expose-Headers": "x-my-header-out",
            "Access-Control-Allow-Methods": "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT",
            "Access-Control-Allow-Origin": goodOrigin ? origin : allowedOrigins[0]
        },
        statusCode: 204
    });
};

Сохраните это как лямбда-функцию. Чтобы настроить это в API-Gateway, добавьте метод OPTIONS, а для Integration Request выберите Lambda Function с галочкой Use Lambda Proxy integration.

Конечно, недостатком этого является то, что вы платите за лямбда-функции, а вызов лямбда-функции, вероятно, будет дополнительной задержкой на 50 мс по сравнению с фиктивной интеграцией.

person Ravenscar    schedule 17.01.2017
comment
Я использую аутентификацию на основе файлов cookie, так как мы можем настроить эти основные заголовки для запроса GET без использования интеграции лямбда-прокси? - person Rajan Sharma; 22.06.2020

Почему бы не использовать шаблон сопоставления языка Velocity Template для проверки из списка разрешенных доменов и установки заголовка источника

$input.json("$")
#set($domains = ["https://www.example.com", "https://www.abcd.com"])
#set($origin = $input.params("origin"))
#if($domains.contains($origin))
#set($context.responseOverride.header.Access-Control-Allow-Origin="$origin")
#end
person Dhiraj Agarwal    schedule 01.02.2020
comment
Где это должно быть размещено и что должно быть в конфигурации ядра по умолчанию, предоставленной was? - person Rajan Sharma; 22.06.2020
comment
такая старая ветка, но все еще полезная, может быть, даже больше с последними проблемами HTST, где уже не так просто просто использовать файл локального хоста для имитации живого или тестового домена. Кроме того, этот ответ заслуживает гораздо большего количества голосов, чем ответы Lambda. Лямбда на этом расточительна, вы должны платить за каждый запрос OPTIONS, совершенно ненужный. Я только что столкнулся с проблемой, когда хочу, чтобы мой тестовый этап API был доступен с локального сервера разработки, а также через тестовый сервер Netlify. Использовал что-то подобное, поместил его в шаблоны сопоставления ответов в каждом методе OPTIONS, аналогично любому другому методу, использующему CORS. - person Mirko Vukušić; 29.06.2020
comment
Я использовал что-то подобное для разработки: я использовал этот подход: #if( $input.params('origin') == 'https://localhost:8080' && $context.stage == 'test') #set($context.responseOverride.header.Access-Control-Allow-Origin = #end Таким образом, значение по умолчанию переопределяется. - person Mirko Vukušić; 29.06.2020
comment
@RajanSharma в консоли AWS Api Gateway, параметры и методы публикации Интеграция ответов -> Content-Type: application/json -> Шаблон сопоставления - person Willie Z; 16.07.2020
comment
Спасибо за это! Работал отлично! - person Chris Phillips; 26.03.2021
comment
Это решение самое простое, спасибо. Меня интересовало одно: где я могу найти хороший справочник по языку шаблонов Velocity? Я бы никогда не узнал, что array.contains() была доступной функцией из этой ссылки, которая действительно скудна: velocity.apache.org/engine/devel/vtl-reference.html - person MattS; 14.04.2021
comment
Может ли кто-нибудь помочь объяснить, для чего используется первая строка: $input.json($)? Спасибо! - person user6318446; 21.07.2021

Я сделал что-то вроде этого:

const handler: APIGatewayProxyHandler = async (event) => {
  const origin = event?.headers?.Origin || event?.headers?.origin;
  const allowedOrigins = ['https://example.com'];
  const headers = {
    'Access-Control-Allow-Origin': allowedOrigins.includes(origin)
      ? origin
      : allowedOrigins[0],
  };

  return {
    headers,
    body: JSON.stringify({
      myResponse: 'data',
    }),
    statusCode: 200,
  };
};

Затем можно протестировать с помощью инструментов chrome dev, перейдя в свой клиентский домен и выполнив выборку в консоли:

fetch('https://exampleLambda.com/v1/example', { 
   method: 'get',
   mode: 'cors'
   headers: new Headers({
     'Authorization': 'Bearer 12345, 
   }), 
 })
 .then(result => result.json())
 .then(console.log)
person Bhetzie    schedule 28.05.2020