skip to content

When should you NOT use string templates, and what risks (injection, locale, logging) do they introduce?

level: seniorimportance: should knowfreq 30%

answer

  1. templates do NO escaping
  2. SQL/HTML/shell + user input = injection
  3. use parameterized queries, arg lists, escaping libs
  4. toString() numbers aren't locale-aware
  5. logging: eager eval + PII leakage

basics

~20 s

Templates just paste text, so don't use them to build SQL, HTML, or shell commands from user input — that's an injection risk. Also be careful logging sensitive data and that number formatting may not match a user's locale.

solid answer

~40 s

String templates perform raw textual substitution via `toString()` — they do **no** escaping, encoding, or parameterization. So building queries or markup by interpolation is dangerous: `"SELECT * FROM users WHERE name = '$name'"` is a SQL-injection vector; use parameterized statements / prepared statements instead. Likewise interpolating into HTML invites XSS — use a templating engine that escapes. For shell commands, interpolation enables command injection; pass argument lists, not interpolated strings. Beyond security: interpolated numbers use `toString()`, which is locale-independent for primitives and won't honor a user's decimal/grouping conventions — use `NumberFormat`/`String.format` for user-facing values. And in logging, interpolating objects can leak secrets/PII because `toString()` may expose fields; templates also eagerly build the string even when the log level is disabled, unlike lazy logging APIs.

code

kotlin · 8 lines
kotlin
// BAD: injection
val sql = "SELECT * FROM u WHERE name = '$name'"
// GOOD: parameterized
val stmt = conn.prepareStatement("SELECT * FROM u WHERE name = ?")
stmt.setString(1, name)

// Lazy logging avoids eager template building when DEBUG is off
log.debug { "loaded ${expensiveDescribe()}" }

go deeper

for a junior

Knows templates just paste text and shouldn't be used for SQL from user input.

for a middle

Names injection categories (SQL/XSS/command) and the parameterization/escaping fixes.

for a senior

Adds locale-aware formatting and logging concerns (eager evaluation, PII) and chooses the right tool per context.

for a principal

Codifies guidelines (no interpolation across trust boundaries, redacted toString(), lazy logging) and reviews for these in the codebase.

## Templates are dumb substitution A string template only evaluates expressions and concatenates their `toString()` output. It performs **no escaping, sanitization, encoding, or parameter binding**. That property drives every risk below. ## SQL injection ```kotlin // DANGEROUS - never do this val sql = "SELECT * FROM users WHERE name = '$name'" ``` If `name` is `'; DROP TABLE users; --`, the query is corrupted. **Fix:** use parameterized queries / `PreparedStatement` (or a JDBC/JPA/Exposed binding) so the value is bound, never spliced as text. ## XSS / HTML injection Interpolating untrusted input into HTML produces unescaped markup, enabling cross-site scripting. Use a template engine (Thymeleaf, kotlinx.html) that HTML-escapes, or escape explicitly. ## Command injection ```kotlin // DANGEROUS Runtime.getRuntime().exec("cp $userPath /tmp") ``` Interpolating into a shell string lets an attacker inject commands. Pass an **argument array** (`ProcessBuilder(listOf("cp", userPath, "/tmp"))`) so the OS treats `userPath` as a single argument. ## Locale & number formatting Templates call `toString()`, which for `Double`/`Int` is locale-independent (always a `.` decimal separator, no grouping). For user-facing money, percentages, or large numbers, that's wrong in many locales. Use `NumberFormat.getInstance(locale)` or `String.format(locale, ...)`: ```kotlin val price = 1234.5 println("$price") // 1234.5 (not localized) println("%,.2f".format(price)) // 1,234.50 (default locale grouping) ``` ## Logging concerns 1. **PII / secrets leakage:** interpolating a domain object calls its `toString()`, which may include passwords, tokens, or personal data. Keep `toString()` clean or redact. 2. **Eager evaluation:** `log.debug("x=$expensive()")` builds the whole string even when DEBUG is off, because the template is evaluated before the call. Prefer lazy logging (`log.debug { "x=${expensive()}" }` style lambdas, or guard with `isDebugEnabled`). ## When templates ARE the right tool For trusted, internal, non-structured text — log messages with safe data, exception messages, building display strings from your own values — templates are clear and idiomatic. The rule of thumb: never use raw interpolation to construct a string that is *parsed by another system* (SQL engine, HTML parser, shell) from untrusted input. ## Summary - No escaping → never interpolate untrusted input into SQL/HTML/shell. - Use parameterization / escaping / argument lists instead. - Use locale-aware formatting for user-facing numbers. - Watch eager evaluation and PII in logs.

  • Why is interpolating into a log statement potentially wasteful?
    The template is evaluated before the log call, so the string (and any expensive toString()) is built even when that log level is disabled. Use a lazy lambda overload or an isXEnabled guard.
  • How should you safely include user input in SQL?
    Never interpolate it; use parameterized/prepared statements (or an ORM/DSL binding) so the value is bound, not concatenated into the SQL text.

A template is a copy-paste, not a security guard: it pastes exactly what you give it, so if the input is malicious the output is too.

saying these in an interview costs you the question

  • Builds SQL or shell commands with $-interpolated user input
  • Assumes templates escape HTML or SQL automatically
  • Thinks interpolated numbers are locale-formatted
  • Unaware that log templates evaluate eagerly
  • Logs whole domain objects without considering PII in toString()

context