-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Python: Promote Template Injection query from experimental #17922
Python: Promote Template Injection query from experimental #17922
Conversation
b487f4c
to
2d04af8
Compare
9377747
to
66e173c
Compare
QHelp previews: python/ql/src/Security/CWE-074/TemplateInjection.qhelpServer Side Template InjectionA template from a server templating engine such as Jinja constructed from user input can allow the user to execute arbitrary code using certain template features. It can also allow for cross-site scripting. RecommendationEnsure that an untrusted value is not used to directly construct a template. Jinja also provides ExampleIn the following case, from django.urls import path
from django.http import HttpResponse
from jinja2 import Template, escape
def a(request):
template = request.GET['template']
# BAD: Template is constructed from user input.
t = Template(template)
name = request.GET['name']
html = t.render(name=escape(name))
return HttpResponse(html)
urlpatterns = [
path('a', a),
] The following is an example of a string that could be used to cause remote code execution when interpreted as a template: {% for s in ().__class__.__base__.__subclasses__() %}{% if "warning" in s.__name__ %}{{s()._module.__builtins__['__import__']('os').system('cat /etc/passwd') }}{% endif %}{% endfor %}
In the following case, user input is not used to construct the template. Instead, it is only used as the parameters to render the template, which is safe. from django.urls import path
from django.http import HttpResponse
from jinja2 import Template, escape
def a(request):
# GOOD: Template is a constant, not constructed from user input
t = Template("Hello, {{name}}!")
name = request.GET['name']
html = t.render(name=escape(name))
return HttpResponse(html)
urlpatterns = [
path('a', a),
] In the following case, a from django.urls import path
from django.http import HttpResponse
from jinja2 import escape
from jinja2.sandbox import SandboxedEnvironment
def a(request):
env = SandboxedEnvironment()
template = request.GET['template']
# GOOD: A sandboxed environment is used to construct the template.
t = env.from_string(template)
name = request.GET['name']
html = t.render(name=escape(name))
return HttpResponse(html)
urlpatterns = [
path('a', a),
] References
|
f6ef223
to
190afe6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments and suggestions here and there, but otherwise this looks good to me! 👍
/** Gets a reference to an instance of `jinja2.Environment`. */ | ||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) { | ||
t.start() and | ||
result = EnvironmentClass::classRef().getACall() | ||
or | ||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t)) | ||
} | ||
|
||
/** Gets a reference to an instance of `jinja2.Environment`. */ | ||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see why we need to use type tracking here. It looks to me like classRef().getAnInstance()
would do the same thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
In what sort of situations do we want to use type tracking as opposed to API graphs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, I would suggest using API graphs wherever possible, only using type tracking when API graphs are not sufficient. One example of where we do this is in the modelling of pathlib
, where we want to observe that the /
operator returns a Path
object. Since the API graph (at present) doesn't have any way to reference the step from a
to a / b
(say) because the latter doesn't have a node in the API graph, we have to use type tracking instead.
In the glorious future when we get API graphs à la Ruby, we should be able to do this without using type trackers. (Well, apart from the two very general type(back)trackers that the API graph calculation is built upon.)
private class Jinja2FromStringConstruction extends TemplateConstruction::Range, | ||
DataFlow::MethodCallNode | ||
{ | ||
Jinja2FromStringConstruction() { this.calls(EnvironmentClass::instance(), "from_string") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also seems like something we could push into the API graph layer, to make it slightly more general.
python/ql/test/library-tests/frameworks/django-v2-v3/template_test.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the changes. This looks good to me! 👍
Do we need a separate docs review from the docs team, or ok to merge as is? |
👋🏼 @joefarebrother I'm not entirely sure if this requires a separate Docs review, so I've asked in #code-security-docs in Slack. cc @felicitymay or @saritai |
👋🏼 @joefarebrother I've confirmed with Felicity that this needs a docs review. For the reasoning, I'll share her Slack message:
I've added this to the Docs Content review board for a writer to review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Just some very minor suggestions—thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just reapproving in case it saves you time—thank you!
`Cheetah` was excluded as it was last updated 15 years ago and its documentation links are dead.
Co-authored-by: Taus <[email protected]>
Co-authored-by: Ben Ahmady <[email protected]>
8a5e55b
to
f82fa20
Compare
Updated test outputs - just needs re-approval if tests now pass |
Promotes the Template Injection query, originally submitted to the experimental query pack here.
Fixes a modeled
Jinja2
sink to properly refer toEnvironment().from_string()
; and promotes sinks from the other template frameworks included in the experimental version.Excludes the
Cheetah
package as its documentation links are dead and its last update was 15 years ago.