Neutral TS is a utility originally written in PHP that we have been using for years, a template engine language-agnostic, all the design and features are done, little by little we are porting the code to Rust.
In this version we have added security issues to the documentation and now all variables coming from the context are automatically filtered, this behavior can be changed:
Safety
By design the templates do not have access to arbitrary application data, the data has to be passed to the template in a JSON, then the data that you have not included in the JSON cannot be read by the template.
Variables
By design the value of the variables is not evaluated:
{
"data": {
"inject": "{:exit;:}",
}
}
Then:
<div>{:;inject:}</div>
<div>{:eval; {:;inject:} >> {:;__eval__:} :}</div>
<div>{:code; {:;inject:} :}</div>
<div>{:code; {:code; {:;inject:} :} :}</div>
In no case is {:exit;:}
evaluated, output:
<div>{:exit;:}</div>
<div>{:exit;:}</div>
<div>{:exit;:}</div>
<div>{:exit;:}</div>
This is especially important when someone tries to do this:
{
"data": {
"inject": "{:include; /path/to/secrets :}"
}
}
Note the following example:
{
"data": {
"secret": "123456",
"reference": "secret"
}
}
The following will produce the error insecure varname
:
{:; {:;reference:} :}
To evaluate a complete variable you must use allow
or evaluate partially:
{:; anything-{:;reference:} :}
The reason it can be partially evaluated is to be able to do this:
{:; array->{:;key:} :}
In the same way that you can do something similar in any programming language:
$array[$key]
In this case you should take precautions or use allow
, and as a rule, NEVER evaluate variables that come from the user, GET, POST, COOKIES, ENV, ... if you are not using allow
:
{:declare; valid >>
word1
word2
:}
{:;
{:allow; valid >> {:;reference:} :}
:}
{:code; ... :}
Unsafe variables can be displayed in a code
block:
{
"data": {
"inject": "<div>{:exit;:}</div>",
}
}
Then:
{:;inject:}
{:code; {:flg; safe :} >> {:;inject:} :}
{:code; {:flg; encode_tags_after :} >> {:;inject:} :}
Output:
<div>{:exit;:}</div>
{:;inject:}
<div>{:exit;:}</div>
Files
The following will produce the error insecure file name
:
{:include; {:;varname:} :}
To evaluate a complete variable you must use allow
or evaluate partially:
{:include; anything-{:;varname:} :}
It is best to always use allow
in include
when using a variable in the file name, including the case of partial evaluation, and as a rule, NEVER evaluate variables that come from the user, GET, POST, COOKIES, ENV, ... if you are not using allow
:
{:declare; valid-files >>
home.ntpl
login.ntpl
error.ntpl
:}
{:include;
{:allow;
valid-files >> {:;varname:}
:}{:else;
error.ntpl
:}
:}
Cross-Site Scripting (XSS)
There is a space reserved in the schema for variables coming from the user:
{
"config": {},
"inherit": {},
"data": {
"CONTEXT": {
"GET": {},
"POST": {},
"SERVER": {},
"REQUEST": {},
"FILES": {},
"COOKIE": {},
"SESSION": {},
"ENV": {}
}
}
}
All CONTEXT
variables are filtered automatically:
& โ &
< โ <
> โ >
" โ "
' โ '
/ โ /
{ โ {
} โ }
However, you must take care of assigning the variables coming from the user to CONTEXT
in your application. This way of proceeding allows the templates to know the insecure variables, moreover, they can be identified at a glance in the code.
To do the following, you will need to specify the filter modifier explicitly, note that name
is just as unsafe as its value:
{:each; CONTEXT->POST name value >>
{:&;name:} = {:&;value:}
{:;:}<br>
:}
There is also a schema configuration to escape all variables filter_all
:
{
"config": {
"filter_all": true
},
"inherit": {},
"data": {}
}
Default is false.
Rules
- Never trust on the context: GET, POST, COOKIES, ENV, ...
- In the application never trust that the templates take care of security.
- In the templates never trust that the application is in charge of security.
Act in your application as if the templates were insecure and filter all variables coming from the user and from insecure sources, do the same in your template, filter all variables from insecure sources.