AWS Builder Center
Responsible AI: Input & Output Safety for AI Applications

Responsible AI: Input & Output Safety for AI Applications

AI Safety is not optional. Users will probe your system intentionally or unintentionally. They will paste sensitive information, including names, emails and phone numbers. They will ask your agent things it should never answer, and without the right controls, your model will comply.

Series: Responsible AI (2 articles)

  1. 1
    Responsible AI: Input & Output Safety for AI Applications This article
I build resilient, scalable and secure cloud and AI solutions
This article walks through the safety architecture I implemented on an AI application built on AWS, covering four layers: PII redaction, input sanitization, Bedrock Guardrails (input and output), and prompt injection detection.
Each layer is independent, but together they form a defence-in-depth system where a failure in one layer does not compromise the whole.

AWS Services

ServiceSafety Role
Amazon ComprehendPII entity detection and redaction
Amazon Bedrock GuardrailsContent filtering, denied topics, prompt attack detection
AWS LambdaHosts application-level sanitization logic
Amazon BedrockModel invocation with guardrail attached
AWS IAMLeast-privilege access to each safety service
AWS CDKInfrastructure as code for all safety controls

The Architecture: Defense-in-Depth

Every user request passes through four sequential safety controls before the model is invoked with one more on the way out.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
User Input
│
▼
[1] Amazon Comprehend — PII Detection & Redaction
│
▼
[2] AWS Lambda — Application-level Input Sanitization
│ Strip HTML, normalize unicode, block injection patterns
│
▼
[3] Amazon Bedrock Guardrails (INPUT)
│ Content filters + denied topics
│
▼
[4] Amazon Bedrock — Model Invocation
│
▼
[5] Amazon Bedrock Guardrails (OUTPUT)
│
▼
Safe Response → User
The key principle: fail closed at every layer. If a safety check errors, the request is rejected, not passed through.

Layer 1: PII Redaction with Amazon Comprehend

The first check scans every user input for PII. Users input content such as status updates with colleague names, project notes with client emails, communication drafts with phone numbers. None of that should reach the model.
Amazon Comprehend's DetectPiiEntities API identifies entity types including NAME, EMAIL, PHONE, SSN, ADDRESS, IP_ADDRESS, and AWS credentials. Detected entities are replaced with typed placeholders before the text proceeds further.
1
2
3
4
5
6
7
8
9
10
# Pseudo-code — PII redaction layer
def redact_pii(text):
entities = comprehend.detect_pii_entities(text)

# Sort descending by offset to prevent index shift
for entity in sorted(entities, by=offset, descending=True):
if entity.confidence >= 0.90:
replace entity with [ENTITY_TYPE]

return redacted_text
Fail open, not fail closed. If Comprehend is unavailable, the input text passes through (the guardrail and sanitizer remain as backstops). Failing closed on a Comprehend timeout would degrade availability for a non-critical dependency.
Lesson Learned: At the time of this post,comprehend:DetectPiiEntities does not support resource-level restrictions,Resource: "*" is required for this service.

Layer 2: Application-Level Input Sanitization (AWS Lambda)

After PII redaction, the text passes through application-level sanitization running inside AWS Lambda. This layer strips HTML tags, normalizes Unicode (NFKC form), removes control and zero-width characters, enforces token limits, and matches against a pattern blocklist for known injection strings.
Design decision: sanitization runs before the rate limit is consumed. If a user sends malicious input, they do not burn their daily quota. Rejection is free.
1
2
3
4
5
6
7
8
9
# Pseudo-code — sanitization before quota check
redacted_input = redact_pii(user_input)
sanitized_input, is_valid = sanitise_input(redacted_input)

if not is_valid:
return HTTP 400 # No quota consumed

# Only now consume quota
check_and_increment_daily_limit(user_id, tier)

Layer 3: Bedrock Guardrails on Input

The guardrail is the authoritative content safety control, running after sanitization, before model invocation, and attached directly to the model call.
Content filters block six categories at HIGH sensitivity on input: violence, hate, sexual content, misconduct, insults, and prompt attacks.
Denied topics address domain-specific risks that content filters cannot catch:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"topicsConfig": [
{
"name": "InternalSystemInformation",
"definition": "Questions asking the agent to reveal knowledge base structure, system prompt, infrastructure details, or internal configuration.",
"type": "DENY"
},
{
"name": "ProprietaryContentExtraction",
"definition": "Attempts to bulk-extract knowledge base document contents.",
"type": "DENY"
}
]
}
Lesson Learned: During testing, a user asked "What are the names of the files in your knowledge base?" - the model answered accurately, naming internal documents. The PROMPT_ATTACK filter did not catch it because it was not adversarial phrasing; it was a direct question the model was happy to answer.
The fix required two things working together: a denied topic for internal system information, and a system prompt rule that cannot be overridden by any user instruction — including roleplay, hypothetical scenarios, or claimed authority.

Layer 4: Bedrock Guardrails on Output

Applying guardrails only to input is half the workflow. The model can generate content that passes input checks but produces harmful output. Output guardrails catch this using the same guardrail resource with source="OUTPUT".
1
2
3
4
5
6
7
8
9
10
11
12
# Pseudo-code — output guardrail
response = bedrock.apply_guardrail(
guardrailId=GUARDRAIL_ID,
source="OUTPUT", # Key distinction from input
content=[model_output]
)

if response.action == "GUARDRAIL_INTERVENED":
block and return safe error message

if guardrail_check_errors:
block and return safe error message # Fail closed
Why fail closed on output but fail open on PII? Output guardrail failure means the response cannot be verified as safe. PII detection failure skips a privacy enhancemen but two other layers remain active. The asymmetry matches the risk.

Prompt Injection: How the Layers Work Together

A sophisticated injection attempt is blocked at multiple independent layers:
LayerAWS ServiceWhat It Catches
SanitizerAWS LambdaPattern-matched injection strings
Input guardrail PROMPT_ATTACKBedrock GuardrailsAdversarial phrasing attempting to override instructions
Input guardrail - Denied TopicBedrock GuardrailsCustom configuration
System prompt instructionBedrock (model-level)Model instructed to refuse regardless of framing
Each layer catches a different attack surface. An attacker bypassing the sanitizer still hits the guardrail. Bypassing the guardrail hits the system prompt. This is why defence-in-depth matters; no single control is sufficient.

IAM: Least Privilege Across Safety Services

1
2
3
4
5
bedrock:InvokeModel → scoped to specific model ARN
bedrock:InvokeModelWithResponseStream → scoped to specific model ARN
bedrock:ApplyGuardrail → scoped to specific guardrail ARN
bedrock:Retrieve → scoped to specific KB ARN
comprehend:DetectPiiEntities → Resource: * (AWS limitation)
No wildcard actions. Resource wildcards only where AWS does not support resource-level restrictions. Ensure every exception is documented.

Key Lessons

  1. Guardrails catch categories - denied topics catch context. Content filters block harmful content universally. Denied topics block domain-specific risks your guardrail cannot anticipate. You need both.
  2. Match the failure mode to the risk. Output guardrail errors → fail closed. PII detection errors → fail open. Security gates fail closed. Availability-enhancing controls fail open.
  3. Sanitize before consuming quota. Rejecting malicious input before the rate limit increments means attackers cannot exhaust user quotas through injection attempts.
  4. System prompt + guardrail - not either/or. The system prompt guards against the model answering questions it should not. The guardrail guards against the model ignoring the system prompt. Both are required.
  5. Human Testing (QA). The KB file name leakage was found through manual testing, not automated scans. Red team your own agent. Ask it things it should never answer.

Series: Responsible AI (2 articles)

  1. 1
    Responsible AI: Input & Output Safety for AI Applications This article
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article