
Defending LLM applications against Unicode character smuggling

When interacting with AI applications, even seemingly innocent elements—such as Unicode characters—can have significant implications for security and data integrity… In this blog post, we explore Unicode tag blocks, a specific range of characters spanning from U+E0000 to U+E007F, and how they …
When interacting with AI applications, even seemingly innocent elements—such as Unicode characters—can have significant implications for security and data integrity. At Amazon Web Services (AWS), we continuously evaluate and address emerging threats across aspects of AI systems. In this blog post, we explore Unicode tag blocks, a specific range of characters spanning from U+E0000
to U+E007F
, and how they can be used in exploits against AI systems. Initially designed as invisible markers for indicating language within text, these characters have emerged as a potential vector for prompt injection attempts.
In this post, we examine current applications of tag blocks as modifiers for special character sequences and demonstrate potential security issues in AI contexts. This post also covers using code and AWS solutions to protect your applications. Our goal is to help maintain the security and reliability of AI systems.
Understanding tag blocks in AI
Unicode tag blocks serve as essential components in modern text processing, playing an important role in how certain emoji and international characters are rendered across systems. For instance, most country flags are shown using two-letter regional indicator symbols (such as U+1F1FA U+1F1F8
, which represents the U and the S for the US). However, countries like England, Scotland, or Wales use a different method. These special flags start with a U+1F3F4
(
Waving black flag emoji), followed by hidden tag characters that represent the region code (such as gbeng for England
), and end with a cancel tag.
Without these underlying Unicode mechanisms, some flag emojis might fail to render as expected. However, the same processing flexibility that makes tag blocks valuable for legitimate text rendering also presents unique security challenges in AI systems. When processing text through large language models (LLMs), these invisible characters can be repurposed to create hidden payloads within seemingly innocent content. LLMs are trained on a large amount of data and can read, interpret, and act on these hidden characters placed with Unicode tags, potentially leading to unauthorized or unexpected behavior.
The risks of tag blocks in AI
Hidden character smuggling in the context of LLMs can be particularly problematic because of the scale at which data is processed. Our testing has revealed that these models, along with their runtime environments (Python, Java, and so on), can interpret the same character sequence in dramatically different ways. This inconsistency creates security gaps; allowing bad actors to craft inputs that can slip through security filters. The goal of this post is to call out those gaps and provide stronger validation patterns.
Example scenario
Consider an AI assistant integrated into an email client to assist users by reading and summarizing emails. A bad actor could embed a malicious instruction in what appears to be an ordinary email. When the email is processed, the assistant might not only summarize the email but also execute the hidden instruction—such as deleting the entire inbox.
For instance, the incoming email might look like this to a user:
However, when viewed with hidden characters revealed, it contains malicious instructions:
Because the malicious instructions are invisible to the user, they don’t notice anything suspicious. If the user then asks the AI assistant to summarize the email, the assistant could execute the hidden instruction, resulting in deletion of the entire inbox.
Solutions overview
Let’s first review a solution commonly proposed online for remediating Unicode tag block vulnerability in Java and then understand its limitations.
The one-pass approach in the preceding example has a subtle but critical flaw. Java represents Unicode tag blocks as surrogate pairs in UTF-16 as uXXXXuXXXX
. If the input contains repeated or interleaved surrogates, a single sanitization pass can inadvertently create new tag block characters. For example, uDB40uDC01
is the surrogate tag block pair for the Language tag (which is invisible). In the following Java example, we include repeating surrogate pairs, then view the output:
The results show the valid surrogate pair in the middle gets converted into a regular tag block character and the non-matching high and low surrogate pairs are still wrapped around. These orphaned non-matching surrogates are displayed as a ? (the display symbol might vary depending on the rendering system), making them visible but their values still hidden. Passing this through the preceding single pass sanitization function would yield a newly formed Unicode invisible tag block character (high and low surrogates combined), effectively bypassing the filter.
Without a recursive function, Java-based AI applications are vulnerable to Unicode hidden character smuggling. AWS Lambda can be an ideal service for implementing this recursive validation, because it can be triggered by other AWS services that handle user input. The following is sample code that removes hidden tag block characters and orphaned surrogates in Java (see the Limitations section to understand why orphaned surrogates are stripped) and can be deployed as a Lambda function handler:
Similarly, you can use the following Python sample code to remove hidden characters and orphaned or individual surrogates. Because Python represents strings as Unicode (UTF-8), characters are not stored as surrogate pairs and are not combined, avoiding the need for a recursive solution. Additionally, Python handles surrogate pairs such that unpaired or malformed surrogate sequences raise an error unless explicitly allowed.
The preceding Java and Python sample code are sanitization functions that remove unwanted characters in the tag block range before passing the cleaned text to the model for inferencing. Alternatively, you can use Amazon Bedrock Guardrails to set up denied topics to detect and block prompts and responses with Unicode tag block characters that could include harmful content. The following denied topic configurations with the standard tier can be used together to block prompts and responses that contain tag block characters:
Note: Denied topics do not sanitize and send cleaned text, they only block (or detect) specific topics. Evaluate whether this behavior will work for your use case and test your expected traffic with these denied topics to verify that they don’t trigger any false positives. If denied topics don’t work for your use case, consider using the Lambda-based handler with Python or Java code instead.
Limitations
The Java and Python sample code solutions provided in this post remediate the vulnerability created by invisible or hidden tag block characters; but stripping Unicode tag block characters from user prompts can lead to some flag emojis not being interpreted by models with their intended visual distinctions, appearing instead as standard black flags. However, this limitation primarily affects a limited number of flag variants and doesn’t impact most business-critical operations.
Additionally, the handling of hidden or invisible characters depends heavily on the model interpreting them. Many models can recognize Unicode tag block characters and can even reconstruct valid orphaned surrogates next to each other (such as in Python), which is why the preceding code samples strip even standalone surrogates. However, bad actors could attempt strategies such as further splitting orphaned surrogate pairs and instructing the model to ignore the characters in between to form a Unicode tag block character. In such cases, the characters are no longer invisible or hidden.
Therefore, we recommend that you continue implementing other prompt-injection defenses as part of a defense-in-depth strategy of your generative AI applications, as outlined in related AWS resources:
- Securing Amazon Bedrock Agents: A Guide to Safeguarding Against Indirect Prompt Injections
- Safeguard Your Generative AI Workloads from Prompt Injections
- Prompt Injection Security.
Conclusion
While hidden character smuggling poses a concerning security risk by allowing seemingly innocent prompts to make malicious instructions invisible or hidden, there are solutions available to better protect your generative AI applications. In this post, we showed you practical solutions using AWS services to help defend against these threats. By implementing comprehensive sanitization through AWS Lambda functions or using the Amazon Bedrock Guardrails denied topics capability, you can better protect your systems while maintaining their intended functionality. These protective measures should be considered fundamental components for critical generative AI applications rather than optional additions. As the field of AI continues to evolve, it’s important to be proactive and stay ahead of threat actors by protecting against sophisticated exploits that use these character manipulation techniques.
If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, contact AWS Support.
Author: Russell Dranch