AWS code-library medium security documentation change
Summary
Enhanced Lambda API Gateway tutorial with security improvements: added input validation, secure parameter handling, error logging, injection sanitization, and robust error handling throughout the deployment script.
Security assessment
Changes include concrete security enhancements: 1) Input validation for AWS account/region in script, 2) Safe handling of user inputs (query/headers/body) with type checking, 3) Sanitization of 'greeter' parameter to prevent injection attacks, 4) Structured error handling that prevents sensitive leakages, 5) Secure shell options (set -euo pipefail), 6) Explicit error handling for AWS CLI operations. These directly mitigate common web security risks like injection and improper error handling.
Diff
diff --git a/code-library/latest/ug/lambda_example_api_gateway_GettingStarted_087_section.md b/code-library/latest/ug/lambda_example_api_gateway_GettingStarted_087_section.md index b54fde3d2..7db17f80f 100644 --- a//code-library/latest/ug/lambda_example_api_gateway_GettingStarted_087_section.md +++ b//code-library/latest/ug/lambda_example_api_gateway_GettingStarted_087_section.md @@ -40,0 +41,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + set -euo pipefail + @@ -52,0 +55,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 + @@ -55 +63 @@ 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 @@ -57,0 +66 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + import logging @@ -59,4 +68,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) @@ -63,0 +71 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + def lambda_handler(event, context): @@ -65,5 +73 @@ 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)) @@ -71,6 +75 @@ 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' @@ -77,0 +77,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): @@ -79,15 +102,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 = { @@ -96 +116 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - "Content-Type": "*/*" + "Content-Type": "application/json" @@ -98 +118 @@ 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}!"}) @@ -101 +121,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"}) + } @@ -105 +136,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 + } @@ -125 +159 @@ 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 @@ -128 +162,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 + } @@ -133 +171,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 + } @@ -140 +181 @@ 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) @@ -143 +184 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --runtime python3.9 \ + --runtime python3.11 \ @@ -146 +187,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 + } @@ -150,2 +197,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 \ @@ -153 +200,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) @@ -155,2 +204,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 @@ -165 +218,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 + } @@ -170 +226 @@ 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) @@ -175 +231,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 + } @@ -186 +245,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 + } @@ -188,0 +251 @@ 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)" @@ -193 +256 @@ 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" \ @@ -196 +259,4 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --source-arn "$SOURCE_ARN"