キャッシュが有効になっている Lambda オーソライザーを使用している API Gateway プロキシリソースから、HTTP 403 エラー "ユーザーはこのリソースへのアクセスを許可されていません" が返される理由を知りたいです。

所要時間4分
0

キャッシュが有効になっている AWS Lambda オーソライザーを使用している Amazon API Gateway プロキシリソースから、次の HTTP 403 エラーメッセージが返されます。 "ユーザーはこのリソースへのアクセスを許可されていません"

簡単な説明

注: API Gateway は、さまざまな理由で 403 エラー「ユーザーはこのリソースへのアクセスを許可されていません」というエラーを返すことがあります。この記事では、キャッシュのみが有効になっている Lambda オーソライザーの API Gateway プロキシリソースに関連する 403 エラーを扱います。他の種類の 403 エラーのトラブルシューティングについては、「API Gateway での HTTP 403 エラーをトラブルシューティングする方法を教えてください」を参照してください。

Lambda オーソライザーの出力は、AWS Identity and Access Management (IAM) ポリシーを API Gateway に返します。IAM ポリシーには、次の形式の明示的な API Gateway API リソース 要素が含まれています。

"arn:aws:execute-api:<region>:<account>:<API_id>/<stage>/<http-method>/[<resource-path-name>/[<child-resources-path>]"

認証キャッシュが Lambda オーソライザーで有効になっている場合、返された IAM ポリシーがキャッシュされます。その後、キャッシュされた IAM ポリシーは、キャッシュの指定された有効期間 (TTL) 期間内に行われる追加の API リクエストに適用されます。

API に、{proxy+} の greedy パス変数があるプロキシリソースが含まれる場合、最初の認証は成功します。キャッシュの TTL 期間内に別のパスに対して追加の API リクエストが行われると失敗し、次のエラーが返されます。

"message": "ユーザーはこのリソースへのアクセスを許可されていません"

パスがキャッシュされた IAM ポリシーで定義されている明示的な API Gateway API のリソース 要素と一致しないため、追加のリクエストは失敗します。

Lambda オーソライザー関数のコードを変更し、代わりにワイルドカード (*/*) リソースを出力で返すことで、この問題を解決できます。詳細については、「Lambda アクションのリソースと条件」を参照してください。

: オーソライザーのキャッシュを有効にするには、オーソライザーが API Gateway のすべてのメソッドに適用されるポリシーを返す必要があります。すべてのリソースを許可するには、Lambda オーソライザー関数のコードが出力でワイルドカード (*/*) リソースを返す必要があります。キャッシュポリシーは、同じリソースパスで同じリクエストを 2 回行った場合を除き、同じリソースパスがキャッシュされることを想定します。

解決策

注: この記事の Lambda オーソライザー関数のコードスニペットの例を、ユースケースに合わせて変更してください。

次の設定例では、Lambda 関数はメソッドの Amazon Resource Name (ARN) ("event.methodArn") から、API Gateway の idを抽出します。次に、関数はワイルドカードの "Resource" 変数を、メソッド ARN のパスと API の id 値およびワイルドカード (*/*) と組み合わせることで定義します。

ワイルドカードの "Resource" 変数を返すトークンベースの Lambda オーソライザー関数コード例

exports.handler =  function(event, context, callback) {
    var token = event.authorizationToken;
    var tmp = event.methodArn.split(':');
    var apiGatewayArnTmp = tmp[5].split('/');

    // Create wildcard resource
    var resource = tmp[0] + ":" + tmp[1] + ":" + tmp[2] + ":" + tmp[3] + ":" + tmp[4] + ":" + apiGatewayArnTmp[0] + '/*/*';
    switch (token) {
        case 'allow':
            callback(null, generatePolicy('user', 'Allow', resource));
            break;
        case 'deny':
            callback(null, generatePolicy('user', 'Deny', resource));
            break;
        case 'unauthorized':
            callback("Unauthorized");   // Return a 401 Unauthorized response
            break;
        default:
            callback("Error: Invalid token"); // Return a 500 Invalid token response
    }
};
// Help function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
    var authResponse = {};

    authResponse.principalId = principalId;
    if (effect && resource) {
        var policyDocument = {};
        policyDocument.Version = '2012-10-17';
        policyDocument.Statement = [];
        var statementOne = {};
        statementOne.Action = 'execute-api:Invoke';
        statementOne.Effect = effect;
        statementOne.Resource = resource;
        policyDocument.Statement[0] = statementOne;
        authResponse.policyDocument = policyDocument;
    }

    // Optional output with custom properties of the String, Number or Boolean type.
    authResponse.context = {
        "stringKey": "stringval",
        "numberKey": 123,
        "booleanKey": true
    };
    return authResponse;
}

ワイルドカードの "Resource" 変数を返すリクエストパラメータベースの Lambda オーソライザー関数コード例

exports.handler = function(event, context, callback) {        

    // Retrieve request parameters from the Lambda function input:
    var headers = event.headers;
    var queryStringParameters = event.queryStringParameters;
    var pathParameters = event.pathParameters;
    var stageVariables = event.stageVariables;

    // Parse the input for the parameter values
    var tmp = event.methodArn.split(':');
    var apiGatewayArnTmp = tmp[5].split('/');

    // Create wildcard resource
    var resource = tmp[0] + ":" + tmp[1] + ":" + tmp[2] + ":" + tmp[3] + ":" + tmp[4] + ":" + apiGatewayArnTmp[0] + '/*/*';
    console.log("resource: " + resource);
    // if (apiGatewayArnTmp[3]) {
    //     resource += apiGatewayArnTmp[3];
    // }

    // Perform authorization to return the Allow policy for correct parameters and
    // the 'Unauthorized' error, otherwise.
    var authResponse = {};
    var condition = {};
    condition.IpAddress = {};


    if (headers.headerauth1 === "headerValue1"
        && queryStringParameters.QueryString1 === "queryValue1"
        && stageVariables.StageVar1 === "stageValue1") {
        callback(null, generateAllow('me', resource));
    }  else {
        callback("Unauthorized");
    }
}

// Help function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
    // Required output:
    console.log("Resource in generatePolicy(): " + resource);
    var authResponse = {};
    authResponse.principalId = principalId;
    if (effect && resource) {
        var policyDocument = {};
        policyDocument.Version = '2012-10-17'; // default version
        policyDocument.Statement = [];
        var statementOne = {};
        statementOne.Action = 'execute-api:Invoke'; // default action
        statementOne.Effect = effect;
        statementOne.Resource = resource;
        console.log("***Resource*** " + resource);
        policyDocument.Statement[0] = statementOne;
        console.log("***Generated Policy*** ");
        console.log(policyDocument);
        authResponse.policyDocument = policyDocument;
    }
    // Optional output with custom properties of the String, Number or Boolean type.
    authResponse.context = {
        "stringKey": "stringval",
        "numberKey": 123,
        "booleanKey": true
    };

    return authResponse;
}

var generateAllow = function(principalId, resource) {
    return generatePolicy(principalId, 'Allow', resource);
}

var generateDeny = function(principalId, resource) {
    return generatePolicy(principalId, 'Deny', resource);
}

Lambda 関数コードの編集方法の詳細については、「.zip ファイルアーカイブとして定義された Lambda 関数をデプロイする」を参照してください。

関連情報

コンソールで Lambda 関数をテストする

コメントはありません

関連するコンテンツ