class syntax_plugin_bold extends DokuWiki_Syntax_Plugin {
// common plugin functions ommited
public function connectTo($mode) {
$this->Lexer->addSpecialPattern('!!!.*?!!!', $mode, 'plugin_bold');
}
public function handle($match, $state, $pos, Doku_Handler $handler){
return [substring($match, 3, -3)];
}
public function render($format, Doku_Renderer $renderer, $data) {
if($format != 'xhtml') return false;
$renderer->doc .= '' . $data[0] . ''; // no escaping
}
}
As you can see, the raw input data captured in the lexer pattern is just passed on to the render method, where no escaping at all is done. Malicious users could introduce what ever JavaScript and HTML code they want.
The fix is simple: proper escaping.
class syntax_plugin_bold extends DokuWiki_Syntax_Plugin {
// common plugin functions ommited
public function connectTo($mode) {
$this->Lexer->addSpecialPattern('!!!.*?!!!', $mode, 'plugin_bold');
}
public function handle($match, $state, $pos, Doku_Handler $handler){
return [substring($match, 3, -3)];
}
public function render($format, Doku_Renderer $renderer, $data) {
if($format != 'xhtml') return false;
$renderer->doc .= '' . htmlspecialchars($data[0]) . ''; //escaping
}
}
=== Forms ===
When your plugin provides a form it is very common to validate the input and redisplay the form with the received user input when a validation error occurs.
Example: The following shows a form vulenerable to an XSS attack because it does not escape the user provided input correctly:
Providing ''%%">%%'' as user input would exploit the vulnerability.
To fix the form use the [[phpfn>htmlspecialchars|htmlspecialchars()]] or DokuWiki shortcut [[xref>hsc|hsc()]] function:
In general it is recommended to not hand-craft forms, but use DokuWiki's [[form|form library]].
=== Classes and other Attributes ===
Often plugins will accept multiple parameters and options that are used to modify the output of the plugin.
Imagine a plugin accepting the following input to display a message box:
Do not believe anything!
In the render method of this syntax there might be code like this:
$renderer->doc .= '' //$class can be everything
. htmlspecialchars($message)
. '';
As you can see the message itself is properly escaped, but the class is not. Instead of escaping it might be more sensible to use a whitelist of allowed classes instead with a default fallback:
$allowed = ['notice', 'info', 'warning', 'error']; // whitelist
if(!in_array($class, $allowed){
$class = 'notice'; // unknown input, fall back to a sane default
}
$renderer->doc .= ''
. htmlspecialchars($message)
. '';
===input URLs ===
When a plugin accepts URLs as input you need to make sure, users can not pass the ''%%javascript://%%'' pseudo protocol.
Here is an example how a very simple check could look like, to make sure only http and https URLs are used.
// empty URL on protocol mismatch
if(!preg_match('/^https?:\/\//i', $url)) {
$url = '';
}
===== Cross Site Request Forgery (CSRF) =====
This vulnerability often appears into plugins due to the lack of understanding of this issue, often confused with the XSS.
Cross Site Request Forgery refers to an attack where the victim's browser is tricked by a malicious site to ask for a page on a vulnerable site to do an unwanted action. The attack assumes the victim's browser has credentials to change something on the vulnerable site.
===Adding security token===
DokuWiki offers functions to help you deal against CSRF attacks. [[xref>getSecurityToken()]] will create a token that should be used to protect any authenticated action. It has to be included in links or forms triggering that action. All forms created with the [[form|form library]] will have security tokens added automatically, for handcrafted forms the [[xref>formSecurityToken()]] function can be used.
It is your resposibility as the plugin author to actually check the token before executing authorized actions using the [[xref>checkSecurityToken()]] function.
==See also==
* [[wp>Cross Site Request Forgery]]
* [[https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29|OWASP explanation]]
==== Typical Vulnerability Example ====
Below is the simplest example to start. You may have a more complicated plugin of your own to secure, here is just a simple example based on form.
Imagine you want to know something which can be answered to Yes or No, you would have a form of this type:
Then you process this form as follows:
global $INPUT;
if($INPUT->get->has('yn')){
do_something_with_yn($INPUT->get->str('yn'));
}
So a user is connected to answer this question, but he doesn't know the response yet. Let's take time to think and browse the web...
Now the user is visiting a malicious website, one which know, or not, that the user may be connected to your DokuWiki. In this website, the developer included this HTML image tag:
What will the user's browser do then?
The browser will process this image as any other and will send a request to this URL. Your plugin will then see that ''$_GET['yn']'' is set and will call the ''do_something_with_yn()'' function.
That's one of the examples of CSRF. Now, how to fix this security hole?
==== Prevent CSRF ====
Remember your form above? Let's add an input in it:
Do you see the first input? Yes? Good. Now you have to check the security token when you receive the form, before processing it:
global $INPUT;
if($INPUT->get->has('yn') && checkSecurityToken()) {
do_something_with_yn($INPUT->get->str('yn'));
}
As the malicious website will never find the value of the "sectok" hidden input, your form is no longer vulnerable to CSRF.
**Note:** If the security token is not valid, the ''checkSecurityToken()'' function displays a message which informs the user.
===== Remote Code Inclusion =====
This attack allows an attacker to inject (PHP) code into your application. This may occur on including files, or using unsafe operations functions like [[phpfn>eval()]] or [[phpfn>system()]].
**Always filter any input** that will be used to load files or that is passed as an argument to external commands.
===== Information leaks =====
This attack may lead to the exposure of files that should usually be protected by DokuWiki's ACL or it might expose files on the server (like ''/etc/passwd'').
**Always filter any input** that will be used to load files or that is passed as an argument to external commands.
**Always use DokuWiki's ACL check functions when accessing page data**.
===== SQL injection =====
This attack is rarely relevant in DokuWiki because no database is used. However if your plugin accesses a database always escape all values before using them in SQL statements.
More info:
* [[wp>SQL injection]]
===== Reporting Security Issues =====
If you encounter an issue with a plugin please inform the author of the plugin via email, optionally putting [[andi@splitbrain.org|Andi]] or the [[:mailinglist]] on CC.
Additionally a ''securityissue'' field with a short description of the problem should be added to the [[plugin:repository|data]] on the page of the plugin. This will create a red warning box and will delist the plugin from the main plugin list.
Once the issue was fixed and a new release was made, this field should be removed again.