Course / 09 Web security / Lesson 23 of 28

Server-side template injection

Estimated time: 18 min reading · 12 min activity

A template renderer combines application-controlled source with values such as a visitor's name. A template is text containing placeholders and sometimes expressions. An expression is an operation the template language evaluates. In this model, {{ name }} inserts a variable and {{ 2 + 3 }} evaluates trusted arithmetic.

Server-side template injection, or SSTI, occurs when submitted text becomes template source instead of staying a variable value. The source role matters: a caller who can supply expressions may gain capabilities the application intended to reserve for its own templates.

Before you begin

Estimated time
18 min reading, 12 min activity. Setup and optional practice may take longer.
Where you work
Read the original fictional source, object tables, token claims and policy models in any current desktop or mobile browser. No loader, template engine, provider registration, service or network request is required.
Without a lab
Follow the printed data through each stated rule on paper. Record the decision, its supporting fact and a limit, then compare with the guided rubric. Model reasoning does not establish a live tool result. Reading works without JavaScript. Checking requires JavaScript and a secure context; saving also requires usable storage.

After this lesson you can:

  • Separate a template's source from a variable.
  • Read the trusted sum as the model's arithmetic.
  • Classify Card D and Card Q by who supplies the source.

01Example

Worked trusted source computes 2 + 3. The activity cards are D and Q.

Trusted source: Sum: {{ 2 + 3 }}
Model result: Sum: 5
User text kept as data stays the characters 2 + 3.
Not an observed engine.

02Source and data

Card D fixes the source as Hello, {{ name }} and supplies Lane as the name value. The caller controls the value, not the source. Card Q instead compiles the caller's whole text as source. That is the trust boundary: data receives authority to define template behavior.

Keep application templates fixed and pass submitted values through the supported data interface. Output escaping remains necessary for the output context, but escaping alone cannot fix a design that compiles untrusted text as template source. Template features and syntax differ between engines; the simple arithmetic results below are a model, not a recorded engine run.

Keep a caller's text in a data variable, and mark the step that compiles that text as the template.Model. A teaching model drawn by hand. It shows the idea, not real tool output.

Read the full explanation below.

rolesresults
FieldExpression in trusted sourceCaller text as data
Trusted template sourceSum: {{ 2 + 3 }}Trusted source remains separate
Caller-data roleNot caller data2 + 3
Predicted resultSum: 5The literal characters 2 + 3
Source and data have separate roles

The expression belongs to trusted template source only in the first card.

The expression belongs to trusted template source only in the first card.

Follow the explanation

  1. The expression belongs to trusted template source only in the first card.
  2. The model predicts 5 for trusted source and literal caller data. No engine was observed.
Read every state and value

1. The expression belongs to trusted template source only in the first card.

Selected Comparison
roles
Highlighted Field
Trusted template source · Caller-data role

2. The model predicts 5 for trusted source and literal caller data. No engine was observed.

Selected Comparison
results
Highlighted Field
Predicted result
The boundary is source versus supplied data; no template engine was observed.

03Classify the cards

Read the evidence before comparing with the activity rubric. Do not install an engine.

Template model. No template engine was installed. Results are the model's arithmetic.

Trusted source, no user text in the source:
  source: Sum: {{ 2 + 3 }}
  model result: Sum: 5

User text kept as data:
  source: Hello, {{ name }}
  name: 2 + 3
  model result: Hello, 2 + 3

User text compiled as the source:
  source: the caller's text
  The caller's text becomes the template.
  That is the boundary this lesson marks.

Download the template model

Activity cards.

Card D
  The fixed source is Hello, {{ name }}
  The caller supplies the name Lane
  The caller text is variable data

Card Q
  The caller supplies the whole template text
  The program compiles that text as the source

Card D keeps the caller in the data role.
Card Q gives the caller the source role.

Download the activity cards

  1. Write who supplies the source on Card D and on Card Q.
  2. Write the data role Card D gives the caller.
  3. Check: Card D keeps the caller in the data role. Card Q gives the caller the source role. The worked sum is the trusted source.
  4. Stop. Do not compile caller text as a template.

04Optional challenge

Explain escaping versus keeping the caller in the data role. The rubric is: escaping changes how a value is written at one insertion point, and the data role stops the caller from becoming the source. About ten minutes.

05Fresh cases for the checks

Use these fictional cases for the checks below. They have different inputs from the worked examples. Read them here or make notes; no installation or external service is needed.

Template case A: fresh source/data cardsOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.

P: trusted fixed source "Total: {{ 4 + 6 }}".
Q: trusted fixed source "Hello, {{ name }}"; caller variable name is the literal text "4 + 6".
R: the caller supplies the entire string which the application compiles as template source.
Model rule: arithmetic in source is evaluated; variable text is inserted as text, not recompiled.
No template engine ran.

06Explain your evidence

Optional written self-check: explain one decision using the supplied case. No tool run is required.

Check your explanation: identify the compiled source and variable independently; trace source arithmetic versus literal data; state the missing engine observation; explain why output escaping alone does not repair caller-controlled source.

07Common mistakes

If you mark Card D as the source problem, you switched the cards. Card Q compiles the caller.

If you treat Sum: 5 as observed output, it is the model's arithmetic.

If you add a server command to the caller's text, you left the card.

References