Wiki source code of Security
Last modified by Eleni Cojocariu on 2026/10/13 13:29
Hide last authors
| author | version | line-number | content |
|---|---|---|---|
![]() |
18.1 | 1 | |
![]() |
1.1 | 2 | This page aims at listing the specific things that a developer should be careful with to avoid introducing new vulnerabilities in scripts and extensions. All security topic related to administration of the wiki is located in [[the security page of the administrator guide>>doc:Documentation.AdminGuide.Security]]. |
| 3 | |||
| 4 | {{box cssClass="floatinginfobox" title="**Contents**"}} | ||
| 5 | {{toc/}} | ||
| 6 | {{/box}} | ||
| 7 | |||
| 8 | == Scripting and Escaping == | ||
| 9 | |||
![]() |
5.1 | 10 | The first vulnerability in scripts is the user inputs: any input that a user can provide could possibly be an open door to an attack. Either XSS when the content is output in an HTML context, or XWiki syntax injection when the syntax is injected, e.g., in a Velocity macro where parsing of the output is enabled (the default). XWiki syntax injection almost always allows executing macros with the rights of the script's author and as this author frequently has the programming right, this gives an attacker the ability to execute arbitrary code. It doesn't matter where in the output the user's input is used. Regardless if it is in a parameter, an HTML macro or a verbatim syntax, by including the respective closing syntax, user input can always close whatever syntax it is part of. The nested script macro protection is no protection against attacks. By putting the nested script macro, e.g., inside an async macro, the nested script macro protection can easily be circumvented as the async macro has its own parsing context. |
![]() |
1.1 | 11 | |
| 12 | So in order to mitigate this, developers should always ensure to use the proper escaping mechanism. The most important ways to escape content are: | ||
| 13 | |||
![]() |
4.2 | 14 | * ##$services.rendering.escape($content, 'xwiki/2.1')## for XWiki syntax |
![]() |
1.1 | 15 | * ##$escapetool.xml($content)## for HTML output. This can also be used in an HTML macro and escapes ##{##, thereby preventing the closing of the HTML macro through user input. |
| 16 | |||
![]() |
17.1 | 17 | Use the escaping that matches the exact place where the value ends up: |
| 18 | |||
| 19 | * In an HTML attribute, use ##$escapetool.xml##, not ##$escapetool.html##, which doesn't escape the single quote. | ||
| 20 | * In a JavaScript string, use ##$escapetool.javascript## (or ##$escapetool.json## to produce a JSON value). ##$escapetool.xml## doesn't escape the backslash, so it's not enough there. Don't insert values in JavaScript template literals: no escaping tool supports them. | ||
| 21 | * In a query, don't use ##$escapetool.sql##: bind the value instead (see [[Queries>>||anchor="HQueries"]]). | ||
| 22 | * Never evaluate a value that a user can control as Velocity (e.g., with ##~#evaluate##): no escaping makes it safe. | ||
| 23 | |||
![]() |
1.1 | 24 | Make sure you always test if escaping actually protects against attacks by writing appropriate tests. |
| 25 | |||
| 26 | === ##xwiki/2.0## and ##xwiki/2.1## syntaxes === | ||
| 27 | |||
![]() |
3.2 | 28 | ==== Link label ==== |
| 29 | |||
![]() |
4.4 | 30 | In XWiki syntax 2.0 and 2.1 the link is parsed in two passes, each pass having its own escaping: |
![]() |
5.1 | 31 | |
![]() |
3.2 | 32 | * the global link is first parsed with the label being just one big string |
![]() |
4.4 | 33 | * then the label is parsed on its own |
![]() |
3.2 | 34 | |
![]() |
4.3 | 35 | What this means in practice is that when you need to escape something in the link label you need to escape it twice. For example if you want to print something that may contains wiki syntax as a link label you could do for example in Velocity: |
![]() |
3.2 | 36 | |
![]() |
4.3 | 37 | {{code language="velocity"}} |
![]() |
4.2 | 38 | #set ($userContent = '{{mymacro/}}') |
| 39 | #set ($escapedLabel = $services.rendering.escape($services.rendering.escape($userContent, 'xwiki/2.1'), 'xwiki/2.1')) | ||
| 40 | [[label $escapedLabel>>Main.WebHome]] | ||
![]() |
3.2 | 41 | {{/code}} |
| 42 | |||
| |
15.1 | 43 | {{warning}} |
| 44 | If the reference (the part after ##>>##) is dynamic, it also needs to be escaped (once) (like pretty much anything else). | ||
| 45 | {{/warning}} | ||
| 46 | |||
![]() |
1.1 | 47 | ==== Verbatim ==== |
| 48 | |||
| 49 | It's impossible to properly escape content to put in [[verbatim syntax>>https://jira.xwiki.org/browse/XRENDERING-686]] in syntaxes ##xwiki/2.0## and ##xwiki/2.1##. | ||
| 50 | |||
| 51 | The simplest for this kind of use case is generally to use something like: | ||
| 52 | |||
| 53 | {{code language="velocity"}} | ||
| 54 | (%class="code"%)((( | ||
| 55 | $services.rendering.escape($doc.content, 'xwiki/2.1') | ||
| 56 | ))) | ||
| 57 | {{/code}} | ||
| 58 | |||
| 59 | ==== Code macro ==== | ||
| 60 | |||
| 61 | It's impossible to properly escape content to put in [[code macro>>https://jira.xwiki.org/browse/XRENDERING-13]] in syntaxes ##xwiki/2.0## and ##xwiki/2.1##. | ||
| 62 | |||
| 63 | There are several alternatives if you need to highlight user input: | ||
| 64 | |||
| 65 | {{version since="14.10.2"}} | ||
| 66 | Use the ##source## code macro parameter: instead of trying to escape the content you provide only a reference of a the content to highlight so no need to escape it anymore. | ||
| 67 | |||
| 68 | See [[extensions:Extension.Code Macro||anchor="HSource"]] for more details. | ||
| 69 | {{/version}} | ||
| 70 | |||
| 71 | {{version before="14.10.2"}} | ||
| 72 | An alternative if you need to highlight user input is to return directly the MacroBlock instead of serializing it. For example in Groovy: | ||
| 73 | |||
| 74 | {{code language="groovy"}} | ||
| 75 | return new org.xwiki.rendering.block.MacroBlock("code", ["language": "xml"], userInput, false) | ||
| 76 | {{/code}} | ||
| 77 | {{/version}} | ||
| 78 | |||
![]() |
12.3 | 79 | === Translations === |
![]() |
7.1 | 80 | |
![]() |
13.1 | 81 | Translation values are often inserted in content that follows a specific syntax (wiki content, HTML, etc.), which means the translation is often re-parsed for this content syntax. Treat every translation value as untrusted and escape it when you render it for two reasons: |
![]() |
13.4 | 82 | |
| 83 | * **Privilege escalation.** A translation value can contain injection ({{code language="none"}}{{groovy}}{{/code}}, {{code language="none"}}{{html}}{{/code}}, ...). Registering a translation that affects other users requires elevated rights (##GLOBAL## scope needs Programming Right, ##WIKI## scope needs Wiki Admin Right). A ##USER## scope translation only needs Script Right, but that is still dangerous: a user with only Script Right can register such a translation, and if it is then rendered in a request handled for a user who has Admin or Programming rights, the injected macro executes with those rights. In other words, leaving a translation value unescaped turns Script Right into a path to Admin/Programming Right. | ||
![]() |
13.1 | 84 | * **Broken display.** A translation value may legitimately contain characters that are conflicting with the syntax of the context in which it's inserted ({{code language="none"}}*{{/code}}, {{code language="none"}}[[{{/code}}, {{code language="none"}}<{{/code}}, ...). Unless escaped, it might break the display of the whole content around the translation. |
![]() |
12.1 | 85 | |
![]() |
13.1 | 86 | ==== Use the ##~{~{translation}}## macro when you can ==== |
![]() |
12.1 | 87 | |
![]() |
13.4 | 88 | The best in terms of performance and security is to use the [[~~{~~{translation}}## macro##>>extensions:Extension.Translation Macro]]. The reason is that in that case the translation is not parsed but directly inserted into the XDOM. |
![]() |
7.1 | 89 | |
![]() |
13.1 | 90 | {{version since="16.10.18,17.10.9,18.4.0RC1"}} |
| 91 | But it can be hard to properly escape the parameter values to pass to the macro, to avoid injection through translation parameters. | ||
![]() |
7.1 | 92 | |
![]() |
13.1 | 93 | In such a case, the translation macro allows passing the name of the variable that contains the list of translation parameters: |
| 94 | |||
![]() |
8.1 | 95 | {{code language="none"}} |
![]() |
7.1 | 96 | {{velocity}} |
| 97 | #set ($txparameters = [$userinput, 42]) | ||
![]() |
11.1 | 98 | {{translation key="some.translation" scriptParameters="txparameters"/}} |
![]() |
7.1 | 99 | {{/velocity}} |
| 100 | {{/code}} | ||
![]() |
13.1 | 101 | {{/version}} |
![]() |
12.6 | 102 | |
![]() |
13.1 | 103 | {{version since="16.10.18,17.10.10,18.4.1,18.5.0RC1"}} |
| 104 | A helper for Velocity is provided to make inserting a translation macro with potential user input even easier and more readable: | ||
![]() |
12.6 | 105 | |
![]() |
13.1 | 106 | {{code language="none"}} |
| 107 | {{velocity}} | ||
| 108 | #wikiTranslation('some.translation', [$userinput, 42]) | ||
| 109 | {{/velocity}} | ||
![]() |
12.6 | 110 | {{/code}} |
![]() |
7.1 | 111 | {{/version}} |
| 112 | |||
![]() |
13.1 | 113 | ==== When you cannot use the ##~{~{translation}}## macro ==== |
![]() |
12.1 | 114 | |
![]() |
13.2 | 115 | So when you are not in a wiki context, or when you're coding for an older version of XWiki (i.e. before 16.10.18,17.10.9,18.4.0RC1) for which passing translation parameters to the ~{~{translation}} macro is too complex (you'd need to escape each parameter separately), do the following: |
![]() |
12.1 | 116 | |
![]() |
13.2 | 117 | * Escape the entire ###render## output. This protects against both injection through translation value and through translation parameters:((( |
![]() |
13.1 | 118 | {{code language="velocity"}} |
| 119 | {{html}} | ||
| 120 | $escapetool.xml($services.localization.render('myapp.key', [$param])) | ||
| 121 | {{/html}} | ||
| 122 | |||
![]() |
13.3 | 123 | ## To be used only for older XWiki versions, see above. For new versions, use the translation macro. |
![]() |
13.1 | 124 | $services.rendering.escape($services.localization.render('myapp.key', [$param]), 'xwiki/2.1')) |
![]() |
12.7 | 125 | {{/code}} |
![]() |
12.1 | 126 | ))) |
![]() |
13.1 | 127 | * Do not rely on translations containing specific syntax (wiki markup, HTML, etc). Aside from making it much more complex for contributors to translate it, it makes it impossible to properly escape it. To wrap translated text in a link or other markup, build the markup in your (trusted) code and put the escaped translation inside it. For example, in the case of a link, having only the link label in the translation:((( |
![]() |
12.1 | 128 | {{code language="velocity"}} |
![]() |
13.1 | 129 | {{html}} |
| 130 | <a href="$link">$esapetool.xml($services.localization.render('myapp.linkLabel', [$param]))</a> | ||
| 131 | {{/html}} | ||
| 132 | |||
| 133 | ## Link label requires double escaping in xwiki/2.1 syntax, see the "Link label" section for more details | ||
![]() |
13.2 | 134 | ## To be used only for older XWiki versions, see above. For new versions, use the translation macro. |
![]() |
13.4 | 135 | [[$services.rendering.escape($services.rendering.escape($services.localization.render('myapp.linkLabel', [$param]), 'xwiki/2.1'), 'xwiki/2.1')>>$target]] |
![]() |
12.1 | 136 | {{/code}} |
| 137 | ))) | ||
![]() |
13.1 | 138 | * When you cannot escape the whole result (which should be avoided, as mentioned above), at least escape the parameters that might come from user input before passing them to #render. |
![]() |
12.1 | 139 | |
![]() |
16.1 | 140 | == Queries == |
| 141 | |||
| 142 | Never concatenate a value into an XWQL/HQL query string, always bind it: | ||
| 143 | |||
| 144 | {{code language="velocity"}} | ||
| 145 | ## BAD | ||
| 146 | #set ($q = $services.query.xwql("from doc.object(Blog.BlogPostClass) as post where post.category = '$request.category'")) | ||
| 147 | |||
| 148 | ## GOOD | ||
| 149 | #set ($q = $services.query.xwql("from doc.object(Blog.BlogPostClass) as post where post.category = :category").bindValue('category', $request.category)) | ||
| 150 | {{/code}} | ||
| 151 | |||
| 152 | Binding only covers values. The structural parts of a query have no escaper at all and must be written literally: the class/space/page identifiers in ##from## and ##doc.object(...)##, ##where doc.space = ...##, the ##order by## column. | ||
| 153 | |||
| 154 | {{code language="velocity"}} | ||
| 155 | ## BAD, even though the value is bound | ||
| 156 | "from doc.object(${space}.Code.EntryClass) as entry where entry.author = :author" | ||
| 157 | |||
| 158 | ## GOOD | ||
| 159 | "from doc.object(Blog.Code.EntryClass) as entry where entry.author = :author" | ||
| 160 | {{/code}} | ||
| 161 | |||
| 162 | Such an injection is worth exploiting even when the query result is not displayed: the query is executed with the rights of the script's author and its results are not filtered by the view rights of the current user. | ||
| 163 | |||
| 164 | If a variable really must appear in a structural position, restrict it to an allow-list or to a strict pattern with a safe fallback, for example ##$space.matches('[a-zA-Z0-9_-]+')##. In a wiki macro, beware that ##$doc## is the page being rendered, i.e. chosen by whoever calls the macro, whereas ##$wikimacro.doc## (also available as ##$xcontext.macro.doc##) is the page where the macro is defined and is thus trusted. | ||
| 165 | |||
![]() |
6.1 | 166 | == HTML == |
![]() |
1.1 | 167 | |
![]() |
6.1 | 168 | While ##$escapetool.xml($content)## is enough for escaping user input/untrusted text that is simply displayed, this is not necessarily the case for values that are used in attributes that have special meaning like the target of a link. There, even a fully escaped string could be interpreted as a script when the user clicks on it. In XWiki syntax, all attributes are automatically validated, but this is not the case for HTML in HTML macros with script right or Velocity templates. You can use ##$services.html.isAttributeSafe($htmlElement, $attributeName, $attributeValue)## to check if a certain attribute with a certain value is safe according to the [[HTML cleaning configuration>>extensions:Extension.XML Module||anchor="HHTMLCleaning"]]. This returns ##false## for script-URLs on links for example. See also the section on [[HTTP requests and redirects>>||anchor="#HHTTPRequestsandRedirects"]] for extra precautions that you should take if the user doesn't expect to land on a different domain when clicking on a link/button, like a "Cancel" button. |
| 169 | |||
![]() |
17.1 | 170 | The cleaning done by the HTML macro (##clean="true"##, the default) is not a replacement for escaping. When the author of the content has script right, which is always the case for HTML produced by a Velocity script, the cleaning only makes the HTML valid: it doesn't remove scripts or event handlers. So always escape the values you insert in HTML, even inside an HTML macro. |
| 171 | |||
![]() |
6.1 | 172 | == Restricted Mode in Macros == |
| 173 | |||
![]() |
18.1 | 174 | As explained in details [[in the documentation for writing macros>>xwiki:documentation.xs.dev.rendering-macros.macro-execution-rules.WebHome]], the Macro Transformation Context can be set in "Restricted Mode" and in your macro should respect this parameter (by checking ##macroContext.getTransformationContext().isRestricted()##) and either not execute at all or execute in a restricted mode. |
![]() |
1.1 | 175 | |
| 176 | == Protect against XXE attacks == | ||
| 177 | |||
| 178 | Always follow the [[OWASP recommendations to protect against XXE attacks>>https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html]] when parsing XML. | ||
| 179 | |||
![]() |
17.1 | 180 | In practice, don't create XML parsers (##XMLInputFactory##, ##DocumentBuilderFactory##, ##SAXParserFactory##, etc.) yourself: use the helpers of the [[XML Module>>extensions:Extension.XML Module]], ##StAXUtils.getXMLStreamReader(...)## or ##XMLUtils.parse(...)##, which are already configured not to load external DTDs and entities. If you really need your own parser, disable DTDs and external entities explicitly: enabling ##XMLConstants.FEATURE_SECURE_PROCESSING## is not enough with every parser implementation. |
| 181 | |||
![]() |
1.1 | 182 | == Right Checks in Script Services == |
| 183 | |||
| 184 | Any code that is exposed as a script service needs to check the rights of the context **author**, i.e., the user who wrote the script, in addition to the context **user**, i.e., the user that is accessing the script. Currently, the only easy way to check rights of the context author is to check script or programming right using a contextual authorization manager. It takes important context information into account like if permissions have been dropped. In addition to that, all right checks that are done for the context user should be duplicated for the context author to ensure that no CSRF attacks are possible, i.e., there is no way to, e.g., write a script that executes a dangerous action as soon as a user with more rights accesses the document. It needs to be taken care that if permissions have been dropped, the context author cannot be trusted and thus no dangerous actions must be performed and no sensitive information must be disclosed. | ||
| 185 | |||
| 186 | If the script service exposes information or executes actions without further right checks, it must check for programming right of the context author. | ||
| 187 | |||
![]() |
17.1 | 188 | Always pass the entity on which you check a right, instead of relying on the default: |
| 189 | |||
| 190 | * Without an entity, ##hasAccess(Right.ADMIN)## (as well as ##$xwiki.hasAdminRights()## and ##$hasAdmin##) checks the right on the current document, so it's also true for someone who only administers the space of the current page. To check wiki administration, pass the wiki: ##hasAccess(Right.ADMIN, new WikiReference(wikiId))##, or use ##$xwiki.hasWikiAdminRights()##. | ||
| 191 | * For script and programming rights, the entity decides whose rights are checked: ##hasAccess(Right.SCRIPT, reference)## checks the content author of the document at ##reference##, not the author of the running script. To check the author of the running script, don't pass an entity. To check a given user on a given entity, use ##AuthorizationManager#hasAccess(right, user, entity)##. | ||
| 192 | |||
| 193 | Programming right can only be granted on the main wiki: an administrator of a subwiki has script right but never programming right. Don't require programming right for something that subwiki administrators should be able to do, and do require it for anything they must not be able to do. | ||
| 194 | |||
![]() |
1.1 | 195 | Note that context author rights are currently not consistently enforced in XWiki, in particular there is no such concept in JavaScript. This is an area for future improvements, new code should still take context author rights into account. |
| 196 | |||
| 197 | == Returning Data in Script Services == | ||
| 198 | |||
| 199 | When returning any object in a script service, ensure that all its methods properly check access rights and don't allow modifying data without proper access right checks. Use wrapper objects to add right checks or hide dangerous methods. For example, returning an ##XWikiDocument## is not safe as it allows modifying author information and executing the content with the new author. | ||
| 200 | |||
![]() |
17.1 | 201 | == Saving Documents in Script Services == |
| 202 | |||
| 203 | Script services, and any other API that scripts can call, must never save a document with the current user as author when the author of the calling script doesn't have programming right. Otherwise, a script that a user with more rights merely views saves content in that user's name, and that content then executes with that user's rights. The public ##Document#save()## API already behaves this way: without programming right, it saves the document with the script's author as author. Reuse it, or apply the same check. | ||
| 204 | |||
![]() |
1.1 | 205 | == Executing Code or XWiki Syntax == |
| 206 | |||
| 207 | If possible, it should be avoided to introduce new code that directly executes (Velocity) code or XWiki syntax with transformations. Instead, existing APIs should be used, e.g., displaying a text area property instead of manually parsing the content of the property. If this should still be necessary, it is of utmost importance to: | ||
| 208 | |||
| 209 | * Check that the author of the code has script right. Make sure you check the content author if the code is in the content of the document and the effective metadata author if the code is in an XObject. | ||
| 210 | * Execute the code or transformations with the correct author in context. In Java, ##AuthorExecutor## should be used for this. There is no way to do this in Velocity. There are hacks like ##dropPermissions## but they are prone to security vulnerabilities and should thus be avoided. | ||
![]() |
2.1 | 211 | |
![]() |
17.1 | 212 | To render an XObject property, display it, e.g., with ##$doc.display('property', $object)##: it's then executed with the rights of the effective metadata author of its document. Never insert the raw value of a property (##$object.getValue('property')##) in content that is parsed as XWiki syntax or evaluated as Velocity, since it would then be executed with the rights of the author of the page doing the rendering. Note that, despite its name, ##$object.get('property')## returns the displayed property and not its value: use ##getValue## to read or compare the value. |
| 213 | |||
![]() |
6.1 | 214 | == Safe Evaluation of Objects == |
![]() |
2.1 | 215 | |
| 216 | {{version since="14.10.21,15.5.5,15.10.2"}} | ||
![]() |
3.1 | 217 | A new API has been introduced, allowing to specify exactly the XObjects properties that needs to be evaluated and how. |
![]() |
2.1 | 218 | |
![]() |
3.1 | 219 | This includes a new [[ObjectEvaluator>>https://github.com/xwiki/xwiki-platform/blob/xwiki-platform-15.10.2/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/evaluation/ObjectEvaluator.java]] component, that can be implemented with a hint matching the name of the XClass for which a safe evaluation is required. Implementations of [[ObjectPropertyEvaluator>>https://github.com/xwiki/xwiki-platform/blob/xwiki-platform-15.10.2/xwiki-platform-core/xwiki-platform-oldcore/src/main/java/org/xwiki/evaluation/ObjectPropertyEvaluator.java]] can be used to provide different evaluation strategies: an existing implementation already exists for Velocity evaluation of properties, with the hint ##velocity##. For a full example of an ##ObjectEvaluator## component, see [[the one for SearchSuggestSourceClass XClass>>https://github.com/xwiki/xwiki-platform/blob/xwiki-platform-15.10.2/xwiki-platform-core/xwiki-platform-search/xwiki-platform-search-api/src/main/java/org/xwiki/search/internal/SearchSuggestSourceObjectEvaluator.java]]. |
![]() |
2.1 | 220 | |
![]() |
3.1 | 221 | Finally once the component is declared, the object properties can be safely evaluated by calling ##evaluate()## on the object instance, which will return the map of evaluated properties. |
![]() |
2.1 | 222 | {{/version}} |
![]() |
6.1 | 223 | |
| 224 | == HTTP Requests and Redirects == | ||
| 225 | |||
| 226 | Whenever the user is redirected to a URL or HTTP requests are performed on the server side, extra care is needed to avoid introducing an [[open redirect>>https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html]] or [[server side request forgery (SSRF)>>https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html]] vulnerability. While the perfect mitigation is to simply never use user-provided data in redirects and HTTP requests, this is sometimes not possible, in particular if backwards-compatibility needs to be maintained. For this reason, XWiki provides the [[possibility for admins to configure a list of trusted domains and methods to validate a URL>>doc:extensions:Extension.URL API||anchor="HSecurity"]] - both in Java and as a script service that can be used in Velocity. Note that there are [[differences in URL parsing that can be exploited>>https://claroty.com/team82/research/exploiting-url-parsing-confusion]], as the validation might see one domain, but the browser actually uses a different domain. For this reason, the provided method ##parseToSafeURI ##doesn't just validate the passed URL but instead returns the parsed URL to ensure that the URL is interpreted as validated. | ||
| 227 | |||
| 228 | The same that applies to redirections also applies to URLs that are used on buttons in the UI where the user doesn't expect to land on a different domain/website, like "Cancel" buttons. There, in addition to unexpected/malicious target websites, XSS is also a topic. For this purpose, the [[###getSanitizedURLAttributeValue## macro>>doc:extensions:Extension.Velocity Macro.Macros.getSanitizedURLAttributeValue.WebHome]] has been introduced that both checks if the attribute is safe, i.e., not a script-URL, and if it is linking to a trusted domain (or the current domain). You can find an example use in [[restore.vm>>https://github.com/xwiki/xwiki-platform/blob/0ae047883e486d601e45b9dc388c7ce5696b5e57/xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/restore.vm#L215-L220]]. | ||
| 229 | |||
![]() |
14.1 | 230 | == Access environment/resource files |
| 231 | |||
| 232 | When accessing a file in the servlet resource based on user input, there is a risk that the user input contains a path traversal attack. That means the value contains things like ##../..## to try to go out of the exepcted folder. | ||
| 233 | |||
| 234 | {{version since="17.10.5, 18.2.0"}} | ||
| 235 | To help protect against this kind of attack, it is recommended to avoid directly accessing files through the servlet API and instead use the ##org.xwiki.environment.Environment## API, and especially ##Environment#getResourceAsStream(String prefixPath, String resourcePath)## which allows indicating a parent folder in which the resource should be located, which is a very common use case. ##Environment#getResourceAsStream(String resourcePath)## will make sure the resource is not trying to go outside of the root resource folder (the root of the web application). | ||
| 236 | |||
| 237 | {{warning}} | ||
| 238 | But in some use cases (like the XWiki Debian packages) some of those resources are actually stored in other locations (like /etc/xwiki/) and then the web application folder contains links to those files. Tomcat (and probably others too) tend to report the real path of the resource behind the link and not the location of the link itself, effectively making the resource identified as being outside the resource root. | ||
| 239 | The property ##environment.servlet.allowedRealPaths## in the configuration file ##xwiki.properties## allows adding more allowed locations. | ||
| 240 | {{/warning}} | ||
| 241 | {{/version}} |





