docs: modernize python secrets management using aws lambda extension#2015
docs: modernize python secrets management using aws lambda extension#2015Angelmendiratta wants to merge 2 commits intoOWASP:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR modernizes the “Secrets Management” guidance in the Serverless/FaaS security cheat sheet by replacing the boto3-based Secrets Manager example with an AWS Parameters and Secrets Lambda Extension example that calls the local localhost:2773 endpoint (intended to leverage extension-side caching and reduce direct Secrets Manager API calls).
Changes:
- Replaced the boto3 Secrets Manager snippet with a
urllib3call to the Lambda extension local endpoint. - Updated surrounding “Secrets Management” guidance text to mention local caching extensions.
- Reformatted the markdown content (full-file rewrite/normalization).
Comments suppressed due to low confidence (1)
cheatsheets/Serverless_FaaS_Security_Cheat_Sheet.md:159
urllib3.PoolManager()is instantiated insideget_secret, which defeats connection pooling across invocations and adds overhead. Prefer creating a single module-level PoolManager and reusing it (this doesn’t persist secrets, just the HTTP client).
http = urllib3.PoolManager()
response = http.request("GET", secrets_extension_endpoint, headers=headers)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ```python | ||
| import os | ||
| import urllib3 | ||
| import json | ||
|
|
There was a problem hiding this comment.
This section introduces the Lambda Parameters and Secrets Extension but doesn’t mention the prerequisite that the extension must be added/enabled (e.g., as a Lambda Layer) for localhost:2773 to be available. Adding a short note here would prevent readers from copying the snippet and getting a connection failure in a default Lambda setup.
| ```python | |
| import os | |
| import urllib3 | |
| import json | |
| Note: Before using this local endpoint, ensure that the AWS Lambda Parameters and Secrets Extension is added and enabled for your function (for example, by configuring it as a Lambda layer). Otherwise, `http://localhost:2773` will not be available and requests to it will fail. | |
| ```python | |
| import os | |
| import urllib3 | |
| import json |
| # Authenticate using the identity token provided by the Lambda environment | ||
| headers = {"X-Aws-Parameters-Secrets-Token": os.environ.get('AWS_SESSION_TOKEN')} |
There was a problem hiding this comment.
The extension auth header is populated via os.environ.get('AWS_SESSION_TOKEN'), which can silently produce None and result in confusing 4xx errors from the extension. Consider failing fast with a clear error if the token is missing (and/or use os.environ['AWS_SESSION_TOKEN'] to surface a KeyError).
| # Authenticate using the identity token provided by the Lambda environment | |
| headers = {"X-Aws-Parameters-Secrets-Token": os.environ.get('AWS_SESSION_TOKEN')} | |
| # Authenticate using the identity token provided by the Lambda environment. | |
| # Fail fast with a clear error if the token is missing to avoid confusing 4xx responses. | |
| session_token = os.environ.get("AWS_SESSION_TOKEN") | |
| if not session_token: | |
| raise RuntimeError( | |
| "Missing AWS_SESSION_TOKEN environment variable required for " | |
| "Parameters and Secrets Extension authentication." | |
| ) | |
| headers = {"X-Aws-Parameters-Secrets-Token": session_token} |
| response = http.request("GET", secrets_extension_endpoint, headers=headers) | ||
|
|
There was a problem hiding this comment.
The request has no explicit timeout; if the extension is missing/misconfigured, this call can block until Lambda times out. Add a short connect/read timeout to the http.request(...) call to fail quickly and improve operational behavior.
| if response.status != 200: | ||
| raise Exception(f"Error retrieving secret: {response.data.decode('utf-8')}") | ||
|
|
||
| return json.loads(response.data)["SecretString"] |
There was a problem hiding this comment.
This returns the raw SecretString value, whereas the previous boto3 example returned json.loads(SecretString) (a parsed object). If SecretString contains JSON (a common pattern), callers will now get a JSON string instead of a dict. Consider decoding the response JSON, extracting SecretString, then JSON-decoding that value to keep behavior consistent (or rename/document the return type).
| return json.loads(response.data)["SecretString"] | |
| response_json = json.loads(response.data) | |
| secret_string = response_json["SecretString"] | |
| return json.loads(secret_string) |
|
@Angelmendiratta looks good, can you review copolit review and fix or comment why copilot is wrong |
|
@mackowski Thanks for the review. I’ve gone through the Copilot suggestions carefully and incorporated the relevant improvements:
|
|
This is such a huge change it will take us time to review, but we will get to it! Please be patient with us. |
|
Thank you! I’ll wait for your feedback. Please let me know if any changes are needed. |
Description
This PR updates the Python secrets management example in the Serverless/FaaS guide to reflect modern AWS best practices.
Key Improvements:
boto3to AWS Lambda Extension: Moved away from direct SDK calls to the localized HTTP endpoint (localhost:2773).urllib3(native to the environment) to keep function packages lightweight.Related Issue
Closes #2011