CVE-2023-29400 - Unquoted HTML Attribute Injection in Templates – How a Design Flaw Turns Empty Input into a Security Nightmare
CVE-2023-29400 is a security issue involving web application templates, especially those that use Go's html/template or similar logic in other development frameworks. This flaw means that unquoted HTML attributes—like attr={{.}}—might allow attackers to inject unexpected attributes or malicious code, simply through weird edge cases in HTML normalization rules, even when the template input is empty.
This post will break down how the vulnerability works, show you with code, explain why it happens, and guide you on protecting your code.
Why is this so Dangerous?
When template engines insert something into an attribute value, we expect that only the expected data ends up there. But if the attribute is unquoted, and normalization (auto-formatting of HTML by browsers or parsers) kicks in, unusual results can occur. Most devs wouldn’t expect empty user input to pose much risk – with CVE-2023-29400, that assumption is wrong.
Attackers can abuse this to inject their own HTML attributes (like onmouseover, onclick, etc.)—leading to Cross-Site Scripting (XSS) or other unexpected behaviors.
The Core Problem
Unquoted attributes in HTML can be problematic when variable data is injected, especially if that data is empty or contains special characters. When a template like this is used:
<div attr={{.}}>
Content
</div>
If the variable ({{.}}) is empty, some browsers may parse this as if the attribute’s value is missing, which can merge it with following content or attributes. With odd input, it can break out and append new attributes, without quotes acting as a boundary.
Exploit Example
Suppose you have the following Go template in your application (similar logic appears in Python/Jinja, PHP, etc.):
// main.go
package main
import (
"html/template"
"os"
)
func main() {
tpl := <button data-info={{.}}>Click Me</button>
t, _ := template.New("x").Parse(tpl)
// Simulate attacker-supplied value
// os.Args[1] can be "", "xyz", or injected string
t.Execute(os.Stdout, os.Args[1])
}
If a user sends this as input
onmouseover=alert(1)
The rendered result is
<button data-info=onmouseover=alert(1)>Click Me</button>
Browsers will *normalize* this as
<button data-info="" onmouseover="alert(1)">Click Me</button>
Boom! The attacker now controls an event handler through nothing but template behavior and HTML parsing rules.
But here’s the really surprising bit: Even if input is empty, depending on surrounding whitespace or parsing, the template may still produce syntactically confusing output, which can be exploited in edge cases.
References
- Original Go security advisory about CVE-2023-29400
- CVE-2023-29400 on NVD
- OWASP: XSS Prevention Cheat Sheet
- HTML attribute value unquoted (MDN)
Let’s take this key example. Given
<input type={{.}}>
If the {{.}} input is left empty (""), the HTML rendered is
<input type=>
Browsers treat this as either an attribute with an empty value (type=""), or they may misinterpret if, for example, someone puts:
autofocus onfocus=alert(1)
So rendered
<input type= autofocus onfocus=alert(1)>
When parsed, this becomes
<input type="" autofocus="" onfocus="alert(1)">
How Attackers Abuse This
Attack vector: Attacker finds a field that ends up inserted (unquoted) in an attribute, perhaps in a custom admin panel, email template, or reporting tool your team built with Go's template engine.
<br>- Use template functions or context-aware escapes.<br>- Never assume empty input is “safe.” Test edge cases.<br>- Use the latest version of your template engine; Go fixed this after April 2023 (Go release note).<br><br>---<br><br>## TL;DR: Key Takeaways<br><br>- Don't use unquoted attribute values in templates.<br>- Even empty user input can cause security issues due to how browsers parse HTML.<br>- Quote every attribute, always: `
- Upgrade your frameworks and libraries to include context-aware auto-escaping.
---
## Learn More
- Read the Go security advisory
- See real-world issues at CVE-2023-29400 NVD page
- Try out the template code above in your own playground to see the effect.
- Safeguard your apps and demonstrate to colleagues how unexpected empty or tricky input can break trust boundaries.
Stay secure, stay smart!
Timeline
Published on: 05/11/2023 16:15:00 UTC
Last modified on: 05/22/2023 18:21:00 UTC