TypeStrings
Background
THT introduces TypeStrings as a method of preventing injection attacks.
What Are Injection Attacks?
Injection attacks are one of the top security vulnerabilities on the web.
This occurs when an attacker sends input containing meta-characters (e.g. quotes) which trick the app into executing something different from what was intended.
For example, here is a SQL query to check if a login was successful:
// Login query select * from users where user = 'admin' and password = 'knock-knock'
An attacker could enter a malicious password that contains a quote, followed by some SQL code, like this:
// ✕ Malicious Input knock-knock' or '1'='1 ^
Here is what the query looks like with the malicious password. It allows the attacker to log in as any user in the system, because the query will always be true.
// ✕ Hijacked Query select * from users where user = 'admin' and password = 'knock-knock' or '1'='1'
Attack Targets
Any subsystem is vulnerable to attack if it:
- Takes a string as input.
- Uses meta-characters to delineate data from code.
The most common targets are:
Subsystem | Meta-characters |
---|---|
Database queries | quotes, comments |
HTML output | angle brackets, quotes |
System commands | quotes |
Sidebar: “Just Be Careful” (JBC) is Not Enough
Unfortunately, it’s not enough to “just be careful” and escape everything manually, for a few reasons:
- The programmer has to remember how each subsystem must be escaped.
- The programmer is human and can simply forget, or make a mistake.
- It is easy to mistakenly double-escape strings.
- Strings that are safe in the present can be made unsafe in the future by a change in another part of the code.
- An attacker only needs to find one mistake to render the whole system vulnerable.
In short, when working with thousands of lines of code, there are too many chances for even the most experienced programmers to overlook something.
How TypeStrings Work
To prevent injection attacks, THT introduces TypeStrings.
A TypeString is a literal string that is prefixed with a type identifier.
$query = sql'select * from table where userId = {}' ^^^
Placeholders
TypeStrings can ONLY be combined with plain, unprotected strings via the use of placeholders ({}
).
Placeholders (also known as parameterized queries) are an industry best practice for securing SQL queries by ensuring that all inserted data is safely escaped.
TypeStrings expand this tactic to cover all types of sensitive strings, like URLs, system commands, and HTML.
Why It Works
This approach is effective because:
- TypeStrings are literal constants hardcoded into your source files. This means you can trust them to be 100% benign. (Assuming you trust yourself!)
- High-risk modules only accept TypeStrings as input.
- TypeStrings automatically escape placeholders safely for their type.
Altogether, this removes human error from the equation, leading to more consistent protection with less effort.
How to Use TypeStrings
Just prepend the type to any literal string to mark it as a TypeString.
This tells THT to keep it separate from all other dynamic strings, which often come from outside (untrusted) sources.
Examples:
$query = sql'select * from users where userId = {}' $cmd = cmd'ls -l {}'
Supported types:
Prefix | Description | Example |
---|---|---|
sql |
SQL query | sql'select * from posts' |
html |
HTML markup | html'<b>user123</b>' |
url |
URL | url'/posts?sort=desc' |
cmd |
System command | cmd'ls -l /some/path' |
js |
JavaScript code | js'var id = 123;' |
json |
JSON data | json'{"a":[1,2,3]}' |
css |
CSS code | css'body { color: #333 }' |
lm |
Litemark markup | lm'# Contact Info' |
Filling Placeholder Values
You can attach dynamic values to a TypeString via the fill
method.
These will be inserted into placeholders (e.g. {}
), which will be safely escaped by the TypeString class.
$query = sql'select * from users where userId = {}' $query.fill($userId) // The Db module only accepts TypeStrings $row = Db.selectRow($query)
Combining TypeStrings
Nested TypeStrings
If you insert a TypeString into another TypeString of the same type, it will be inserted verbatim.
$name = html'<b>Admin</b>' html'Posted by: {}'.fill($name) //= 'Posted by: <b>Admin</b>'
Nested Placeholders
TypeStrings can be nested in any combination.
When the top-level TypeString is rendered, all of the embedded TypeStrings will be rendered and escaped according to their type.
$colorHex = '#FF0000' $css = css'color:{}'.fill($colorHex) $userName = 'Admin' $name = html'<b style="{}">{}</b>'.fill($css, $userName) $name.renderString() //= '<b style="#FF0000">Admin</b>'
Appending TypeStrings
You can join two TypeStrings together using the stringy ~
operator.
Placeholder values must be filled separately before they are merged.
$query = sql'select title from posts where postDate > {}' $monthAgo = Date.create('30 days ago') $query.fill($monthAgo) // Append a dynamic 'limit' value $rowsPerPage = 20 $query ~= sql' limit {}'.fill($rowsPerPage)
Template Functions
Most TypeStrings have a corresponding Template Function that make it easy to create blocks of content that are safely parsed and escaped.
// Returns an HTML TypeString tem headerHtml($userName) { <.welcome-banner> Welcome back, {{ $userName }}! </> }
Methods
See the TypeString class for a full list of methods.