web-dev-qa-db-ja.com

AWS Cloudfront(WAF付き)+ API Gateway:Cloudfrontを介してアクセスを強制する方法は?

WAFをAPIGatewayの前に配置したいのですが、 (little)info これは、WAFを有効にした追加のCloudfrontディストリビューションをAPIGの前に手動で配置することによってのみ可能であることがわかりました。特にAPIGがカスタムドメインをネイティブにサポートするようになったため、少し残念ですが、機能するはずです。

ソリューションをあいまいにするのではなく安全にするために、APIはCloudfrontディストリビューションを介してのみアクセスできるように強制したいと思います。これを行うための最良のオプションは何ですか?

  • S3と同様の「オリジンアクセスID」を使用できることを望んでいましたが、その方法がわかりません。
  • IAMユーザー(またはロール?)をCloudfrontディストリビューションに割り当てることができれば、APIG IAM機能を使用できますが、これを行う方法がわかりません。
  • APIGでAPIキーを要求し、それをCloudfrontからOriginカスタムヘッダーとして渡すことができます。他の目的でAPIキーを使用したくない限り、それは機能する可能性があるので、私はそれについて完全に満足しているわけではありません。
  • ダミー(!)カスタムオーソライザーを使用できます。トークン検証式は、Cloudfrontからオリジンカスタムヘッダーとして渡されるシークレットを実際にチェックします。動作するはずです、それはより柔軟ですが、少し汚れています...またはそうではありませんか?

より良いアイデアはありますか?それとも、それを行うための「正しい方法」が存在しますが、私はそれを見落としましたか?

13
Free Willaert

私はAPIGatewayの出身です。

残念ながら、現時点での最善の解決策は、CloudFrontにOriginカスタムヘッダーを挿入し、それをカスタムオーソライザーで検証することです(質問のオプション4)。

私たちはすでにこの制限とそれほど素晴らしい回避策を認識していません。将来的にはより良いWAF統合を提供することを目指していますが、ETAはありません。

5
Balaji

「正しい」方法は、他の人が言及しているように、APIGatewayでカスタムオーソライザーを使用することです。

「安価な」方法は、APIキーである箇条書き3です。 ddos攻撃をかわそうとしている場合は、おそらくwaf-> cloudfront-> apiゲートウェイのみをプロビジョニングします。したがって、誰かがあなたのapiゲートウェイURLを発見し、クラウドフロントの代わりにそれをddosすることにした場合、カスタムオーソライザーはあなたがラムダへの攻撃の矢面に立たされていることを意味します。 Apiゲートウェイは1秒あたり10,000を超えるリクエストを処理でき、デフォルトのラムダ制限は1秒あたり100です。 Amazonに制限を引き上げてもらったとしても、持続的な攻撃に対して1秒あたり10kラムダを支払う用意はありますか?

AWSの担当者は、「APIキーは認証用ではなく、識別用です。キーはリクエストの署名には使用されないため、セキュリティメカニズムとして使用しないでください」と通知します https://aws.Amazon.com/ blogs/aws/new-usage-plans-for-Amazon-api-gateway /

しかし、正直なところ、ラムダで巨大なごちゃ混ぜの文字列を検証するよりも良いことをするつもりがないのなら、その負担とコストを他の誰かに任せてみませんか。 (最大キー長は128文字です)

たぶん、新しいapiキーを発行し、6時間ごとにcloudfrontのヘッダーを更新するようにスケジュールされたラムダ関数を使用できますか?

他の目的でAPIキーを使用する場合は、認証用に1つのAPIゲートウェイOriginを使用し、その他すべてに別のOriginとAPIゲートウェイを使用します。このように、ddos攻撃では、認証APIに対して毎秒10kのリクエストを処理できますが、すでにログインしている他のすべての顧客は、APIを使用するために毎秒10kの集合を持っています。 Cloudfrontとwafは1秒あたり100Kを処理できるため、このシナリオであなたを妨げることはありません。

APIゲートウェイの背後でラムダを使用している場合は、lambda @ Edgeを使用して、APIゲートウェイをすべてスキップすることができます。 (lambda @ Edgeは厳しく制限されているため、これはほとんどのシナリオに適合しませんが、私はそれをそこに捨てると思いました。)

しかし、最終的にはWE APIGATEWAYとのWAF統合が必要です!! :)

3
Neo

カスタムドメイン名を使用して、DNSがWAFを使用してディストリビューションを指すようにすることができます。その場合、元のAPIGatewayディストリビューションへの直接のリクエストは機能しません。

0

Lambda @ Edge関数 for SigV4 Originリクエストに署名し、API GatewayでIAM認証を有効にすることで、CloudFrontを介したアクセスを強制することができます。このストラテジーは、CloudFrontディストリビューションのAPIキーと組み合わせて使用​​できます( CloudFront + APIキーのガイド )。

すでにCloudFrontディストリビューションのオリジンとしてAPIGatewayをセットアップしていると仮定すると、最初にLambda @ Edge関数( Lambda @ Edgeセットアップのガイド )を作成してから、その実行ロールがにアクセスできることを確認する必要がありますアクセスしたいAPIゲートウェイ。簡単にするために、Lambdaの実行ロールでAmazonAPIGatewayInvokeFullAccessマネージドIAMポリシーを使用して、アカウント内の任意のAPIGatewayを呼び出すためのアクセス権を与えることができます。

次に、署名クライアントとして aws4 を使用する場合、Lambda @Edgeコードは次のようになります。

const aws4 = require("aws4");

const signCloudFrontOriginRequest = (request) => {
  const searchString = request.querystring === "" ? "" : `?${request.querystring}`;

  // Utilize a dummy request because the structure of the CloudFront Origin request
  // is different than the signing client expects
  const dummyRequest = {
    Host: request.Origin.custom.domainName,
    method: request.method,
    path: `${request.Origin.custom.path}${request.uri}${searchString}`,
  };

  // Include the body in the signature if present
  if (Object.hasOwnProperty.call(request, 'body')) {
    const { data, encoding } = request.body;
    const buffer = Buffer.from(data, encoding);
    const decodedBody = buffer.toString('utf8');

    if (decodedBody !== '') {
      dummyRequest.body = decodedBody;
      dummyRequest.headers = { 'content-type': request.headers['content-type'][0].value };
    }
  }

  // Use the Lambda's execution role credentials
  const credentials = {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    sessionToken: process.env.AWS_SESSION_TOKEN
  };

  aws4.sign(dummyRequest, credentials); // Signs the dummyRequest object

  // Sign a clone of the CloudFront Origin request with appropriate headers from the signed dummyRequest
  const signedRequest = JSON.parse(JSON.stringify(request));
  signedRequest.headers.authorization = [ { key: "Authorization", value: dummyRequest.headers.Authorization } ];
  signedRequest.headers["x-amz-date"] = [ { key: "X-Amz-Date", value: dummyRequest.headers["X-Amz-Date"] } ];
  signedRequest.headers["x-amz-security-token"] = [ { key: "X-Amz-Security-Token", value: dummyRequest.headers["X-Amz-Security-Token"] } ];

  return signedRequest;
};

const handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;
  const signedRequest = signCloudFrontOriginRequest(request);

  callback(null, signedRequest);
};

module.exports.handler = handler;

リクエストに本文を含める場合は、コンソールまたはSDKを介して本文を含めるようにLambda @ Edge関数を手動で構成するか、 CloudFormationカスタムリソース)を設定する必要があることに注意してください 以来SDKを呼び出す CloudFormationはこれをネイティブに有効にすることをまだサポートしていません

0
Reed Hermes