AWS Builder Center
Securing generative AI on Amazon Bedrock: lessons from a workshop

Securing generative AI on Amazon Bedrock: lessons from a workshop

I completed a hands-on workshop that walks through four of the most common generative AI security risks using a healthcare chatbot built on Amazon Bedrock. Prompt injection, excessive agency, sensitive information disclosure, and harmful content generation are easier to trigger than you might expect.

Generative AI applications introduce security challenges that traditional application security practices do not fully address. When you build on top of large language models (LLMs), the attack surface shifts in ways that are easy to underestimate. Inputs are free-form natural language. Outputs are non-deterministic. And a meaningful chunk of your application logic lives in prompts rather than code.
The OWASP Top 10 for Large Language Model Applications  names and categorizes these risks so teams have a common language when talking about them. I recently completed a hands-on workshop that walks through four of the most common risks from that list using a sample healthcare chatbot built on Amazon Bedrock . I tested the application, found the vulnerabilities, implemented fixes, and verified the results with both manual and automated testing.
Here is how the workshop is structured and what I learned from each module.

The workshop setup

The sample application is a healthcare chatbot that serves two user personas: a doctor named Christine and a receptionist named Alice. It is built on Amazon Bedrock Agents  using Amazon Nova Pro  as the underlying foundation model, backed by an AWS Lambda  action group for patient data retrieval, and uses Amazon Bedrock Knowledge Bases  for policy and medical reference content stored in Amazon S3 .
Each module follows the same structure: identify the vulnerability through testing, protect against it through configuration or code changes, detect it using AWS monitoring tools, and respond to it with automated actions. This maps to the NIST Cybersecurity Framework  (identify, protect, detect, respond), which made it easy to connect the hands-on steps to a broader security practice.
An overview of the solution architecture

Prompt injection is easier to trigger than you might expect

The first module covers LLM01:2025 Prompt Injection , crafting inputs that cause an LLM to ignore its instructions and produce unintended outputs. Before configuring any protections, I tested the application by entering a prompt that asked the chatbot to begin its response with a specific phrase. Without any guardrails in place, the application complied. More targeted variations, like asking the application to reveal the underlying model ID or return data in a structured format to bypass system prompt protections, were only slightly more involved.
I fixed this by configuring an Amazon Bedrock Guardrails  prompt attack filter and attaching it to the Bedrock Agent. Before making that change, I used an AWS Config  custom Lambda rule provided by the workshop (named bedrock-agent-guardrail-prompt-attack) to confirm the non-compliant state. The rule checks whether all Bedrock Agents in the account are configured with a guardrail that has the prompt attack filter enabled. The agent showed as noncompliant with an annotation confirming that no guardrail was attached. After creating the guardrail and attaching it to the agent, I re-evaluated the rule and the agent moved to compliant.
What I liked about this step is that it uses infrastructure compliance tooling to catch the security gap before anyone exploits it. You find out from AWS Config, not from an incident.
One detail worth calling out: guardrails are not attached to an agent automatically after you associate them. You have to prepare the agent after making changes, or the guardrail will not take effect. If you are writing a runbook for your team, make this step explicit.

Authorization belongs in code, not in prompts

The second module covers LLM06:2025 Excessive Agency . The vulnerability here is straightforward: the Lambda function backing the Bedrock Action Group returns both contact and medical information for every patient query, regardless of who is asking. The application relies on a system prompt instruction to hide medical data from receptionists. A crafted prompt can override that instruction entirely.
The solution is to move the authorization check into the Lambda function code. Once the function reads the user's role from the Bedrock Agent session attributes and returns medical data only for users with the Doctor role, the system prompt instruction becomes irrelevant to the security outcome. The LLM never receives data the user is not authorized to see, so there is nothing to manipulate.
The key takeaway: LLM outputs are not a reliable security boundary. Any authorization decision that matters should be made in deterministic code, not in a prompt.

Metadata filtering is the right tool for knowledge base access control

The third module covers LLM02:2025 Sensitive Information Disclosure . In the workshop, both the doctor and receptionist roles could retrieve each other's data from the knowledge base because there were no access controls on the documents it returned. Christine the doctor could access appointment scheduling policies intended only for receptionists, and Alice the receptionist could access medical reference documents intended only for doctors.
The solution uses metadata filtering in Amazon Bedrock Knowledge Bases . Each document in the S3 data source has a corresponding .metadata.json file that tags the document with the role authorized to access it, using a metadataAttributes element with a role key set to either Doctor or Receptionist. The application code passes the current user's role as a filter when invoking the agent, so the knowledge base only retrieves documents tagged for that role.
This keeps access control configuration separate from application logic. The metadata files define who can see what, and the application code passes the role. If you need to change access rules, you update the metadata files and re-sync the data source rather than modifying application code.

Content filtering catches what prompt defenses miss

The fourth module covers LLM09:2025 Misinformation  and harmful content generation. Without content filters in place, the workshop application responded to prompts asking for information that could facilitate harm to patients. I fixed this by configuring a misconduct content filter in Amazon Bedrock Guardrails and attaching it to the agent, following the same pattern as the prompt attack guardrail in the first module.
Prompt injection defenses and content filters address different parts of the problem. A prompt attack guardrail blocks attempts to manipulate the model's behavior through crafted instructions. A content filter blocks harmful outputs regardless of how the request was framed. Both are necessary. Amazon Bedrock Guardrails supports enabling them together in a single guardrail configuration, which simplifies management.

Automated testing makes security testing repeatable

Across all four modules, the workshop uses promptfoo , an open-source testing and evaluation tool for generative AI applications, to run automated tests against the chatbot. Each test suite is a YAML file that defines the prompts, the expected outputs, and the assertions. Because LLM outputs are non-deterministic, the assertions use semantic similarity comparisons powered by the Amazon Titan Text Embeddings V2  model rather than exact string matches, with a configurable similarity threshold. Deterministic assertions using substring matching handle cases where specific terms must be present or absent in the output.
I saw the value of this when I ran the same test before and after making a fix. The test that failed because the application returned medical data to a receptionist passed after the Lambda function was updated. The test that failed because a prompt injection was not blocked passed after the guardrail was attached. The tests gave me a consistent, repeatable way to verify that fixes were working and that they continued to work as other changes were made.
For production environments, consider packaging these test suites into a Lambda function and running them on a schedule using Amazon EventBridge , or integrating them into a CI/CD pipeline to gate deployments. That way security validation is not something you do once and forget about.

Detecting and responding to guardrail violations

Configuring guardrails and fixing code vulnerabilities addresses prevention. The workshop also covered detection and response using Amazon CloudWatch , and this part of the architecture is worth spending a minute on.
The application writes structured logs to a CloudWatch Logs log group. A metric filter on that log group evaluates each log entry against a pattern that looks for two specific fields: the username and the event type, which is set to either PROMPT_ATTACK or MISCONDUCT depending on the module. When a match is found, the filter increments a CloudWatch metric and attributes the count to the specific user through a dimension configuration. This means you can create per-user alarms rather than a single aggregate alarm for the entire application.
A CloudWatch alarm monitors the metric. When the count exceeds a configured threshold, it invokes a Lambda function that disables the chatbot user account. The workshop uses a low threshold to make it easy to trigger during the session, but the pattern is reusable: log guardrail events with structured fields, filter for the event types you care about, alarm on the count with a threshold appropriate for your traffic patterns, and invoke a function to take action. Whether that action is disabling a user, notifying a security team, or opening a ticket is up to you.

What I took away

Running the application without guardrails first matters more than it might seem. You get a concrete baseline, and it makes the value of the controls you add much clearer. It also forces you to see what your application is actually doing before you assume it is behaving as intended.
The workshop combines guardrails, authorization logic in Lambda, metadata filtering in the knowledge base, infrastructure compliance checks in AWS Config, and monitoring in CloudWatch. No single layer is enough on its own. Remove any one of them and a gap opens that the others do not cover.
And automated testing is not optional. Manual testing is useful for exploration, but it does not scale and it is not repeatable. Building test suites early and running them regularly is the best way to know your application is still secure as you keep changing it. The OWASP Top 10 for LLM Applications  is a useful starting point for deciding what to test, and tools like promptfoo make it practical to build coverage across multiple risk categories without writing custom test infrastructure.
If you are building generative AI applications on Amazon Bedrock and want to explore these topics hands-on, I would recommend looking for workshops that cover the OWASP Top 10 for LLMs using AWS services. The hands-on format, where you trigger the vulnerability yourself before fixing it, is what made the lessons stick.
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