AWS code-library medium security documentation change
Summary
Enhanced API Gateway/Lambda integration example with security improvements: added input validation, secure parameter handling, output sanitization, structured error handling, and robust resource management
Security assessment
The changes implement multiple security best practices: 1) Added input validation checks for AWS account parameters to prevent misconfiguration. 2) Implemented safe extraction of user inputs from multiple sources (query/headers/body) with type checking to prevent injection vulnerabilities. 3) Added output sanitization (escaping quotes) to prevent XSS in downstream consumers. 4) Implemented structured exception handling to prevent leakage of sensitive error details. 5) Added IAM role descriptions to improve auditability. These changes directly mitigate common web security risks.
Diff
diff --git a/code-library/latest/ug/bash_2_api-gateway_code_examples.md b/code-library/latest/ug/bash_2_api-gateway_code_examples.md index 3bed8cdda..e162a373f 100644 --- a//code-library/latest/ug/bash_2_api-gateway_code_examples.md +++ b//code-library/latest/ug/bash_2_api-gateway_code_examples.md @@ -54,0 +55,2 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + set -euo pipefail + @@ -66,0 +69,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 + @@ -69 +77 @@ 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 @@ -71,0 +80 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + import logging @@ -73,4 +82,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) @@ -77,0 +85 @@ There's more on GitHub. Find the complete example and learn how to set up and ru + def lambda_handler(event, context): @@ -79,5 +87 @@ 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)) @@ -85,6 +89 @@ 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' @@ -91,0 +91,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): @@ -93,15 +116,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 = { @@ -110 +130 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - "Content-Type": "*/*" + "Content-Type": "application/json" @@ -112 +132 @@ 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}!"}) @@ -115 +135,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"}) + } @@ -119 +150,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 + } @@ -139 +173 @@ 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 @@ -142 +176,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 + } @@ -147 +185,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 + } @@ -154 +195 @@ 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) @@ -157 +198 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --runtime python3.9 \ + --runtime python3.11 \ @@ -160 +201,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 + } @@ -164,2 +211,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 \ @@ -167 +214,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) @@ -169,2 +218,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 @@ -179 +232,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 + } @@ -184 +240 @@ 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) @@ -189 +245,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 + } @@ -200 +259,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 + } @@ -202,0 +265 @@ 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)" @@ -207 +270 @@ 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" \ @@ -210 +273,4 @@ There's more on GitHub. Find the complete example and learn how to set up and ru - --source-arn "$SOURCE_ARN"