Platform security · AWS Lambda
Prompt injection scanner for AWS Lambda AI workloads
AWS Lambda is the dominant serverless runtime for AI image processing pipelines: S3-triggered document analysis, Lambda function URL endpoints that accept image uploads, Amazon Bedrock multi-modal model invocations, and Step Functions–orchestrated multi-stage AI workflows. AWS provides comprehensive security controls at every layer of the Lambda execution environment: IAM function execution roles, resource-based policies, VPC placement, AWS WAF at the API Gateway layer, AWS Macie for S3 data classification, and AWS GuardDuty for runtime threat detection. None of these controls inspect image pixel content for embedded prompt injection payloads. IAM validates identity and permissions; WAF inspects HTTP headers and text-based request bodies; Macie classifies data sensitivity by content type; GuardDuty monitors API call patterns and network traffic — all are orthogonal to the question of whether an image byte stream contains an adversarially crafted pixel-level instruction for the downstream vision-language model. An adversarial image submitted to an S3 bucket, uploaded to a Lambda function URL, or passed as a base64 payload in a Bedrock InvokeModel request passes through all AWS security controls undetected and reaches the VLM with its injection payload intact. Glyphward provides the pre-VLM scan gate that closes this platform gap within Lambda's own execution context.
TL;DR
AWS Lambda AI pipelines have no platform-layer multimodal PI scanning — IAM, WAF, VPC, and Macie all operate at layers above image pixel content. Add POST https://glyphward.com/v1/scan as the first call in every Lambda handler that passes images to a VLM. Store your Glyphward key in AWS Secrets Manager. Reject images with score >= 65; return HTTP 400 from function URL or route to a dead-letter queue from S3 triggers. Fail-closed: if the scan is unavailable, do not fall through to the VLM. Free tier — 10 scans/day, no card required.
The four multimodal attack surfaces in AWS Lambda AI pipelines
1. Lambda function URL with direct image upload — adversarial images bypassing API Gateway WAF inspection. AWS Lambda function URLs provide HTTPS endpoints that invoke Lambda functions directly, without routing through API Gateway. Teams use function URLs for lightweight image upload endpoints: the client POSTs a multipart image or base64 payload directly to the function URL, and the Lambda handler extracts the image bytes and invokes a VLM (Amazon Rekognition custom model, Bedrock Claude with vision, or a self-deployed container-based model). Lambda function URLs support IAM and IAM_NONE authentication modes; when combined with CloudFront, AWS WAF rules can inspect HTTP traffic. AWS WAF's managed rules inspect request headers, body byte limits, and known attack signatures for SQL injection and XSS — all text-layer inspections. WAF does not and cannot inspect image pixel content within a multipart body for embedded prompt injection payloads. An adversarially crafted JPEG submitted to a Lambda function URL that accepts image uploads reaches the Lambda handler, is decoded to bytes, and is passed to the VLM with full fidelity — WAF sees a valid multipart request with a valid JPEG content type and passes it without inspection. The Lambda execution environment (IAM role, VPC, environment variables) is irrelevant to whether the image pixel content is adversarial.
2. S3 event-triggered Lambda — adversarial images in automated document processing pipelines. The most common pattern for AI document processing on AWS is S3-triggered Lambda: objects uploaded to an S3 bucket trigger an S3:ObjectCreated event, which invokes a Lambda function that reads the object, extracts images (from PDFs, Office documents, or direct image uploads), and passes them to a VLM for analysis. AWS Macie can scan S3 objects for sensitive data classification; S3 Object Lambda can intercept object reads for custom processing; S3 bucket policies and ACLs control who can upload objects — none of these controls inspect the pixel content of images for embedded prompt injection payloads. An adversarially crafted image uploaded to an S3-triggered pipeline — whether by an external user uploading a profile photo, a third-party API delivering documents, or an automated data pipeline ingesting external content — triggers the Lambda function, which reads the S3 object, extracts the image bytes, and passes them directly to the VLM. The adversarial payload is in the pixels; the S3 event trigger, Lambda IAM role, and Bedrock API authentication are all irrelevant to the payload's effectiveness.
3. Amazon Bedrock multi-modal InvokeModel — adversarial images in Bedrock Claude and Titan vision calls. Amazon Bedrock provides managed access to multi-modal foundation models including Anthropic Claude (claude-3-opus, claude-3-5-sonnet, claude-3-7-sonnet) and Amazon Titan Multimodal. Lambda functions invoking Bedrock multi-modal models pass image content as base64-encoded payloads within the InvokeModel request body. AWS IAM controls which Lambda execution roles can call bedrock:InvokeModel; AWS CloudTrail logs all Bedrock API calls; Amazon Bedrock Guardrails can filter model inputs and outputs by content category. Bedrock Guardrails operates on text content categories (hate speech, violence, profanity, PII) and can block specific topic areas in text prompts — it does not inspect image pixel content for embedded prompt injection payloads within the image bytes. A Lambda function that retrieves an S3 image, base64-encodes it, and passes it to Bedrock InvokeModel with the image in the content array reaches Claude or Titan with the adversarial pixel payload intact. Bedrock Guardrails will not flag it; CloudTrail records the API call metadata, not the image content; IAM validates the execution role, not the image payload.
4. AWS Step Functions multi-stage AI orchestration — adversarial images propagating through parallel Lambda execution states. AWS Step Functions orchestrates multi-stage AI workflows where images move through sequential or parallel Lambda execution states: a pre-processing Lambda resizes and normalises the image; an analysis Lambda invokes the VLM; a post-processing Lambda transforms the VLM output; a storage Lambda writes results to DynamoDB or S3. Each Lambda state is an isolated execution environment with its own IAM role and resource policy. An adversarial image that enters the Step Functions execution at the pre-processing stage propagates through the full state machine: the pre-processing Lambda does not inspect pixel content for PI payloads (it resizes and normalises — pixel-preserving operations that may even enhance the adversarial signal by standardising the image format); the analysis Lambda receives the normalised adversarial image and passes it to the VLM; downstream states process the adversarially influenced VLM output. The Step Functions execution audit log records the state transitions and IAM role assumptions, not the image content at each state. Standard Step Functions error handling (retry with exponential backoff, catch states, dead-letter queues) addresses Lambda execution failures, not VLM output manipulation by adversarial image content.
Integration: Glyphward pre-scan gate in AWS Lambda
import base64
import boto3
import json
import os
import urllib.request
import urllib.error
GLYPHWARD_THRESHOLD = 65
def get_glyphward_key() -> str:
"""Retrieve Glyphward API key from AWS Secrets Manager."""
client = boto3.client("secretsmanager")
secret = client.get_secret_value(SecretId="glyphward/api-key")
return json.loads(secret["SecretString"])["GLYPHWARD_KEY"]
def scan_image(image_bytes: bytes, glyphward_key: str) -> dict:
"""Call Glyphward /v1/scan. Fail-closed: raise on any non-200."""
encoded = base64.b64encode(image_bytes).decode()
payload = json.dumps({"image": encoded}).encode()
req = urllib.request.Request(
"https://glyphward.com/v1/scan",
data=payload,
headers={
"Authorization": f"Bearer {glyphward_key}",
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(req, timeout=5) as resp:
return json.loads(resp.read())
def lambda_handler(event, context):
"""
S3-triggered Lambda handler with Glyphward pre-scan gate.
Reads image from S3, scans before VLM invocation.
"""
glyphward_key = get_glyphward_key()
s3 = boto3.client("s3")
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
bucket = event["Records"][0]["s3"]["bucket"]["name"]
key = event["Records"][0]["s3"]["object"]["key"]
# Download image from S3
obj = s3.get_object(Bucket=bucket, Key=key)
image_bytes = obj["Body"].read()
# Glyphward pre-scan gate — must pass before Bedrock InvokeModel call
try:
scan = scan_image(image_bytes, glyphward_key)
except Exception as e:
# Fail-closed: scan unavailability → route to dead-letter queue, do not invoke VLM
raise RuntimeError(f"Glyphward scan unavailable: {e}") from e
if scan["score"] >= GLYPHWARD_THRESHOLD:
# Log rejection to CloudWatch and route object to quarantine prefix
s3.copy_object(
Bucket=bucket,
CopySource={"Bucket": bucket, "Key": key},
Key=f"quarantine/{key}",
)
s3.delete_object(Bucket=bucket, Key=key)
return {
"status": "rejected",
"reason": "adversarial_image",
"score": scan["score"],
"scan_id": scan["scan_id"],
}
# Bedrock multi-modal invocation — only reached by non-adversarial images
encoded = base64.b64encode(image_bytes).decode()
bedrock_payload = {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 512,
"messages": [
{
"role": "user",
"content": [
{"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": encoded}},
{"type": "text", "text": "Analyse this document image."},
],
}
],
}
response = bedrock.invoke_model(
modelId="anthropic.claude-3-7-sonnet-20250219-v1:0",
body=json.dumps(bedrock_payload),
)
result = json.loads(response["body"].read())
return {"status": "ok", "analysis": result["content"][0]["text"], "scan_id": scan["scan_id"]}
Store the Glyphward API key in AWS Secrets Manager under glyphward/api-key and grant the Lambda execution role secretsmanager:GetSecretValue on that resource ARN. For function URL handlers, return HTTP 400 with the scan rejection reason rather than raising an exception — raising causes Lambda to retry, which would re-scan the same adversarial image. For S3-triggered handlers, raise an exception on scan rejection so Lambda's error handling routes the event to the configured dead-letter queue (SQS or SNS) for inspection, and use the quarantine S3 prefix pattern shown above. For Step Functions orchestration, add the scan gate in the first Lambda state before pre-processing: a Glyphward rejection at the first state terminates the execution early, before any pre-processing amplification of the adversarial signal. Get early access
Coverage matrix
| AWS security control | Lambda function URL image upload | S3-triggered Lambda pipeline | Bedrock InvokeModel multi-modal | Step Functions multi-stage orchestration |
|---|---|---|---|---|
| AWS IAM (execution role policies) | Controls who can invoke the function; does not inspect image pixel content | Controls who can upload to S3 and invoke Lambda; does not inspect image content | Controls which roles can call bedrock:InvokeModel; does not inspect image payload | Controls state transition permissions; does not inspect images at each state |
| AWS WAF managed rules | Inspects HTTP headers and text request bodies; does not inspect image pixel content | Not applicable to S3 event triggers | Not applicable to Bedrock API calls from Lambda VPC | Not applicable to internal Step Functions state transitions |
| Amazon Bedrock Guardrails | Not applicable at Lambda function URL layer | Not applicable at S3 trigger layer | Inspects text content categories; does not inspect image pixel content for PI payloads | Applies at Bedrock call only; does not inspect images at pre-processing states |
| AWS Macie (S3 data classification) | Not applicable to Lambda function URL | Classifies data sensitivity by content type; does not detect pixel-level PI payloads | Not applicable to Bedrock API calls | Not applicable to in-flight Step Functions data |
| Glyphward pre-VLM scan gate (multimodal PI detection) | Yes — scans image bytes before VLM call; rejects adversarial images at function entry | Yes — scans S3 object before Bedrock/VLM invocation; routes adversarial images to quarantine | Yes — scans base64 image before InvokeModel call; prevents adversarial payload reaching Bedrock | Yes — scan gate in first Lambda state terminates adversarial executions before pre-processing |
Related questions
Does Lambda's 15-minute timeout and 10GB memory limit affect Glyphward scan performance?
The Glyphward POST /v1/scan API call completes in under 2 seconds for standard image sizes (up to 4096×4096 pixels). Lambda's 15-minute execution timeout is not a constraint for synchronous scan calls — the scan completes in a negligible fraction of the timeout budget. Lambda memory allocation does not affect the scan call, which is a remote API call and not an in-process computation. For Lambda functions with aggressive timeout configurations (sub-second timeouts on function URLs for latency-sensitive paths), set the Glyphward timeout parameter to 5 seconds and handle scan timeouts as fail-closed rejections rather than pass-throughs. For asynchronous S3-triggered handlers where latency is less critical, the default 5-second timeout is appropriate. Cold starts on Lambda functions add latency to the first invocation of a new execution environment; the Glyphward scan call adds 1–2 seconds to Lambda wall-clock time but not to the Lambda billed duration because the function is waiting on a network call rather than consuming CPU.
How should I handle the Glyphward API key in a multi-account AWS organisation?
Store the Glyphward API key in AWS Secrets Manager in a centralised security account and use IAM cross-account role assumption to retrieve it from Lambda execution environments in workload accounts. Alternatively, store the key in each workload account's Secrets Manager and use AWS Systems Manager Parameter Store SecureString for lower-cost, lower-sensitivity key storage. Do not store the key in Lambda environment variables in plaintext — while Lambda environment variables are encrypted at rest with KMS, they are visible in the Lambda console and CloudTrail logs. For organisations with multiple Lambda functions performing image processing, consider implementing the Glyphward scan gate as an AWS Lambda Layer containing the scan utility function, deployed across accounts via the organisation's internal artifact registry. Tag Glyphward API usage with Lambda function ARNs in the request metadata to attribute scan costs by workload. The Glyphward API pricing is per-scan, so each Lambda invocation that reaches the scan gate incurs one scan count.
Can I use AWS EventBridge to route rejected images to a security monitoring workflow?
Yes — the recommended pattern for S3-triggered Lambda handlers is to publish a custom EventBridge event on Glyphward scan rejection, route rejected events to a security monitoring workflow (a separate Lambda that logs to a security-specific DynamoDB table or sends to a SIEM via Amazon Kinesis Data Firehose), and store the rejected image in a quarantine S3 bucket with a retention policy. For function URL handlers, publish the rejection event and return HTTP 400 to the caller synchronously. EventBridge allows the security monitoring workflow to be decoupled from the image processing Lambda and versioned independently. If you are already using AWS Security Hub for centralised security findings, publish Glyphward scan rejections as custom Security Hub findings using the ASFF (Amazon Security Finding Format) so they appear in the Security Hub console alongside GuardDuty and Macie findings. This gives your security team a single-pane view of adversarial image attempts across all Lambda-based image processing workloads.
Is there a Lambda layer or CDK construct for the Glyphward scan gate?
Glyphward does not currently publish a managed Lambda Layer or CDK construct — the scan gate is a single HTTP call to POST https://glyphward.com/v1/scan, which you can implement with the Python standard library (urllib.request), the AWS SDK-bundled Boto3, or the requests library packaged in your Lambda deployment bundle. The code example on this page uses only the Python standard library for the scan call, making it layer-dependency-free. For teams deploying multiple Lambda functions with image processing, the recommended pattern is a shared utility module (not a Lambda Layer) stored in an internal PyPI index or AWS CodeArtifact repository, imported as a dependency in each Lambda function's requirements.txt. This allows the scan gate code to be versioned and updated independently of Lambda Layer version management. The API documentation has the full scan request and response schema for building your own integration.
Further reading
- Prompt injection scanner for Google Cloud Run AI — the equivalent GCP serverless container pattern with Cloud Run, Vertex AI, and Secret Manager
- OWASP LLM01 Prompt Injection — multimodal dimension — the underlying attack mechanism that AWS Lambda AI security controls do not address
- Indirect prompt injection via image — the attack pattern most relevant to S3-triggered pipelines where images arrive from third-party sources
- Multimodal AI security checklist — structured security review for AWS Lambda AI workloads including IAM, Bedrock Guardrails, and Glyphward scan gate
- Glyphward API free tier — 10 scans/day at no cost; integrate with Lambda and test against your image corpus