Part of meta-issue #1788918: [GTD] [META] Prepare for Twig main engine core inclusion until December 1
Follow-up from: #1696786: Integrate Twig into core: Implementation issue
Motivation
100% of all custom themes are insecure (the rest is rounding error). The reason is often that developers forget to escape their data in the templates.
Problems
- To use twig autoescape every string that has been escaped already needs to be marked as "safe", so that it does not get escaped twice.
- Concatenation of a safe string is an unsafe string
- Different escaping strategies need to be used for different contexts (html, css, js, html attributes)
- Our implementation of auto-render makes the optimized |raw filter usage not possible
Approaches available
- a) Have Twig handle security internally and assume every string in $variables is secure (not true for objects). That would keep backwards compatible options to also have node.tpl.php etc. templates secure while still preventing accidental XSS via node.title or db_query in templates.
- b) Mark things that are secure, as such by utilizing a TypedData SafeString either automatically or manually.
For both variants are two possible implementations: Implicit and Explicit
- a) 1) Preprocess variables before rendering of template and mark every string as secure, needs only is_object check.
- => Has some performance overhead, but simple todo.
- a) 2) Mark strings secure as they are gathered from _context, by overriding getAttribute and getContext and set $this->secure = TRUE in getContext and $this->secure = FALSE in getAttribute if an object is encountered, wrap every encountered String as a Twig_Markup.
- => Compile time overhead, but little less run time overhead.
- b) 1) Do it like we do now and have an escaped string remain secure until it is concatenated.
- => Needs fixing of many tests, change from untyped strings to typed strings.
- b) 2) Explicitly use drupal_mark_safe(check_plain($node->title)) in the preprocess_ and process_ functions and wherever something is rendered to be output in a template.
- => Overhead in preprocess, process, etc. functions. They all need to be adjusted. Still needs typed SafeStrings. Developers need to learn to mark strings as safe for output as well as escaping.
Solution
- Wrap everything that is returned from check_plain, check_markup, etc. in Twig_Markup() objects.
- Tell Developers to wrap the concatenated escaped strings again in a Twig_Markup to mark it as safe again
- Document clearly that developers need to take care of security escaping themselves when using inline-css and inline-script with user input (better not do this at all)
- Fix the raw filter to explicitly return a Twig_Markup object instead of compile time optimizing it away.
Advantages
- Accidental XSS is caught (refer to #1811684: XSS: Bartik's node.tpl.php prone to XSS (prints $title)) as long as twig templates are used
- Strings are not escaped twice
Limitations
Not solved here are (currently?):
- Auto-escaping in different contexts (JS, CSS) though it can be done manually via ({% autoescape js/css %}) tags
- Users putting non-escaped data in #prefix / #suffix or via #theme_wrapper
- The solution is only as secure if all "marked safe" parts are secure (if all theme_ functions are gone and all goes via twig it is secure)
- No auto-escape for .tpl.php files and it needs to handle Twig_Markup objects
- Might need a function like is_twig_string for that
Disadvantages
- Performance: Obviously wrapping in Twig_Markup and doing __toString has overhead. Lets find out how worse it is.
Original report
Right now, we can't autoescape in twig because we are still calling drupal_render as an interim solution. When drupal_render concatenates it's results it loses the metadata containing the information whether a string is already escaped or not.
We need to replace or rewrite drupal_render so that the escaped strings are stored in a Twig_Markup object instead of being lost when flattened. When this is done we can turn autoescaping back on in twig.
Comments
Comment #1
jenlamptonImportant tagging
Comment #2
jenlamptonadding tag
Comment #3
Fabianx CreditAttribution: Fabianx commentedWe will also need to make for example $vars['css'] in header safe by wrapping them as Twig_Markup.
To properly support easy concatenation we could use something like: #1788192: Prepare autosanitization
It might be that we need to extend Twig_Markup as Drupal_Safe_Markup to do some magic things like storing a series of:
Safe|Unsafe|Safe etc.
But this should be discussed here.
Comment #4
Fabianx CreditAttribution: Fabianx commentedFirst patch: DONE
We have auto escaping in the twig_engine branch of the sandbox now.
Comment #5
Fabianx CreditAttribution: Fabianx commentedHijacking this issue for now to followup / fork() from main integration issue.
Comment #5.0
Fabianx CreditAttribution: Fabianx commentedUpdated issue summary.
Comment #6
Fabianx CreditAttribution: Fabianx commentedAnd here is the first patch against our core issue.
Again this is a double patch, so please review only [PATCH] 2/2 (Ctrl-F 2/2).
@todo's here
Remove: User error: "content" is an invalid render array key in element_children() (line 6251 of core/includes/common.inc).The plans to increase performance are:
Possible followups for security:
Comment #9
Fabianx CreditAttribution: Fabianx commentedTestbot: Get to work!
Comment #10
Fabianx CreditAttribution: Fabianx commentedAnother one for the testbot only.
Comment #12
Fabianx CreditAttribution: Fabianx commentedAnd another one for testbot, really white space that is not white space is "meh".
Comment #14
Fabianx CreditAttribution: Fabianx commentedTry again ...
Comment #16
Fabianx CreditAttribution: Fabianx commented.
Comment #18
Fabianx CreditAttribution: Fabianx commentedExceptions testing ...
Comment #20
Fabianx CreditAttribution: Fabianx commentedAnother one
Comment #21
Fabianx CreditAttribution: Fabianx commented.
Comment #23
Fabianx CreditAttribution: Fabianx commentedJust trying to pass tests once.
Comment #24.0
(not verified) CreditAttribution: commentedUpdate issue summary
Comment #25
Fabianx CreditAttribution: Fabianx commentedV3 from a secure theme system. Lets see what testbot means ...
Comment #26
Fabianx CreditAttribution: Fabianx commentedMerged back into #1696786: Integrate Twig into core: Implementation issue
Comment #27
Fabianx CreditAttribution: Fabianx commentedComment #28
Fabianx CreditAttribution: Fabianx commentedRe-titling
Comment #29
mbrett5062 CreditAttribution: mbrett5062 commented@Fabianx, did you mean to leave this 'active'.
Seems to me the patch here was merged back into [#1696706], and that issue is closed 'fixed'.
So can we close this as duplicate again, with your revised title? O do you intend to do more with it?
Comment #30
Fabianx CreditAttribution: Fabianx commented#29: Auto escape was not enabled in the original patch. It was stripped out again.
Will work on it once feature freeze is over and we have "rely on autoescape patch progress".
Comment #31
jenlamptonRemoving awesome tag.
Comment #32
David_Rothstein CreditAttribution: David_Rothstein commentedThere are some other issues open that seem closely related to (or the same as) this one:
#1818266: [meta] A secure theme system (with twig)
#1825952: Turn on twig autoescape by default
Does it make sense to keep this one open too?
Comment #33
moshe weitzman CreditAttribution: moshe weitzman commentedI don't think it does.
Comment #33.0
moshe weitzman CreditAttribution: moshe weitzman commentedAdd new methods of doing secure markup