【AWS】API Gateway Lambdaオーソライザー「User is not authorized to access this resource」エラーの原因と対応


上記のようなAPIGatewayで構築したAPIにLambdaオーソライザーを適用した際に、正しい認証情報、正しいAWSリソースへのアクセス権限を設定しているにも関わらず「User is not authorized to access this resource」エラーが発生することがあります。
本記事ではその原因と対応を記載します。

原因

Lambdaオーソライザーから返却される認可情報のキャッシングが原因です。

以下のようなアクセスポリシーを返却するコードを記述した際に発生します。

node.js
  ...
  statementOne.Action = 'execute-api:Invoke'; 
  statementOne.Effect = 'Allow';
  // ↓ 以下のアクセス承認するリソースを指定するコードが原因
  statementOne.Resource = event.methodArn;
  ...

ポイントはevent.methodArnです。
ここでは呼び出しされたAPIのみのArnが含まれています。
例: arn:aws:execute-api:ap-northeast-1:xxxxxxxxxxx:xxxxxxxxx/stg/GET/users/43/info

このArnへのアクセス権限を許可したポリシーを返却すると、初回のアクセスはうまくいきます。

しかし同じLambdaオーソライザーが適用されている他のAPIを呼び出した際に、上記のArnが含まれたポリシーがキャッシュとして返却されてしまい、初回アクセスとは別の認可されていないリソースにアクセスすると「User is not authorized to access this resource」エラーが発生します。

対応

2パターンの対応方法があります。

パターン1Lambdaオーソライザーの認可のキャッシュのTTL値を0に設定

API Gatewayのオーソライザーから対象のオーソライザーを選択して、

TTLを0に変更すると、毎回認証を実行するためevent.methodArnをアクセス承認リソースとして返却しても、問題なく認可が通ります。
ただ毎回認証処理が走るため、パフォーマンス的には優れません
その点が気になる場合は、後述のパターン2を利用することを推奨します。

※ AWS CloudFormation, AWS SAMを利用する場合の注意点
CloudFormation、SAMを利用してLambdaオーソライザーを設定した場合、デフォルトでは認可のキャッシュのTTL値は300秒です。
認可キャッシュのTTL値を変更せずにデプロイしたオーソライザーの設定値をマネジメントコンソールから確認すると...

こんな感じでundefinedとなっており、キャッシュが無効になっているように見えます。
しかし実際には300秒のキャッシュ設定が有効になっています。
(これに1時間くらいハメられました...)

TTLを0に設定する際には、ReauthorizeEveryを0に設定しましょう。

template.yaml
  TestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prd
      Auth:
        AddDefaultAuthorizerToCorsPreflight: false
        DefaultAuthorizer: MyLambdaCustomAuthorizer
        Authorizers:
          MyLambdaCustomAuthorizer:
            FunctionPayloadType: REQUEST
            FunctionArn:
              Fn::GetAtt:
              - RestApiAuthFunction
              - Arn
            Identity:
              QueryStrings:
              - authToken
              ReauthorizeEvery: 0

パターン2 Lambdaオーソライザー側で返却するリソースポリシーを変更

Lambdaオーソライザー側でevent.methodArnを指定するのではなく、ワイルドカードを指定して対応する方法です。

node.js
  ...
  statementOne.Action = 'execute-api:Invoke'; 
  statementOne.Effect = 'Allow';
  // ↓ ワイルドカード指定に変更
  statementOne.Resource = 'arn:aws:execute-api:*';
  ...

上記例では、全Lambdaの実行権限を返却しています。
こうすることでポリシーのキャッシュが行われていても、ワイルドカードで許可されたリソースは問題なくコールすることが可能です。
どこまでをワイルドカード指定にするかは、自身のセキュリティポリシーと相談してみてください。

キャッシュと併用することができるため、パターン1よりもパフォーマンス的に優れていますが、ワイルドカード指定になる分、セキュリティとトレードオフです。

まとめ

  • 1つのオーソライザーを複数のリソースで共有した際に、オーソライザーの認可キャッシュが有効になっていると「User is not authorized to access this resource」が発生する。
  • オーソライザーの認可情報のキャッシュが原因であるため、TTLを0にするか認可リソースを拡大することで対応可能

記載情報に誤りがあったらご指摘いただけると助かります。

参考リンク