diff options
Diffstat (limited to 'doc/source/config.rst')
| -rw-r--r-- | doc/source/config.rst | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/doc/source/config.rst b/doc/source/config.rst new file mode 100644 index 0000000..8318e7d --- /dev/null +++ b/doc/source/config.rst | |||
| @@ -0,0 +1,283 @@ | |||
| 1 | Configuration | ||
| 2 | ============= | ||
| 3 | |||
| 4 | Since PHP *ini-like* configuration model isn't flexible enough, | ||
| 5 | Snuffleupagus is using its own format. | ||
| 6 | |||
| 7 | Options are chainable by using dots (``.``), and string parameters | ||
| 8 | **must** be quoted, while booleans and integers aren't. | ||
| 9 | |||
| 10 | Comments are prefixed either with ``#``, or ``;``. | ||
| 11 | |||
| 12 | Some rules applies in a specific ``function`` (context), on a specific ``variable`` | ||
| 13 | (data), like ``disable_functions``, others can only be enabled/disabled, like | ||
| 14 | ``harden_random``. | ||
| 15 | |||
| 16 | .. warning:: | ||
| 17 | |||
| 18 | Careful, a wrongly configured Snuffleupagus might break your website. | ||
| 19 | It's up to you to understand its :doc:`features <features>`, | ||
| 20 | read the present documentation about how to configure them, | ||
| 21 | evaluate your threat model, and write your configuration file accordingly. | ||
| 22 | |||
| 23 | The rules are evaluated in the order that they are written, and the **first** one | ||
| 24 | to match will terminate the evaluation (except for rules in simulation mode). | ||
| 25 | |||
| 26 | Bugclass-killer features | ||
| 27 | ------------------------ | ||
| 28 | |||
| 29 | global_strict | ||
| 30 | ^^^^^^^^^^^^^ | ||
| 31 | `default: disabled` | ||
| 32 | |||
| 33 | ``global_strict`` will enable the `strict <https://secure.php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration.strict>`_ mode globally, | ||
| 34 | forcing PHP to throw a `TypeError <https://secure.php.net/manual/en/class.typeerror.php>`_ | ||
| 35 | exception if an argument type being passed to a function does not match its corresponding declared parameter type. | ||
| 36 | |||
| 37 | It can either be ``enabled`` or ``disabled`` | ||
| 38 | |||
| 39 | :: | ||
| 40 | |||
| 41 | sp.global_strict.disable(); | ||
| 42 | sp.global_strict.enable(); | ||
| 43 | |||
| 44 | harden_random | ||
| 45 | ^^^^^^^^^^^^^ | ||
| 46 | * `default: enabled` | ||
| 47 | * `more <features.html#weak-prng-via-rand-mt-rand>`__ | ||
| 48 | |||
| 49 | ``harden_random`` will silently replace the insecure `rand <https://secure.php.net/manual/en/function.rand.php>`_ | ||
| 50 | and `mt_rand <https://secure.php.net/manual/en/function.mt-rand.php>`_ functions with | ||
| 51 | the secure PRNG `random_int <https://secure.php.net/manual/en/function.random-int.php>`_. | ||
| 52 | |||
| 53 | It can either be ``enabled`` or ``disabled``. | ||
| 54 | |||
| 55 | :: | ||
| 56 | |||
| 57 | sp.harden_random.enable(); | ||
| 58 | sp.harden_random.disable(); | ||
| 59 | |||
| 60 | .. _config_global: | ||
| 61 | |||
| 62 | global | ||
| 63 | ^^^^^^ | ||
| 64 | |||
| 65 | This configuration variable contain parameters that are used by other ones: | ||
| 66 | |||
| 67 | - ``secret_key``: A secret key used by various cryptographic features, | ||
| 68 | like `cookies protection <features.html#session-cookie-stealing-via-xss>`__ or `unserialize protection <features.html#unserialize-related-magic>`__, | ||
| 69 | so do make sure that it's random and long enough. | ||
| 70 | You can generate it with something like this: ``head -c 256 /dev/urandom | tr -dc 'a-zA-Z0-9'``. | ||
| 71 | |||
| 72 | :: | ||
| 73 | |||
| 74 | sp.global.secret_key("44239bd400aa82e125337c9d4eb8315767411ccd"); | ||
| 75 | |||
| 76 | unserialize_hmac | ||
| 77 | ^^^^^^^^^^^^^^^^ | ||
| 78 | * `default: disabled` | ||
| 79 | * `more <features.html#unserialize-related-magic>`__ | ||
| 80 | |||
| 81 | ``unserialize_hmac`` will add integrity check to ``unserialize`` calls, preventing | ||
| 82 | abritrary code execution in their context. | ||
| 83 | |||
| 84 | :: | ||
| 85 | |||
| 86 | sp.unserialize_hmac.enable(); | ||
| 87 | sp.unserialize_hmac.disable(); | ||
| 88 | |||
| 89 | |||
| 90 | auto_cookie_secure | ||
| 91 | ^^^^^^^^^^^^^^^^^^ | ||
| 92 | * `default: disabled` | ||
| 93 | * `more <features.html#session-cookie-stealing-via-xss>`__ | ||
| 94 | |||
| 95 | ``auto_cookie_secure`` will automatically mark cookies as `secure <https://en.wikipedia.org/wiki/HTTP_cookie#Secure_cookie>`_ | ||
| 96 | when the web page is requested over HTTPS. | ||
| 97 | |||
| 98 | It can either be ``enabled`` or ``disabled``. | ||
| 99 | |||
| 100 | :: | ||
| 101 | |||
| 102 | sp.auto_cookie_secure.enable(); | ||
| 103 | sp.auto_cookie_secure.disable(); | ||
| 104 | |||
| 105 | cookie_encryption | ||
| 106 | ^^^^^^^^^^^^^^^^^ | ||
| 107 | * `default: disabled` | ||
| 108 | * `more <features.html#session-cookie-stealing-via-xss>`__ | ||
| 109 | |||
| 110 | .. warning:: | ||
| 111 | |||
| 112 | To use this feature, you **must** set the :ref:`global.secret_key <config_global>` variable. | ||
| 113 | This design decision prevents attacker from | ||
| 114 | `trivially bruteforcing <https://www.idontplaydarts.com/2011/11/decrypting-suhosin-sessions-and-cookies/>`_ | ||
| 115 | session cookies. | ||
| 116 | |||
| 117 | ``cookie_secure`` will activate transparent encryption of specific cookies. | ||
| 118 | |||
| 119 | It can either be ``enabled`` or ``disabled``. | ||
| 120 | |||
| 121 | :: | ||
| 122 | |||
| 123 | sp.cookie_encryption.cookie("my_cookie_name"); | ||
| 124 | sp.cookie_encryption.cookie("another_cookie_name"); | ||
| 125 | |||
| 126 | |||
| 127 | readonly_exec | ||
| 128 | ^^^^^^^^^^^^^ | ||
| 129 | * `default: disabled` | ||
| 130 | |||
| 131 | ``readonly_exec`` will prevent the execution of writable PHP files. | ||
| 132 | |||
| 133 | It can either be ``enabled`` or ``disabled``. | ||
| 134 | |||
| 135 | :: | ||
| 136 | |||
| 137 | sp.readonly_exec.enable(); | ||
| 138 | |||
| 139 | upload_validation | ||
| 140 | ^^^^^^^^^^^^^^^^^ | ||
| 141 | * `default: disabled` | ||
| 142 | * `more <features.html#remote-code-execution-via-file-upload>`__ | ||
| 143 | |||
| 144 | ``upload_validation`` will call a given script upon a file upload, with the path | ||
| 145 | to the file being uploaded as argument, and various information about it in the environment: | ||
| 146 | |||
| 147 | * ``SP_FILENAME``: the name of the uploaded file | ||
| 148 | * ``SP_FILESIZE``: the size of the file being uploaded | ||
| 149 | * ``SP_REMOTE_ADDR``: the ip address of the uploader | ||
| 150 | * ``SP_CURRENT_FILE``: the current file being executed | ||
| 151 | |||
| 152 | This feature can be used, for example, to check if an uploaded file contains php | ||
| 153 | code, with something like `vld <https://derickrethans.nl/projects.html#vld>`_ | ||
| 154 | (``php -d vld.execute=0 -d vld.active=1 -d extension=vld.so yourfile.php``). | ||
| 155 | |||
| 156 | The upload will be **allowed** if the script return the value ``0``. Every other | ||
| 157 | value will prevent the file from being uploaded. | ||
| 158 | |||
| 159 | :: | ||
| 160 | |||
| 161 | sp.upload_validation.script("/var/www/is_valid_php.py").enable(); | ||
| 162 | |||
| 163 | |||
| 164 | disable_xxe | ||
| 165 | ^^^^^^^^^^^ | ||
| 166 | * `default: enabled` | ||
| 167 | * `more <features.html#xxe>`__ | ||
| 168 | |||
| 169 | ``disable_xxe`` will prevent XXE attacks by disabling the loading of external entities (``libxml_disable_entity_loader``) in the XML parser. | ||
| 170 | |||
| 171 | :: | ||
| 172 | |||
| 173 | sp.disable_xxe.enable(); | ||
| 174 | |||
| 175 | |||
| 176 | Virtual-patching | ||
| 177 | ---------------- | ||
| 178 | |||
| 179 | Snuffleupagus provides virtual-patching, via the ``disable_functions`` directive, allowing you to stop or control dangerous behaviours. | ||
| 180 | Admitting you have a call to ``system()`` that lacks proper user-input validation, thus leading to an **RCE**, this might be the right tool. | ||
| 181 | |||
| 182 | :: | ||
| 183 | |||
| 184 | # Allow `id.php` to restrict system() calls to `id` | ||
| 185 | sp.disable_functions.function("system").filename("id.php").param("cmd").value("id").allow(); | ||
| 186 | sp.disable_functions.function("system").filename("id.php").drop() | ||
| 187 | |||
| 188 | Of course, this is a trivial example, and a lot can be achieved with this feature, as you will see below. | ||
| 189 | |||
| 190 | |||
| 191 | Filters | ||
| 192 | ^^^^^^^ | ||
| 193 | |||
| 194 | - ``alias(:str)``: human-readable description of the rule | ||
| 195 | - ``cidr(ip/mask:str)``: match on the client's `cidr <https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing>`_ | ||
| 196 | - ``filename(name:str)``: match in the file ``name`` | ||
| 197 | - ``filename_r(regexp:str)``: the file name matching the ``regexp`` | ||
| 198 | - ``function(name:str)``: match on function ``name`` | ||
| 199 | - ``function_r(regexp:str)``: the function matching the ``regexp`` | ||
| 200 | - ``hash(:str)``: match on the file's `sha256 <https://en.wikipedia.org/wiki/SHA-2>`_ sum | ||
| 201 | - ``param(name:str)``: match on the function's parameter ``name`` | ||
| 202 | - ``param_r(regexp:str)``: match on the function's parameter ``regexp`` | ||
| 203 | - ``param_type(type:str)``: match on the function's parameter ``type`` | ||
| 204 | - ``ret(value:str)``: match on the function's return ``value`` | ||
| 205 | - ``ret_r(regexp:str)``: match with a ``regexp`` on the function's return | ||
| 206 | - ``ret_type(type_name:str)``: match on the ``type_name`` of the function's return value | ||
| 207 | - ``value(:str)``: match on a litteral value | ||
| 208 | - ``value_r(:regexp)``: match on a value matching the ``regexp`` | ||
| 209 | - ``var(name:str)``: match on a **local variable** ``name`` | ||
| 210 | |||
| 211 | The ``type`` must be one of the following values: | ||
| 212 | |||
| 213 | - ``FALSE``: for boolean false | ||
| 214 | - ``TRUE``: for boolean true | ||
| 215 | - ``NULL``: for the **null** value | ||
| 216 | - ``LONG``: for a long (also know as ``integer``) value | ||
| 217 | - ``DOUBLE``: for a **double** (also known as ``float``) value | ||
| 218 | - ``STRING``: for a string | ||
| 219 | - ``OBJECT``: for a object | ||
| 220 | - ``ARRAY``: for an array | ||
| 221 | - ``RESOURCE``: for a resource | ||
| 222 | |||
| 223 | Actions | ||
| 224 | ^^^^^^^ | ||
| 225 | |||
| 226 | - ``allow()``: **allow** the request if the rule matches | ||
| 227 | - ``drop()``: **drop** the request if the rule matches | ||
| 228 | - ``dump(directory:str)``: dump the request in the ``directory`` if it matches the rule | ||
| 229 | - ``simulation()``: enabled the simulation mode | ||
| 230 | |||
| 231 | Details | ||
| 232 | ^^^^^^^ | ||
| 233 | |||
| 234 | The ``function`` filter is able to do various dereferencing: | ||
| 235 | |||
| 236 | - ``function("AwesomeClass::my_method")`` will match in the method ``my_method`` in the class ``AwesomeClass`` | ||
| 237 | |||
| 238 | The ``param`` filter is also able to do some dereferencing: | ||
| 239 | |||
| 240 | - ``param(foo[bar])`` will get match on the value corresponding to the ``bar`` key in the hashtable ``foo``. | ||
| 241 | Remember that in PHP, almost every data structure is a hashtable. You can of course nest this like | ||
| 242 | ``param(foo[bar][baz][batman])``. | ||
| 243 | - The ``var`` filter will walk the calltrace until it finds the variable's name, or the end of it, | ||
| 244 | allowing to match on global variables: ``.var("_GET[param]")`` will match on the GET parameter ``param``. | ||
| 245 | |||
| 246 | For clarity's sake, the presence of the ``allow`` or ``drop`` action is **mandatory**. | ||
| 247 | |||
| 248 | .. warning:: | ||
| 249 | |||
| 250 | When you're writing rules, please do keep in mind that the **order matters**. | ||
| 251 | For example, if you're denying a call to ``system()`` and then allowing it in a | ||
| 252 | more narrowed way later, the call will be denied, | ||
| 253 | because it'll match the deny first. | ||
| 254 | |||
| 255 | If you're paranoid, we're providing a php script to automatically generate | ||
| 256 | hash of files containing dangerous functions, | ||
| 257 | and blacklisting them everywhere else. | ||
| 258 | |||
| 259 | Examples | ||
| 260 | ^^^^^^^^ | ||
| 261 | |||
| 262 | Evaluation order of rules | ||
| 263 | """"""""""""""""""""""""" | ||
| 264 | |||
| 265 | The following rules will: | ||
| 266 | |||
| 267 | 1. Allow calls to ``system("id")`` | ||
| 268 | 2. Issue a trace in the logs on calls to ``system`` with its parameters starting with ``ping``, | ||
| 269 | and pursuing evaluation of the remaining rules. | ||
| 270 | 3. Drop calls to ``system``. | ||
| 271 | |||
| 272 | |||
| 273 | :: | ||
| 274 | |||
| 275 | sp.disable_functions.function("system").param("cmd").value("id").allow(); | ||
| 276 | sp.disable_functions.function("system").param("cmd").value_r("^ping").drop().simulation(); | ||
| 277 | sp.disable_functions.function("system").param("cmd").drop(); | ||
| 278 | |||
| 279 | Miscellaneous examples | ||
| 280 | """""""""""""""""""""" | ||
| 281 | |||
| 282 | .. literalinclude:: ../../config/examples.ini | ||
| 283 | :language: python \ No newline at end of file | ||
