Shopify AI Chatbot XSS Explained

x32x01
  • by x32x01 ||
  • #1
A chatbot greeting may look harmless, but if an application lets users control the greeting and then renders that value as Markdown, it can become an XSS attack surface.

A recently disclosed Shopify Help Center issue demonstrates the general pattern: an attacker-controlled greeting value was reflected into the AI assistant interface, where Markdown image rendering could be abused to create an attacker-controlled link. The disclosed report was classified as Reflected XSS and was publicly disclosed in June 2026.

The interesting lesson is not the specific payload. It is the complete flow:
Attacker-controlled input → Markdown rendering → attacker-controlled URL → browser execution context → actions performed with the victim's session

⚠️ The dangerous exfiltration and account-manipulation payloads from the original report are intentionally not reproduced here. The focus is on understanding the vulnerability and how developers can prevent this class of attack.



How the Attack Surface Started​

The vulnerable functionality was associated with the AI assistant on the Shopify Help Center.
When the chat interface loaded, a request contained a greeting parameter similar to: greeting=Welcome ya dawly
The important observation was that the value was not simply static text.
Changing the parameter changed what appeared in the chatbot interface.

That immediately raises a useful security-testing question:
Where does this input go after the server receives it?

If attacker-controlled input is reflected into a page, the next step is to determine how that value is interpreted.

Is it rendered as:
  • Plain text?
  • HTML?
  • Markdown?
  • A URL?
  • JavaScript?
  • A template?
  • JSON that is later inserted into the DOM?
The answer determines the available attack surface.



Why Markdown Matters​

Markdown is normally used to make text easier to format.
For example, Markdown can represent:
  • Bold text
  • Italic text
  • Links
  • Images
  • Lists
A simplified example might look like: [IMG alt="Example"]https://example.com/image.png[/IMG]
A Markdown renderer can convert that into HTML containing an image element.
The security problem begins when an application assumes that Markdown is automatically safe.
Markdown is not a security boundary.
Depending on the renderer and its configuration, Markdown can generate HTML elements and URLs that require additional validation.
Shopify's current security guidance explicitly recommends treating external input as untrusted and validating URLs so that dangerous schemes such as javascript: are rejected.



From Controlled Text to a Controlled Link​

The interesting discovery was that the attacker-controlled greeting could be turned into Markdown that generated a clickable link.
A harmless example is: [IMG alt="Click here"]https://example.com[/IMG]
If the application renders that Markdown, the resulting interface may contain an element controlled partly by the attacker.

At this point, the vulnerability is no longer simply:
"I can change the chatbot greeting."

The security question becomes:
"Can I control the generated URL or HTML in a way that changes browser behavior?"

That distinction is critical during XSS research.

Why javascript: URLs Are Dangerous​

One of the classic browser security problems is allowing an attacker-controlled URL to use the javascript: scheme.

A safe application normally expects links to use schemes such as: https:
or: http:

A dangerous URL scheme can instead cause the browser to interpret the URL as executable JavaScript.

For example, a security test should use a harmless proof of concept rather than code that steals data:
javascript:alert(document.domain)

⚠️ Whether this executes depends on the browser, application, sanitization, link attributes, and the exact context in which the URL is opened.
This is why simply seeing a javascript: value reflected in HTML does not automatically prove exploitable XSS.
The entire browser execution path has to be understood.

The target="_blank" Detail​

One interesting part of this vulnerability class is the behavior of links that open in a new browsing context.
A link may contain: target="_blank"
This tells the browser to open the destination in a new tab or window.
However, developers should not treat target="_blank" as an XSS protection mechanism.
It is primarily a browsing behavior.
Security depends on the URL scheme, sanitization, browser behavior, and the context in which the link is processed.

For links opened in a new tab, developers should also consider: rel="noopener"
and, where appropriate: rel="noreferrer"

These attributes address opener and referrer behavior. They should not be confused with input sanitization or XSS protection.
Shopify's security guidance similarly treats URLs as an active-content security boundary and recommends allowing only schemes that the application actually needs.



The Real Impact of XSS​

Finding a JavaScript execution primitive is only part of an XSS report.
The next question is: "What can JavaScript do in the victim's browser?"

If the vulnerable page is running inside an authenticated session, malicious JavaScript may potentially be able to perform actions that the victim is authorized to perform.

Depending on the application's architecture, an XSS vulnerability can potentially be used to:
  • Read sensitive information accessible to the page.
  • Make authenticated requests.
  • Modify application data.
  • Perform actions as the victim.
  • Interact with internal APIs exposed to the browser.
  • Access sensitive DOM content.
  • Abuse application functionality available to the current user.
Shopify's own security documentation lists session information, personal data, order information, and actions performed as a logged-in user among the potential consequences of XSS.
The exact impact must always be demonstrated within the authorized testing scope.



Why CSRF and XSS Are Different​

A common mistake is to describe every browser-based attack as CSRF.
They are different vulnerabilities.
CSRF abuses a victim's authenticated browser to send an unwanted request.
XSS gives attacker-controlled JavaScript an execution context inside the target origin.
The difference matters because XSS can sometimes make traditional CSRF protections less effective.
For example, if an attacker can execute JavaScript on the same origin as the application, that script may be able to interact with application functionality in ways that a cross-origin attacker cannot.

Therefore, a developer should not think:
"We have CSRF protection, so XSS is harmless."
CSRF defenses remain important, but they do not replace output encoding, sanitization, URL validation, and other XSS defenses.



Why a Missing CSRF Token Can Make a Chain Worse​

Suppose an application exposes a state-changing endpoint that accepts a browser request but does not require a CSRF token or another strong anti-CSRF mechanism.

By itself, that may create a CSRF vulnerability depending on the endpoint, authentication mechanism, cookie policy, and request requirements.

If an attacker also has XSS on the same origin, however, the situation can become more serious because malicious JavaScript may be able to make authenticated requests from the victim's browser.

A simplified defensive model looks like this:
Code:
Untrusted input
↓
Markdown renderer
↓
Unsafe URL
↓
JavaScript execution
↓
Authenticated browser requests
↓
Sensitive application actions
This is why security researchers often investigate what authenticated functionality is reachable after establishing script execution.



The Importance of the Application's Internal APIs​

Modern web applications frequently use APIs behind the user interface.
The browser may communicate with endpoints using:
  • REST APIs
  • GraphQL
  • Fetch requests
  • XMLHttpRequest
  • Form submissions
  • Internal application routes
A successful XSS vulnerability can potentially interact with APIs that the victim's browser is already authorized to access.

For defensive testing, the important question is:
Which server-side actions are available to the current browser session?

A useful methodology is to inspect normal application traffic and identify:
  1. Which requests load the page.
  2. Which requests retrieve user information.
  3. Which requests retrieve conversation data.
  4. Which requests modify application state.
  5. Which requests require CSRF protection.
  6. Which requests rely only on the user's authenticated session.
This is much more useful than blindly sending random payloads.



Why Base64 Does Not Make JavaScript Safe​

Another common misconception is that encoding a JavaScript payload with Base64 makes it safer.
It does not.
Base64 is an encoding format, not encryption and not sanitization.
For example: atob()
can decode Base64 data in JavaScript.

If an application eventually passes decoded attacker-controlled data to an execution sink, Base64 merely hides the original content from casual inspection.

From a defensive perspective:
Encoded attacker input is still attacker input.
Developers should therefore decode and validate data according to the actual context in which it will be used.



How Developers Should Prevent This Vulnerability​

The most important defense is to decide whether the greeting actually needs Markdown.
If the greeting is supposed to be text, render it as text.

For example, prefer a text API such as:
JavaScript:
element.textContent = greeting;
instead of inserting untrusted content as HTML.
Shopify's security guidance specifically recommends text APIs such as textContent when the application does not need to render HTML.
If Markdown is genuinely required, use a well-maintained Markdown parser with a strict security configuration.

Then:
  • Sanitize generated HTML.
  • Use an allowlist rather than trying to block a few known-bad strings.
  • Validate every generated URL.
  • Allow only required URL schemes.
  • Reject javascript: URLs.
  • Be careful with data: URLs.
  • Treat Markdown image destinations as untrusted.
  • Avoid dangerous HTML extensions unless they are explicitly required.
  • Apply output encoding appropriate to the final rendering context.
Shopify currently recommends using a well-maintained sanitizer with a strict allowlist when applications genuinely need to accept HTML.



A Safer URL Validation Model​

If an application allows users to provide URLs, validation should happen before the URL reaches an HTML or Markdown renderer.
A simplified defensive example is:
JavaScript:
function isSafeUrl(value) {
try {
const url = new URL(value, window.location.origin);
return url.protocol === 'https:' || url.protocol === 'http:';
} catch {
return false;
}
}
This is only a basic example. Real applications should define exactly which URL schemes, hosts, ports, and paths are required by the product.

The important principle is:
Validate the URL based on what the application actually needs, not based on a blacklist of dangerous strings.
Shopify's security documentation recommends this allowlist-oriented approach for URL handling.



What Security Researchers Can Learn From This​

🔍 The most useful lesson from this bug class is the attack-chain mindset.
Instead of stopping at: "I can control this parameter."
continue asking:
  1. Where is the value reflected?
  2. Is it treated as text or markup?
  3. Is Markdown supported?
  4. What HTML does the Markdown generate?
  5. Can the generated URL be controlled?
  6. Which URL schemes are accepted?
  7. Where does the link open?
  8. Does JavaScript execute in any supported browser path?
  9. What authenticated functionality is available from that origin?
  10. Are state-changing requests protected against CSRF?
This approach turns a simple input reflection into a structured security investigation.



The Bigger Lesson From AI Chatbots​

🤖 AI chatbots are adding another layer to modern web applications, but the security fundamentals have not changed.
A chatbot still processes:
  • User input
  • Server-side data
  • Markdown
  • HTML
  • URLs
  • Browser APIs
  • Authentication state
  • Internal API requests
Every one of these can become part of the attack surface.
The fact that the interface is called an "AI chatbot" does not make traditional web security problems disappear.

Shopify's current security guidance explicitly says that both external input and AI output should be treated as untrusted data, and that model output should never be passed directly into dangerous execution contexts.

🎯 The core lesson is simple:
Never let convenience turn untrusted chatbot content into trusted browser code.
A greeting should be text.
A Markdown link should be a validated link.
A URL should use an allowed scheme.
And a state-changing API should enforce proper authentication and request protections independently of whatever the frontend happens to render.



Frequently Asked Questions​

-----------------

Can Markdown cause XSS?​

Yes. Markdown itself is not automatically a security boundary. Depending on the parser and configuration, Markdown can generate HTML and URLs that become dangerous if attacker-controlled input is not sanitized and validated.

Is javascript: always an XSS vulnerability?​

Not by itself. The actual impact depends on whether the value reaches an executable browser context and whether the application, browser, or security controls prevent execution.

Does Base64 protect JavaScript payloads?​

No. Base64 is encoding, not encryption or sanitization. An encoded malicious value can still become dangerous after decoding if it reaches an execution sink.

Does target="_blank" prevent XSS?​

No. target="_blank" controls where a link opens. It should not be considered an XSS defense.

Is CSRF protection enough to prevent XSS?​

No. CSRF and XSS address different problems. CSRF defenses protect state-changing requests against cross-site abuse, while XSS defenses prevent attacker-controlled code from executing in the application's origin.

What is the main defense against Markdown XSS?​

Treat Markdown as untrusted input. If Markdown is not required, render the value as plain text. If it is required, use a trusted parser and sanitize the resulting HTML with a strict allowlist, including strict URL-scheme validation.
 
Similar threads
x32x01
Replies
0
Views
54
x32x01
x32x01
x32x01
Replies
0
Views
91
x32x01
x32x01
x32x01
Replies
0
Views
91
x32x01
x32x01
x32x01
Replies
0
Views
91
x32x01
x32x01
x32x01
Replies
0
Views
78
x32x01
x32x01
Forum Statistics
Threads
1,040
Messages
1,045
Members
15
Latest Member
Mohamed
Back
Top