web-dev-qa-db-ja.com

API Gatewayを介してHTTPエンドポイントにAuthorizationヘッダーを渡す方法は?

AWS API Gatewayの背後にAPIがあり、処理にAuthorizationヘッダーを使用する必要があります。残念ながら、処理のためにこれをバックエンドに渡すことができませんでした。

Method RequestでAuthorization HTTPリクエストヘッダーを作成してから、Integration Requestで対応するAuthorization HTTPヘッダーを作成しようとしました(この場合、Authorizationはmethod.request.header.Authorizationからマップされます)。バックエンドが受信するすべてのヘッダーをログに記録します。ログからは、統合リクエストにはリストされているが承認は表示されていない他のヘッダーを確認できます。

また、Content-Type application/jsonと、次のように定義されたテンプレートを使用してマッピングテンプレートを作成しようとしました。

  {
      "AccountID": "$context.identity.accountId",
      "Caller": "$context.identity.caller",
      "User": "$context.identity.user",
      "Authorization": "$input.params().header.get('Authorization')",
      "UserARN": "$context.identity.userArn"
  }

ただし、バックエンドログには、JSON本体にまだAuthorizationヘッダーもAuthorizationフィールドもないことが示されています。ユーザーのARNも表示されません。 Lambda関数に渡されるイベントオブジェクトのAuthorizationフィールドへのアクセスについてユーザーが言及している他の例やスレッドを見てきましたが、Lambda関数を使用していません。

両方のシナリオでAPI Gatewayをデプロイするようにしました。

APIゲートウェイを介してHTTPエンドポイントにAuthorizationヘッダーを渡す方法があるかどうかは誰にもわかりますか? API呼び出し元のユーザー名またはIDにアクセスする別の方法はありますか?


編集-これは、API Gatewayにアクセスするために使用しているコードのスニペットです。

String awsAccessKey = "myaccesskey";
String awsSecretKey = "mysecretkey";

URL endpointUrl;
try {
    endpointUrl = new URL("https://<Host>/<path>/<to>/<resource>?startDate=20151201&endDate=20151231");
} catch(Exception e) {
    throw new RuntimeException("Unable to parse service endpoint: " + e.getMessage());
}

Date now = new Date();

SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
sdf1.setTimeZone(new SimpleTimeZone(0, "UTC"));
String dateTS = sdf1.format(now);

String headerNames = "Host;x-amz-date";
String queryParameters = "endDate=20151231&startDate=20151201";

String canonicalRequest = "GET\n" +
        "/<path>/<to>/<resource>\n" +
        queryParameters + "\n" +
        "Host:<Host>\n" +
        "x-amz-date:" + dateTS + "\n" +
        "\n" +
        headerNames + "\n" +
        "<sha256 hash for empty request body>";

System.out.println(canonicalRequest);

SimpleDateFormat sdf2 = new SimpleDateFormat("yyyyMMdd");
sdf2.setTimeZone(new SimpleTimeZone(0, "UTC"));
String dateStr = sdf2.format(now);
String scope =  dateStr + "/us-east-1/execute-api/aws4_request";
String stringToSign =
        "AWS4-HMAC-SHA256\n" +
               dateTS + "\n" +
               scope + "\n" +
               "hex encoded hash of canonicalRequest";

System.out.println(stringToSign);

byte[] kSecret = ("AWS4" + awsSecretKey).getBytes();
byte[] kDate = HmacSHA256(dateStr, kSecret);
byte[] kRegion = HmacSHA256("us-east-1", kDate);
byte[] kService = HmacSHA256("execute-api", kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
byte[] signature = HmacSHA256(stringToSign, kSigning);
String credentialsAuthorizationHeader = "Credential=" + awsAccessKey + "/" + scope;
String signedHeadersAuthorizationHeader = "SignedHeaders=" + headerNames;
String signatureAuthorizationHeader = "Signature=" + "hex encoded signature";
String authorization = "AWS4-HMAC-SHA256 "
        + credentialsAuthorizationHeader + ", "
        + signedHeadersAuthorizationHeader + ", "
        + signatureAuthorizationHeader;

Map<String, String> headers = new HashMap<String, String>();
headers.put("x-amz-date", dateTS);
headers.put("Host", endpointUrl.getHost());
headers.put("Authorization", authorization);
headers.put("Content-Type", "application/json");

HttpURLConnection connection = null;
try {
    connection = (HttpURLConnection) endpointUrl.openConnection();
    connection.setRequestMethod("GET");

    for (String headerKey : headers.keySet()) {
        connection.setRequestProperty(headerKey, headers.get(headerKey));
    }
    connection.setUseCaches(false);
    connection.setDoInput(true);
    connection.setDoOutput(true);

    InputStream is;
    try {
        is = connection.getInputStream();
    } catch (IOException e) {
        is = connection.getErrorStream();
    }

    BufferedReader rd = new BufferedReader(new InputStreamReader(is));
    String line;
    StringBuffer response = new StringBuffer();
    while ((line = rd.readLine()) != null) {
        response.append(line);
        response.append('\r');
    }
    rd.close();
    System.out.println(response.toString());
} catch (Exception e) {
    throw new RuntimeException("Error: " + e.getMessage(), e);
} finally {
    if (connection != null) {
        connection.disconnect();
    }
}

これは、認証に成功し、バックエンドでHTTPエンドポイントをヒットするのに十分です。

17
Nick

コメントに記載されているように、Authorizationヘッダーにはユーザーが誰であるかを確認するための不完全な情報が含まれているため、このルートに進むことはお勧めしません。さらに、AWS_IAM authが有効になっている場合、AuthorizationヘッダーはAPI Gatewayによって消費されます。

AWS_IAM認証が有効で、署名が正しく提供されている場合、$ context.identityパラメーターshouldは、リクエストの署名に使用される認証情報を反映します。

コンソールでテスト呼び出し機能を使用する場合、コンテキストフィールドが入力されていますか?

更新:この問題を再現できません。次のマッピングテンプレートを使用したAPIがあります。

#set($path = $input.params().path)
#set($qs = $input.params().querystring)
{
    "resource-path": "$context.resourcePath",
    "http-method": "$context.httpMethod",
    "identity": {
        #foreach($key in $context.identity.keySet())
            "$key": "$context.identity.get($key)"
        #if($foreach.hasNext), #end
        #end
    },
    "params": {
        #foreach($key in $path.keySet())
            "$key": "$path.get($key)"
        #if($foreach.hasNext), #end
        #end
    },
    "query": {
        #foreach($key in $qs.keySet())
            "$key": "$qs.get($key)"
        #if($foreach.hasNext), #end
        #end
    },
    "body": $input.json('$')
}

そして、単に入力を出力として吐き出すラムダ関数。リクエストに署名してAPIを呼び出すと、予期した結果が返されます。

{
  "resource-path":"/iam",
  "http-method":"GET",
  "identity":{ 
    "cognitoIdentityPoolId":"",
    "accountId":"xxxxxxxx",
    "cognitoIdentityId":"",
    "caller":"AIDXXXXXXXXXXX,
    "apiKey":"",
    "sourceIp":"54.xx.xx.xx",
    "cognitoAuthenticationType":"",
    "cognitoAuthenticationProvider":"",
    "userArn":"arn:aws:iam::xxxxxxxx:user/hackathon",
    "userAgent":"Java/1.8.0_31",
    "user":"AIDXXXXXXXXXXXXXX"
  },
  "params":{},
  "query":{},
  "body":{}
}
7
Bob Kinney

現在、Authorizationヘッダーは、AWS認証を必要としないメソッドに対してのみ転送できます。 SigV4署名プロセスはAuthorizationヘッダーに依存しており、セキュリティ目的でこれを公開しません。 (SigV4署名以外に)送信する必要があるデータがある場合は、別のヘッダーで送信する必要があります。

4
RyanG

AWS API Gatewayでは、GETメソッドのリクエストボディはサポートされていません。

1
jlorie

Integration Requestで、HTTPメソッドとしてPOSTを指定して、GETをPOSTに変換します。その後、@ BobKinneyが提案するBody Mapping Templateの指定に進みます。

これにより、リクエスト本文は適切に伝播されますが、クライアントは引き続きGETリクエストを期待どおりに作成します

1
malenkiy_scot