This document is provided as a supplement to Security for developers.
This is a list of common development tasks, and the security measures that need to be taken.
Security checklist
If you are working with ...
have you ...
Browser Cookies
reduced reviewer anxiety by using $wgRequest instead of $_COOKIE?
fetched cookies using $wgRequest->getCookie(...)?
set cookies using $wgRequest->response()->setCookie(...)?
# Attempt to fetch the UserID cookie value.# Note: the value returned isn't trusted and is forced to be an int.$sId=intval($wgRequest->getCookie('UserID'));
it's easier to put arbitrary strings into text processed by regular expressions, which – when combined with the /e pattern modifier – can lead to code injection attacks.
it is harder to read and maintain code that is part of a string.
static analysis tools won't catch warnings and errors in the code.
opcode caches (like APC) can't cache code mixed into strings.
create_function() sometimes has garbage-collection issues.
A loop which has a create_function() inside will create a new function on each iteration.
Sometimes you really do need these features (obviously eval.php needs to run eval();) but in most cases, we'd rather see the function broken out and referred as a callback.
Inline lambda functions will make it easier to make your callback inline while retaining the benefits of code that's written in native syntax instead of strings.
Anything external that is used in part of regex should be escaped with preg_quote ($externalStr, $delimiter). It puts a backslash in front of every character that is part of the regular expression syntax, and escapes also the delimiter given as second parameter:
executed the program via Shell::command() from namespace MediaWiki\Shell?
quoted all arguments to external programs using the above's secure parameter passing facilities (which is basically everything except for unsafeParams())?
// Automatically escape any naughty characters$result=Shell::command($cmd,'--version')->params('some','extra','parameters')->execute();
Note that old wfShellExec()/wfEscapeShellArg() are not recommended because they make it easier for developers to miss escaping a parameter.
# rawElement() escapes all attribute values# (which, in this case, is provided by $myClass)echoHtml::rawElement('p',['class'=>$myClass]);
reduced reviewer anxiety by using ResourceLoader to deliver CSS and JavaScript resources?
User provided CSS
User provided CSS (Say for use in a style attribute) needs to be sanitized to prevent XSS, as well as to disallow insertion of tracking images (via background-image), etc
Use the Sanitizer::checkCss method for any css received from a user, possibly along with the Html class.
# let $CSSFromUser be the user's CSS.echoHtml::rawElement('p',['style'=>Sanitizer::checkCss($CSSFromUser)]);
For CSS provided by the extension (and not the user), this is not needed (and will remove some valid things like background-image:). However, extension provided CSS should go in stylesheets loaded by ResourceLoader, and not in style attributes.
POST data
reduced reviewer anxiety by using $wgRequest instead of $_POST
Always validate that any POST data received is what you expect it to be
# Check if the action parameter is set to 'render'if($wgRequest->getVal('action')=='render'){...
Some of these issues can be checked with phan-taint-check-plugin, which is required for all MediaWiki code in Wikimedia production.
This is of course just a tool, and it cannot detect all issue types, and may miss issues even in the issue types it can check for.