Have a simple Lambda POST integration with DynamoDB. Inserts one record into Dynamo upon execution. Works well when testing in AWS Lambda.
Response output is:
{
"isBase64Encoded": false,
"statusCode": 204,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true
},
"body": "{}"
}
This response is programmatically defined as part of the Lambda response handling in accordance with the spec (afaict).
However, when run via a test in API Gateway, I receive a 502 Internal server error. Am using Lambda-Proxy integration as below:
Cloudwatch logs indicate:
{ ValidationException: Supplied AttributeValue is empty, must contain exactly one of the supported datatypes
at Request.extractError
with a limited use stacktrace (webpack has hashed the code pretty well). The above error would suggest DynamoDB is not receiving the payload correctly, or in a format it wants that honours required attributes. However, I have taken the same JSON used for the (successful) tests (tweaking the IDs to be unique between runs) from lambda, and believe my request header (Content-Type: application/json) is sensible.
Any thoughts / help on narrowing down the issue? I can post more info as requested if it helps.
Ok, solved this by logging all output (and inspecting in CloudWatch), in particular the event object. When running in a Lambda Test mode, the ID and other POST attributes were passed in the root of the event object. However, when using lambda-proxy mode, the integration remaps the event object hierarchy, and POST attributes are JSON stringified into the body attribute.
Just a quirk that makes sense once you understand what it's doing. That said, it's odd that the same payload fails when testing Lambda & API Gateway in turn.
Related
The frontend developers at my company want to be able to see the lambda function logs of their requests.
Is there a way to pass something like a user created unique id in a header in an http request that will be passed from api gateway to the lambda function so that it easy to find the logs for that particular request?
It is an environment where there could be many requests happening simultaneously.
API Gateway will attach a unique ID to each request and this will filter through into Lambda. Each request should return a header with its response x-amzn-RequestId containing the request ID. In Lambda, you can access this request ID through the context object.
You're also free to create your own headers, x-correlation-id for example. In Lambda, you can access this header via the event.
Once you have you're value, you can attach that to your logs, directly as part of the log message or as metadata — depending on what logger you're using. If you're using CloudWatch, you might want to construct an object:
{ "requestId": "abc", "message": "This is a log entry." }
However you choose to do it, you should end up with a correlation ID which you can use to query your log entries.
I have implemented an AWS Lambda Authorizer in Java (type=REQUEST). I would like to invoke the lambda using a Function URL. I need to pass the following fields to my Lambda AuthorizerEvent:
headers
type (REQUEST)
methodArn
I am sending the HTTP GET message using Postman. The headers get mapped correctly to the event object. However, type and methodARN are shown as null in the CloudWatch logs and the Function URL returns a 502 Bad Gateway error:
Received AuthorizerEvent(type=null, authorizationToken=null, headers={...}, methodArn=null)
I've tried putting type and methodArn in query parameters, headers and body (even though REQUEST authorizers don't use the body, see here). I also can't find complete documentation outlining how HTTP parameters are mapped to the lambda request event parameters for Authorizer Function URLs.
Any help would be greatly appreciated!
I'm setting up a Lambda as a webhook handler that receive data POSTed from a remote API.
As per usual, the external webhook is POSTing the data in the form:
payload='SOME_JSON' for example payload='{"foo":"bar"}'
How can AWS API Gateway be configured to correctly handle application/x-www-form-urlencoded data POSTed from an external webhook, e.g., to correctly map the raw content string into a "form style" parameter name/value pair and pass the value (which is JSON) to the Lambda?
The problem is that AWS API Gateway, by default, tries to relay the entire raw string payload=JSONSTRING to the Lambda, but of course the payload= is not JSON, it's the form parameter name, so that generates a 400 error (body not in JSON format).
To test, I posted the same data to both the AWS API Gateway, and, so that I can see exactly what is POSTed from the webhook, also to webhook.site for debugging.
As shown below the in webhook.site screengrab, the raw data POSTed is in the usual webhook format, "parameter=value" eg payload=JSONSTRING, specifically: payload='{\"arg1\":\"val1\"}'
And as shown in the webhook.site screengrab, their site completely understood how to map that to a "parameter name" (payload) and the parameter value.
webhook.site screengrab:
The closest I've come is to get the API Gateway & Lambda to stop throwing 400 errors, by using API Gateway's "Body Mapping Template" to at least force the raw content "param=value" into JSON format using this body mapping:
{"body": "$input.body"}
as described in this helpful blog post: https://blog.summercat.com/using-aws-lambda-and-api-gateway-as-html-form-endpoint.html
(edit: that blog post was old, Body Mapping Template is not required, API Gateway now supports enabling "Use Lambda Proxy integration" on the Integration Request for the POST method which passed to the Lambda the full input object (headers, payload, etc) as JSON to get rid of the 400 errors. But the "body" key/value in the JSON is still not parse-able JSON, it is prefixed with the payload=")
But the problem is the the Lambda event hash still isn't in a usable format it still includes the "payload=...(escaped string)" e.g.
`{"body"=>"payload=%7B%22arg1%22%3A%22val1%22%7D"
So I hope there is some way to configure API Gateway to do what the rest of the world can do, and map the POSTed raw content "payload=JSONSTRING" to a JSON object that is passed to the Lambda, such as {"payload" : "JSONSTRING"} or perhaps just JSONSTRING?
I've created a few resources in AWS in an attempt to create MRE showcasing how the WAF can be used to prevent malicious requests from being sent to an API Gateway RestAPI.
Ive created
S3 Bucket
Kinesis Data Firehose
WAF Web ACL
API Gateway RestAPI
Ive associated the RestAPI with the WAF ACL at the stage-level. The WAF Web ACL has been configured to use the an AWS managed rule
AWS-AWSManagedRulesSQLiRuleSet
The SQL database rule group contains rules to block request patterns
associated with exploitation of SQL databases, like SQL injection
attacks. This can help prevent remote injection of unauthorized
queries. Evaluate this rule group for use if your application
interfaces with an SQL database.
https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html
Configured with defaults shown below
This is the only rule and all other defaults were chosen, as shown below.
Ive created a serverless api POST resource endpoint and despite having everything configured as described above, requests with payload/body as shown below are not blocked by the WAF.
{
"MyString":"SELECT username, password FROM Users"
}
Why arent requests being blocked by the WAF? How can I configure so that requests with SQL within request payload are rejected at the WAF before being sent to RestAPI?
I assumed that my SQL above would be enough. Is that not true? What SQL code can I use to validate the WAF and AWS Managed Rule is working as expected? What logic could explain why my request above is not being blocked?
Ive also configured S3 logging, and here is an example of 1 of the records from the s3 log, showing that this request was allowed
"timestamp": 1612921225458,
"formatVersion": 1,
"webaclId": "ABC",
"terminatingRuleId": "Default_Action",
"terminatingRuleType": "REGULAR",
"action": "ALLOW",
"terminatingRuleMatchDetails": [],
"httpSourceName": "APIGW",
"httpSourceId": "ABC:ABC:Prod",
"ruleGroupList": [{
"ruleGroupId": "AWS#AWSManagedRulesSQLiRuleSet",
"terminatingRule": null,
"nonTerminatingMatchingRules": [],
"excludedRules": null
}
],
"rateBasedRuleList": [],
"nonTerminatingMatchingRules": [],
"requestHeadersInserted": null,
"responseCodeSent": null,
What assumption am I making that is incorrect here?
I would like an MRE that shows how WAF rejects the request when post body contains malicious looking SQL, and also return 4xx response. I'm assuming this is achievable.
Update 1: Ive tried to add my own rules and rule groups, using rule builder and specifically looking at the request body. Even with these in place, it still does not reject.
Ive added rules for Body, Query string, Path. Still, request was not rejected
I have a AWS Lambda function that is triggered via AWS API Gateway. When I test my function on Lambda it is working. When I send a POST request to the API url via ajax, I get a 502 bad gateway error.
XMLHttpRequest cannot load https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/prod/myLambdaFunction. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'mywebsite.com' is therefore not allowed access. The response had HTTP status code 502.
Obviously this was a CORS issue, so I thought I could change the CORS settings in my AWS API Gateway URL which I did, but I am still getting an error for this.
What do I have to change on AWS side or my own to be able to POST to the URL?
Unfortunately there is a known issue with many classes of 4xx and 5xx errors where CORS headers will not be sent, even if you've added CORS support via the console.
As noted in comments, the CORS error is a side effect of the fact that your API is returning a 502. This often occurs if you are using the LAMBDA_PROXY integration type and are returning invalid JSON from your Lambda function.
Please try either using the test invoke functionality from the console or enable logging in your API to debug further.
I solved that exact same problem by outputting the CORS header myself.
See below - and I hope that'll help.
Teebo
Amazon docs
function respond(context, responseData) {
var response = {
statusCode: 200,
body: JSON.stringify(responseData),
headers: {
"Content-Type": "application/json; charset=utf-8",
"Access-Control-Allow-Origin": "*"
}
};
context.succeed(response); }