Commit bd606a8f authored by Carsten  Rose's avatar Carsten Rose
Browse files

Security: Encoding and Honepot vars.

Manual.rst: small abstract about implemented security enhancements in QFQ.
Sanatize.php: New function urlDecodeArr(). Decode all _GET vars.
AbstractBuildForm.php, BuildFormBootstrap.php: form head now contains the honeypot vars.
parent 4fb3c33c
...@@ -496,6 +496,50 @@ Sanitize class ...@@ -496,6 +496,50 @@ Sanitize class
| **all** | Form | Query | no sanitizing | | **all** | Form | Query | no sanitizing |
+------------------+------+-------+-----------------------------------------------------------------------------------------+ +------------------+------+-------+-----------------------------------------------------------------------------------------+
Security
========
All values passed to QFQ will be:
* UTF8 normalized (normalizer::normalize) to unify different ways of composing characters,
*
Get Parameter
-------------
Get parameter might contain urlencoded content (%xx). 'urldDecode' all GET parameter.
Post Parameter
--------------
Per FormElement (HTML input) the default is to htmlspecialchars() the input. This means &<>'" will be encoded as htmlentity
and saved as a htmlentity in the database. In case quotes or some of the others characters (e.g. for HTML tags) are
required, the encoding can be disabled per FormElement. During Form load, the htmlentities are decoded.
$_SERVER
--------
All $_SERVER vars are htmlentities (all, not only specialchars) encoded.
Honeypot
--------
Every QFQ Form contains 'honeypot'-HTML input elements (hidden & readonly). Which to use is configured in `config.qfq.ini`_
(default: 'username', 'password' and 'email'). Independet of the QFQ Form, on every start of QFQ these variables are
tested if they are non-empty and if 'yes', QFQ will sleep <x> seconds and than exit the running PHP process (prevent
brute force attacks) - an detected attack leads to a complete white (=empty) page. By default an error message is displayed.
SIP
---
Links with URL parameters, targeting to the local website, are typically SIP encoded. Instead of transferring the parameter
as part of the URL, only one uniqe timestamp, called 's', is appended at the link. The parameter 's' is uniq (equal to a
timestamp) for the user and the assigned variables are stored as a part of the PHP user session on the server.
Two users might have the same value of parameter 's', but the content is completely independet.
Variables needed by Typo3 remains on the link and are not 'sip-encoded'.
Store Store
===== =====
...@@ -602,7 +646,7 @@ Store: *RECORD* - R ...@@ -602,7 +646,7 @@ Store: *RECORD* - R
* Sanatized: *no* * Sanatized: *no*
* Current record loaded in Form. * Current record loaded in Form.
* If r=0, alle values are empty. * If r=0, all values are empty.
+------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+ +------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------+
| Name | Explanation | | Name | Explanation |
...@@ -617,7 +661,7 @@ Store: *BEFORE* - B ...@@ -617,7 +661,7 @@ Store: *BEFORE* - B
* Sanatized: *no* * Sanatized: *no*
* Current record loaded in Form without any modification. * Current record loaded in Form without any modification.
* If r=0, alle values are empty. * If r=0, all values are empty.
This store is handy to compare new and old values of a form. This store is handy to compare new and old values of a form.
...@@ -1415,6 +1459,8 @@ Fields: ...@@ -1415,6 +1459,8 @@ Fields:
| | 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad', 'afterSave', 'afterInsert', 'afterUpdate', 'afterDelete', | | | 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad', 'afterSave', 'afterInsert', 'afterUpdate', 'afterDelete', |
| | 'sendMail') | | | 'sendMail') |
+---------------+-----------------------------+---------------------------------------------------------------------------------------------------+ +---------------+-----------------------------+---------------------------------------------------------------------------------------------------+
|encode | 'none', 'specialchar' | With 'specialchar' (default) the chars <>"'& will be encoded to their htmlentity. |
+---------------+-----------------------------+---------------------------------------------------------------------------------------------------+
|checkType | enum('min|max', 'pattern', | | |checkType | enum('min|max', 'pattern', | |
| | 'number', 'email') | | | | 'number', 'email') | |
+---------------+-----------------------------+---------------------------------------------------------------------------------------------------+ +---------------+-----------------------------+---------------------------------------------------------------------------------------------------+
......
...@@ -273,9 +273,29 @@ abstract class AbstractBuildForm { ...@@ -273,9 +273,29 @@ abstract class AbstractBuildForm {
$attribute = $this->getFormTagAtrributes(); $attribute = $this->getFormTagAtrributes();
return '<form ' . OnArray::toString($attribute, '=', ' ', "'") . '>'; $honeypot = $this->getHoneypotVars();
return '<form ' . OnArray::toString($attribute, '=', ' ', "'") . '>' . $honeypot;
} }
/**
* Create HTML Input vars to detect bot automatic filling of forms.
*
* @return string
*/
public function getHoneypotVars() {
$html = '';
$vars = $this->store->getVar(SYSTEM_SECURITY_VARS_HONEYPOT, STORE_SYSTEM);
// Iterate over all fake vars
$arr = explode(',', $vars);
foreach ($arr as $name) {
$html .= "<input name='$name' type='hidden' value='' readonly>";
}
return $html;
}
/** /**
* Build an assoc array with standard form attributes. * Build an assoc array with standard form attributes.
* *
......
...@@ -391,7 +391,10 @@ class BuildFormBootstrap extends AbstractBuildForm { ...@@ -391,7 +391,10 @@ class BuildFormBootstrap extends AbstractBuildForm {
if (isset($this->formSpec[F_SAVE_BUTTON_ACTIVE])) { if (isset($this->formSpec[F_SAVE_BUTTON_ACTIVE])) {
$attribute[DATA_ENABLE_SAVE_BUTTON] = 'true'; $attribute[DATA_ENABLE_SAVE_BUTTON] = 'true';
} }
return '<form ' . OnArray::toString($attribute, '=', ' ', "'") . '>';
$honeypot = $this->getHoneypotVars();
return '<form ' . OnArray::toString($attribute, '=', ' ', "'") . '>' . $honeypot;
} }
/** /**
......
...@@ -228,4 +228,29 @@ class Sanitize { ...@@ -228,4 +228,29 @@ class Sanitize {
} }
return $item; return $item;
} }
/**
* urlencode() any input and decode again. This normalizes all characters and guarantees that there are no more urlencoded characters.
*
* @param array|string $item
* @return array|string
* @throws CodeException
*/
public static function urlDecodeArr($item) {
if (is_array($item)) {
foreach ($item as $key => $value) {
$value = self::urlDecodeArr($value);
$item[$key] = $value;
}
} else {
if (is_string($item)) {
$item = urldecode($item);
} elseif (!is_numeric($item)) {
throw new qfq\CodeException ("Expect type 'string / numeric / array' - but there is something else.", ERROR_UNEXPECTED_TYPE);
}
}
return $item;
}
} }
\ No newline at end of file
...@@ -346,14 +346,18 @@ class Store { ...@@ -346,14 +346,18 @@ class Store {
if (isset($_GET)) { if (isset($_GET)) {
$arr = array_merge($arr, $_GET); $arr = array_merge($arr, $_GET);
} }
$arr = \qfq\Sanitize::urlDecodeArr($arr);
if (isset($_POST)) { if (isset($_POST)) {
$arr = array_merge($arr, $_POST); $arr = array_merge($arr, $_POST);
} }
// It's important to merge the SERVER array last: those entries shall overwrite client values. // It's important to merge the SERVER array last: those entries shall overwrite client values.
if (isset($_SERVER)) { if (isset($_SERVER)) {
$server = Sanitize::htmlentitiesArr($_SERVER); // $_SERVER values might be compromised. $server = Sanitize::htmlentitiesArr($_SERVER); // $_SERVER values might be compromised.
$arr = array_merge($arr, $server); $arr = array_merge($arr, $server);
} }
$arr = \qfq\Sanitize::normalize($arr); $arr = \qfq\Sanitize::normalize($arr);
self::setStore($arr, STORE_CLIENT, true); self::setStore($arr, STORE_CLIENT, true);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment