Skip to content

docs: modernize python secrets management using aws lambda extension#2015

Open
Angelmendiratta wants to merge 2 commits intoOWASP:masterfrom
Angelmendiratta:update/serverless-secrets-python
Open

docs: modernize python secrets management using aws lambda extension#2015
Angelmendiratta wants to merge 2 commits intoOWASP:masterfrom
Angelmendiratta:update/serverless-secrets-python

Conversation

@Angelmendiratta
Copy link
Contributor

Description

This PR updates the Python secrets management example in the Serverless/FaaS guide to reflect modern AWS best practices.

Key Improvements:

  • Transitioned from boto3 to AWS Lambda Extension: Moved away from direct SDK calls to the localized HTTP endpoint (localhost:2773).
  • Performance & Cost: The new implementation leverages managed caching, reducing latency and Secrets Manager API costs.
  • Zero Dependencies: Used urllib3 (native to the environment) to keep function packages lightweight.

Related Issue

Closes #2011

jmanico
jmanico previously approved these changes Feb 23, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 urllib3 call 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 inside get_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.

Comment on lines +140 to +144
```python
import os
import urllib3
import json

Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
```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

Copilot uses AI. Check for mistakes.
Comment on lines +155 to +156
# Authenticate using the identity token provided by the Lambda environment
headers = {"X-Aws-Parameters-Secrets-Token": os.environ.get('AWS_SESSION_TOKEN')}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
# 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}

Copilot uses AI. Check for mistakes.
Comment on lines +159 to +160
response = http.request("GET", secrets_extension_endpoint, headers=headers)

Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
if response.status != 200:
raise Exception(f"Error retrieving secret: {response.data.decode('utf-8')}")

return json.loads(response.data)["SecretString"]
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
return json.loads(response.data)["SecretString"]
response_json = json.loads(response.data)
secret_string = response_json["SecretString"]
return json.loads(secret_string)

Copilot uses AI. Check for mistakes.
@mackowski
Copy link
Collaborator

@Angelmendiratta looks good, can you review copolit review and fix or comment why copilot is wrong

@Angelmendiratta
Copy link
Contributor Author

@mackowski Thanks for the review. I’ve gone through the Copilot suggestions carefully and incorporated the relevant improvements:

  • "Replaced urllib3 with the Python standard library (urllib.request) to avoid introducing external dependencies in the cheat sheet example"
  • "Added a prerequisite note clarifying that the Lambda Parameters and Secrets Extension must be enabled"
  • "URL-encoded secret_name to prevent malformed query strings"
  • "Added explicit validation for AWS_SESSION_TOKEN to fail fast if it’s missing"
  • "Introduced a request timeout to avoid blocking behavior"
  • "Ensured return-type consistency with the previous boto3 example by JSON-decoding SecretString"

@jmanico
Copy link
Member

jmanico commented Mar 2, 2026

This is such a huge change it will take us time to review, but we will get to it! Please be patient with us.

@Angelmendiratta
Copy link
Contributor Author

Thank you! I’ll wait for your feedback. Please let me know if any changes are needed.

@mackowski mackowski requested a review from szh March 3, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[UPDATE] Modernize Python Secrets Retrieval in Serverless Cheat Sheet using AWS Lambda Extension

4 participants