Server Side Template Injection in Adonisjs
How Server Side Template Injection Manifests in Adonisjs
Server Side Template Injection (SSTI) in Adonisjs occurs when user-controlled data is rendered directly through the template engine without proper sanitization. Adonisjs uses Edge as its default templating engine, which provides powerful features but can be exploited when user input reaches the template context.
The most common vulnerability pattern in Adonisjs applications involves passing request parameters directly to views. For example:
// Vulnerable controller action
async profile({ request, view }) {
const userData = await User.find(request.input('id'))
return view.render('profile', {
user: userData,
// User-controlled data flows directly to template
bio: request.input('bio')
})
}In this scenario, if an attacker can control the bio parameter, they can inject Edge template syntax. Edge templates use curly braces for expressions: {{ expression }} and {{{ html }}} for unescaped HTML.
A practical attack might look like this:
{{ Process.env.SECRET_KEY }}
{{ new Date().getTime() }}
{{ include('admin/dashboard') }}This would execute server-side JavaScript, potentially exposing environment variables, system time, or including other templates. More sophisticated attacks can chain multiple expressions:
{{ (new Error('debug')).stack }}
{{ (new java.lang.ProcessBuilder(['cat', '/etc/passwd'])).start().text }}Adonisjs's Edge engine also supports JavaScript expressions within templates, making it particularly dangerous when user input reaches the template context. The injection can occur through:
- URL parameters rendered in templates
- Database fields containing template syntax
- API responses passed to views
- Form inputs used directly in template rendering
The risk is amplified because Edge templates execute within the Node.js process, giving attackers access to the full application context, including database connections, file system, and environment variables.
Adonisjs-Specific Detection
Detecting SSTI in Adonisjs requires both static analysis and dynamic testing. middleBrick's black-box scanning approach is particularly effective because it tests the actual running application without requiring source code access.
middleBrick scans Adonisjs applications by sending crafted payloads to template-rendering endpoints and analyzing responses. The scanner tests for:
- Edge template syntax injection (
{{ }},{{{ }}}) - JavaScript expression evaluation within templates
- Template inclusion attacks (
include(),partial()) - Context-aware payload testing for different template locations
For manual detection in Adonisjs, examine your controllers for patterns where request data flows directly to views:
# Check for direct parameter usage in view rendering
grep -r "view.render" app/Controllers/Http/ | grep -E "(request|params|query)"Look specifically for Edge template injection indicators in your templates:
{{ user.bio }} # Safe if sanitized
{{{ user.bio }}} # Dangerous - raw HTML output
{{ Process.env.SECRET }} # Environment variable exposuremiddleBrick's API security scan for Adonisjs applications includes specific checks for:
- Authentication bypass through template injection
- Property authorization bypass via template context manipulation
- Input validation bypass through template expression execution
The scanner tests endpoints with payloads like:
{{ (new Date()).getTime() }}
{{ include('malicious-template') }}
{{ Process.env.NODE_ENV }}Successful injection is detected when these expressions evaluate and appear in the response, indicating that user-controlled data is being rendered through the template engine.
Adonisjs-Specific Remediation
Remediating SSTI in Adonisjs requires a defense-in-depth approach. The primary strategy is to prevent user-controlled data from reaching the template engine in the first place.
Safe data handling in controllers:
// Instead of direct parameter usage
async profile({ request, view }) {
const userData = await User.find(request.input('id'))
const bio = request.input('bio', '')
// Sanitize and validate before template rendering
const sanitizedBio = this.sanitizeBio(bio)
return view.render('profile', {
user: userData,
bio: sanitizedBio
})
}
// Helper method for sanitization
sanitizeBio(bio) {
// Remove template syntax
const templatePattern = /{{(.*?)}}/gs
const cleaned = bio.replace(templatePattern, '')
// HTML escape remaining content
return cleaned
.replace(/&/g, '&')
.replace(/ .replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(///g, '/')
}Edge template best practices for Adonisjs:
{{! Safe rendering with HTML escaping }}
{{ user.bio }}
{{! Explicit escaping for dynamic content }}
{{= user.bio }}
{{! Avoid triple braces for user data }}
{{{ user.bio }}} {{! Dangerous - raw output }}Content Security Policy (CSP) headers in Adonisjs:
// In your middleware or route handler
async handle({ response }, next) {
response.header('Content-Security-Policy', "default-src 'self'; script-src 'self'")
await next()
}Input validation middleware for Adonisjs:
const { schema } = use('@adonisjs/validator')
const bioSchema = schema.create({
bio: schema.string.optional({ escape: true })
})
// Apply in controller
async profile({ request, view }) {
const payload = await request.validate({ schema: bioSchema })
return view.render('profile', {
user: await User.find(request.input('id')),
bio: payload.bio
})
}For maximum security, implement a Content Security Policy that restricts script execution and use the Edge engine's built-in escaping mechanisms consistently throughout your Adonisjs application.