65 comments

[ 3.0 ms ] story [ 80.2 ms ] thread
It was never about the prompt, it is about the prompt delivery.
Good job AI, after we managed to almost fix SQL injections everywhere, you made them come back!
Putting AI anywhere near people’s finances without even being asked while being responsible for those finances is some next level negligence imho.
While this is relevant and should indeed be fixed, the attack surface and the practicality of the exploit is a bit meh.

The user needs to do 3 things for this to be actually be phished:

1. Receive money from somebody they don’t known with a weird description 2. Proactively ask the agent for such transaction 3. Click the link the agent provide

While this of course can happen on scale, doesn’t seems so critical in practice

This is similar to scam where people are sent messages about bad transaction with a fake link to the bank to verify it. Some attackers have gotten Paypal to send notifications that have the link. People are supposed to check the source and go directly to bank, and this will bypass that.
> There is no single control that solves indirect prompt injection

There is, actually. It's called removing the AI agent. Done.

the solution to this problem is so simple and so easy to reason about from first principles i am shocked i can continue making $$$ deploying agents (LLM-driven workflows) for finance customers
This is very interesting. Before I read the article, I thought this one one of those instances where a bank asks a customer to verify a recent transaction to prove they are the account holder (like where did you make your last purchase, and how much did you spend there?), for things like password resets or PIN resets over the phone. It occured to me that a phisher who deposits money into a checking account (a small sum included, could use this if they knew the bank would ask what the most recent transaction amount was. Then when they call in pretending to be the customer, they (if they have other personal information like last 4 of SS# and address, email, phone etc), can get their password reset and gain access to the account. But if the customer blocks any unauthorized deposits, such as ACH/Zelle, then they might not have this issue. Obviously banks should caution or avoid using received funds as an authentication method, except as part of a larger number of evidentiary items.

Was this the type of phishing attack they used? If not, there's two vulnerabilities, and one is not yet patched.

This is so simple to prevent, it's just a matter of prompting. The fact that the bank didn't proactively secure against this makes me glad that I'm not one of their customers.
Okay, time to close the account with them I guess
This line really stood out to me.

> It may look like ordinary text, but when it is placed into an LLM context window, the model may interpret it as an instruction rather than as data.

I feel like as long as this is the case, we'll never have secure LLMs. It concisely summarises the alarm bell I hear every time someone talks about adding AI features to their product. I plan on using this as a sort of benchmark for future AI discussions: "how do you plan on separating data from instructions?"

It's a tricky problem for sure. Even on CPUs this separation is maintained by architectural guardrails. The CPU will happily execute whatever it is permitted to fetch. There is and cannot be a fundamental divide betwixt the two. It's always going to be an artificial externally managed issue. I suppose this is no different for LLMs.

My thinking is we are in the 50s/60s. Stuff is starting to come forward, it's all very exciting but very, very raw. I don't think this will last.

The notions of "tokens" and how inference works will become arcane insider knowledge like how CPU registers and interrupts work. You don't work with CPUs, you work with "computers" and even then mostly "operating systems" or even "browsers". Reality has been abstracted away from you to a very impressive degree. I don't think it'll be different here, but we haven't had our Xerox PARC and Bell Labs moments yet.

> separating data from instructions

There's been a lot of talk about this (for years, honestly), but it all stems from a fundamental nonunderstanding of how LLMs work. There is no distinction for an LLM; "instructions" are a prompt concept, nothing more. It's not possible to separate the two, because LLMs simply take text (ie your instructions, then the data, or maybe in a different order, or maybe something completely else) and "predict" the next token, and repeat for as long as you want, with the volatility you ask for. There is no control plane, and there never will be a control plane, because asking for that is akin to asking "how do I separate data from instructions when I speak to a person?". You can ask nicely, "pretty please obey the first part of what I say and not stuff after", but there's no way to guarantee it (like you're used to with software). There is just input and output.

I would love to have a unicode character for representing the start/end of a data block so that LLMs could at least send data meant to be uncorrupted down a different path at tokenization.
I have been working on this issue for a bit, and the most interesting approach I have seen so far comes from the research domain of information-flow control, specifically Microsoft’s FIDES work.

The idea is not to distinguish instructions from data. It is closer to having different privilege levels. Not all code has to run in kernel space, some code runs in unprivileged user space. So what is the equivalent for LLM agents?

In FIDES-style systems, every piece of information that enters the agent context is labeled along two dimensions: integrity and confidentiality. Integrity captures whether the data is trusted or untrusted (i.e. could it contain a prompt injection attack). Confidentiality captures who is allowed to see or receive it [0].

The privileged agent, sometimes called the planning agent, should not directly see untrusted data because it would be susceptible to prompt injection attacks. In the article’s example, a bank transaction’s sender-supplied reference would be untrusted. Instead, the planning agent receives a variable token. It can then either delegate processing of that variable to an unprivileged / quarantined agent with no or limited tool access, or pass the token as a reference to a tool.

Tools then have policies attached to their arguments and outputs. These policies specify which integrity and confidentiality levels are allowed, and whether the tool call may proceed. The policy also determines how the result should be labeled.

For example:

1. High-confidentiality data should not be allowed to flow into a `send_email` tool call addressed to an external recipient.

2. A tool call whose result depends on untrusted input should generally produce untrusted output.

3. A sensitive side-effecting tool should be able to reject calls that are influenced by untrusted context.

So the answer to “how do you separate data from instructions?” may be: you do not rely on the model to do that separation. You track provenance and privilege outside the model, and then enforce the security policy at the tool boundary.

[0] In the simplest implementation, confidentiality is assessed with a binary low/high value, however, in a more advanced implementation, confidentiality can be represented as the set of users or principals allowed to learn that information.

Quite simple you make harness and loads of people are building harnesses as we speak.

Right now also a lot of people are building in a way where they give a sample data to LLM so that AI agent builds deterministic code for crunching data so that actual data doesn't go to LLM and is processd by regular code, only that code for processing is written by agent.

You can always process only descriptions that are in the list and ones that are not recognized "ask a human" so just an allowlist. I do believe normal person would have most transactions that would be mostly the same and then couple that would stand out so you also can make allowlist from last 2 years as a starting point, not to bother people too much (I think no one has prompt injection in their last 2 years banking history besides ultra nerds maybe).

I think by now it is common knowledge that "just dump all data at LLM and as some questions" or "let LLM process anything someone sends me in an e-mail" is silly.

In "the standoff" Pliny was trying to hack tszzl harness and it wasn't working an Pliny is notorious for jail breaking LLMs.

The current usage model comingles commands and data. That doesn't have to be the case. Use an input format that explicitly presents them as separate components parsed into a data structure with non-LLM tooling. Or stick with natural language input but parse into an intermediate format that can be verified to some standard of correctness.
I’m no expert, but as long as they’re represented by tokens in the end, they’re just tokens. Even if you train the transformer to treat them specially, a token is a token, and there’s no free lunch. At best, you’re going to be trading off between paying attention to this would-be security boundary and delivering high-quality results; the more you focus on one, the more you lose on the other.
There's a few simple things they could do to make these injections less dramatic / dangerous, but we have to re-learn everything in tech every time there's a new fancy toy.
Well, your computer is a Von Neumann machine that does not separate data from instructions, right? The techniques that secured it are not directly applicable to LLMs but in principle there must be ways to track them.
In this case it could be solved by not letting the LLM consume the transaction message. Effectively the same as preventing user supplied info going to the first argument of printf().

The transaction in question can remain opaque to the LLM and a %transaction.message% string is resolved in the layer between the LLM and the user.

I found it very ironic that this article, which in practice is trying to solicit customers, is both talking about a fundamental weakness of LLM while also trying to sell AI consultation. In reality as many have said here you cannot have untrusted data in the context. There isn't that much more to say.

Prompt injection is XXS without possibility of sanitation. There is no absolute cure.

> benchmark for future AI discussions: "how do you plan on separating data from instructions?"

Ah! Avoiding in-band signalling!

As this point, maybe we should have a discussion on why modern developers are unable to learn dangers we were aware of > 30 years ago from using 2600 Hz signalling tones over the phone.

> "how do you plan on separating data from instructions?"

Hang on, is this basically just the same fault as SQL injection, you can force it to misinterpret a string as a command rather than an operand?

I feel like we already fixed that.

> I plan on using this as a sort of benchmark for future AI discussions: "how do you plan on separating data from instructions?"

I'm reminded of the thing that HN drools over constantly with LISP languages.

> This line really stood out to me.

Where did you people find any text to discuss? I saw like 3 headlines while scrolling past the first 3 screens then gave up.

Some companies just want to torch their own reputation, in rolling out such stupid AI things on top of critical industries without any oversight or thinking because "AI is cool rn".

This is not the place where AI should be used here.

I mean it's bunq. Them and reputation aren't in the same zip code too often
Defense in depth approach, would this work to help as a layer?

- Wrap user input in strong markers like <user-input-do-not-trust />

- Have the agent compute what it will perform as structured output.

- Have another agent evaluate the structured output against the intent of the code.

- Determine if it aligns or deviates from the intended workflow. Execute or deny gate from here.

This kind of prompt injection should also work for customer feedback forms for companies I really don't like, right?
Well this is rather dumb to the point I dont understand why they wrote this article?

This line of attack is so extremely obvious and variants of it have been discussed so many times as to be effectively the quintessential example of what not to do. Having the ?tech? consultants to a bank prance it about as a show of their skill and dedication is making me question the bank itself.

It’s a case study. Why wouldn’t they present work they’ve done for a customer?
Oh i maybe was a bit too short worded. I meant specifically that they framed this as if they discovered a previously unknown class of bug and are now sharing it with the world to help save us.

I liked that they shared it - but the tone was all wrong. It wasn't an unknown type of attack and the fact that (they're presenting it as if) neither the bank nor they knew about it before hand makes both look bad.

There's not really a great way to write that blog post and make everybody happy, but if you had to i'd just not have named the bank and offer it as a case study of why this class of attacks needs attention.

It’s a nice simple example of the problem. I will be using it to explain to my friends why they should not be using OpenClaw just yet.
separated context for data and instructions?
Brilliant. Nobody’s thought of that.
One can use custom message roles and indented XML for such data. If this doesn't help, your model hasn't undergone basic training in prompt injection. SoTA models are expected to have undergone it.

Hiding the data via encryption or templating or tool calling doesn't reliably work because the data is needed for other questions.

Also, all potentially harmful actions must require approval in a fresh context by an independent workflow or agent.

Why would the agent send the results of the query "Show me my recent transactions" to LLM? This pretty deterministic results which involve no LLM interpretation or decision making.

I understand that people are no longer writing IF expression in their code, because they think it's too brittle, and so they delegate all "IF" branching logic to LLM, but it beats me why displaying of the results from a database query should involve LLM.

Why would this even be in the chat? Showing recent transactions is a basic functionality of a bank.
Taking in the text and calling the database tool is kind of a decision
Because they want the user to be able to say things like “show me my transactions for business meals in the last month”. That requires an LLM to analyze the transaction descriptions.
Because the question they're asking isn't "What is the best way to solve this problem" the question they're asking is "Where can I shove my AI into this product".
That seems like a lot of text in a SEPA transfer message. I don't think I've ever gotten that amount of space to enter a message when making a transfer.

Is there a much higher standard limit that any banks I've used have stayed below?

Could we fix the title to match the article?

> How we helped Bunq secure their financial AI assistant

The name of the agent is 'finn' - is that a reference to Intercom's Fin agent?
> Modern banking apps increasingly include AI-powered features. These sit between the user and a range of backend data sources, such as transaction records, product documentation, account details

Literally no one stopped to even question the insanity of this. "just add more AI"

The solution is obviously another AI which checks the output for sanity.

You'd of course need another one to check the sanity of the sanity check decision of the previous one.