AWS IAM medium security documentation change
Summary
Updated API Gateway/Lambda tutorial with enhanced input validation, error handling, security hardening, and Python 3.11 runtime
Security assessment
Added input validation (ACCOUNT_ID/REGION checks), secure event data handling with type checks, input sanitization (quote escaping), proper error logging, exception handling to prevent information leakage, and explicit content-type headers. These changes mitigate injection risks and improve error handling security.
Diff
diff --git a/IAM/latest/UserGuide/iam_example_api_gateway_GettingStarted_087_section.md b/IAM/latest/UserGuide/iam_example_api_gateway_GettingStarted_087_section.md index aeb952988..005d44056 100644 --- a//IAM/latest/UserGuide/iam_example_api_gateway_GettingStarted_087_section.md +++ b//IAM/latest/UserGuide/iam_example_api_gateway_GettingStarted_087_section.md @@ -38,0 +39,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + set -euo pipefail + @@ -50,0 +53,6 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + # Validate inputs + if [[ -z "$ACCOUNT_ID" ]] || [[ -z "$REGION" ]]; then + echo "Error: Failed to retrieve AWS account information" >&2 + exit 1 + fi + @@ -53 +61 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Create Lambda function code + # Create Lambda function code with input validation @@ -55,0 +64 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + import logging @@ -57,4 +66,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - def lambda_handler(event, context): - print(event) - - greeter = 'World' + logger = logging.getLogger() + logger.setLevel(logging.INFO) @@ -61,0 +69 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + def lambda_handler(event, context): @@ -63,5 +71 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if (event['queryStringParameters']) and (event['queryStringParameters']['greeter']) and ( - event['queryStringParameters']['greeter'] is not None): - greeter = event['queryStringParameters']['greeter'] - except KeyError: - print('No greeter') + logger.info("Received event: %s", json.dumps(event)) @@ -69,6 +73 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - try: - if (event['multiValueHeaders']) and (event['multiValueHeaders']['greeter']) and ( - event['multiValueHeaders']['greeter'] is not None): - greeter = " and ".join(event['multiValueHeaders']['greeter']) - except KeyError: - print('No greeter') + greeter = 'World' @@ -75,0 +75,24 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + # Safely retrieve greeter from query string parameters + query_params = event.get('queryStringParameters') or {} + if isinstance(query_params, dict) and 'greeter' in query_params: + greeter_value = query_params.get('greeter') + if isinstance(greeter_value, str) and greeter_value: + greeter = greeter_value + + # Safely retrieve greeter from multi-value headers + multi_headers = event.get('multiValueHeaders') or {} + if isinstance(multi_headers, dict) and 'greeter' in multi_headers: + greeter_list = multi_headers.get('greeter', []) + if isinstance(greeter_list, list) and greeter_list: + greeter = " and ".join(str(g) for g in greeter_list if g) + + # Safely retrieve greeter from headers + headers = event.get('headers') or {} + if isinstance(headers, dict) and 'greeter' in headers: + greeter_value = headers.get('greeter') + if isinstance(greeter_value, str) and greeter_value: + greeter = greeter_value + + # Safely retrieve greeter from body + body = event.get('body') + if body and isinstance(body, str): @@ -77,15 +100,12 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - if (event['headers']) and (event['headers']['greeter']) and ( - event['headers']['greeter'] is not None): - greeter = event['headers']['greeter'] - except KeyError: - print('No greeter') - - if (event['body']) and (event['body'] is not None): - body = json.loads(event['body']) - try: - if (body['greeter']) and (body['greeter'] is not None): - greeter = body['greeter'] - except KeyError: - print('No greeter') - - res = { + body_dict = json.loads(body) + if isinstance(body_dict, dict) and 'greeter' in body_dict: + greeter_value = body_dict.get('greeter') + if isinstance(greeter_value, str) and greeter_value: + greeter = greeter_value + except (json.JSONDecodeError, ValueError) as e: + logger.warning("Failed to parse body: %s", str(e)) + + # Sanitize greeter to prevent injection + greeter = greeter.replace('"', '\\"').replace("'", "\\'") + + response = { @@ -94 +114 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - "Content-Type": "*/*" + "Content-Type": "application/json" @@ -96 +116 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - "body": "Hello, " + greeter + "!" + "body": json.dumps({"message": f"Hello, {greeter}!"}) @@ -99 +119,12 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - return res + logger.info("Response: %s", json.dumps(response)) + return response + + except Exception as e: + logger.error("Unexpected error: %s", str(e), exc_info=True) + return { + "statusCode": 500, + "headers": { + "Content-Type": "application/json" + }, + "body": json.dumps({"error": "Internal server error"}) + } @@ -103 +134,4 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - zip function.zip lambda_function.py + zip -q function.zip lambda_function.py || { + echo "Error: Failed to create function.zip" >&2 + exit 1 + } @@ -123 +157 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Create IAM role + # Create IAM role with error handling @@ -126 +160,5 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --assume-role-policy-document file://trust-policy.json + --assume-role-policy-document file://trust-policy.json \ + --description "Temporary role for Lambda execution" || { + echo "Error: Failed to create IAM role" >&2 + exit 1 + } @@ -131 +169,4 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" || { + echo "Error: Failed to attach IAM policy" >&2 + exit 1 + } @@ -138 +179 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Create Lambda function + # Create Lambda function with Python 3.11 (more recent runtime) @@ -141 +182 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --runtime python3.9 \ + --runtime python3.11 \ @@ -144 +185,7 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --zip-file fileb://function.zip + --zip-file fileb://function.zip \ + --timeout 30 \ + --memory-size 128 \ + --environment "Variables={LOG_LEVEL=INFO}" || { + echo "Error: Failed to create Lambda function" >&2 + exit 1 + } @@ -148,2 +195,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Create REST API - aws apigateway create-rest-api \ + # Create REST API with minimum logging + API_RESPONSE=$(aws apigateway create-rest-api \ @@ -151 +198,3 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --endpoint-configuration types=REGIONAL + --endpoint-configuration types=REGIONAL \ + --description "API for Lambda proxy integration tutorial" \ + --output json) @@ -153,2 +202,6 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Get API ID - API_ID=$(aws apigateway get-rest-apis --query "items[?name=='$API_NAME'].id" --output text) + API_ID=$(echo "$API_RESPONSE" | grep -o '"id": "[^"]*"' | head -1 | cut -d'"' -f4) + + if [[ -z "$API_ID" ]]; then + echo "Error: Failed to create API Gateway" >&2 + exit 1 + fi @@ -163 +216,4 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --path-part helloworld + --path-part helloworld || { + echo "Error: Failed to create resource" >&2 + exit 1 + } @@ -168 +224 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - # Create ANY method + # Create ANY method with no authorization (intentional for tutorial) @@ -173 +229,4 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --authorization-type NONE + --authorization-type NONE || { + echo "Error: Failed to create method" >&2 + exit 1 + } @@ -184 +243,4 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --uri "$LAMBDA_URI" + --uri "$LAMBDA_URI" || { + echo "Error: Failed to create integration" >&2 + exit 1 + } @@ -186,0 +249 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + STATEMENT_ID="apigateway-invoke-$(openssl rand -hex 4)" @@ -191 +254 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --statement-id "apigateway-invoke-$(openssl rand -hex 4)" \ + --statement-id "$STATEMENT_ID" \ @@ -194 +257,4 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --source-arn "$SOURCE_ARN"