From ecbc2bba7ba2d1c0c766dd16195ee88edbe550a8 Mon Sep 17 00:00:00 2001 From: Ben Fuhrmannek Date: Sun, 8 Aug 2021 12:44:13 +0200 Subject: more PHP 7 compatibility and license clarification --- doc/source/faq.rst | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'doc/source') diff --git a/doc/source/faq.rst b/doc/source/faq.rst index bdfc7c1..57b910d 100644 --- a/doc/source/faq.rst +++ b/doc/source/faq.rst @@ -79,6 +79,12 @@ We chose the LGPL because we don't care that much how you're using Snuffleupagus but we'd like to force people to make their improvements/contributions available to everyone. +The complete license text is shipped with the sources and can be found under ``LICENSE``. + +For compatibility with older PHP versions, some original PHP source code was copied or ported back to older versions. +This source code resides in ``src/sp_php_compat.c`` and ``src/sp_php_compat.h`` and retains its original license +`The PHP License, version 3.01 `, also included with the sources as ``PHP_LICENSE``. + What is the different between SNuffleupaugs and a (WAF) like ModSecurity? """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -- cgit v1.3 From 372e7a04d9140eb9be657e80a8362402ca748386 Mon Sep 17 00:00:00 2001 From: Ben Fuhrmannek Date: Sun, 8 Aug 2021 12:51:44 +0200 Subject: rst syntax fix --- doc/source/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'doc/source') diff --git a/doc/source/faq.rst b/doc/source/faq.rst index 57b910d..0695089 100644 --- a/doc/source/faq.rst +++ b/doc/source/faq.rst @@ -83,7 +83,7 @@ The complete license text is shipped with the sources and can be found under ``L For compatibility with older PHP versions, some original PHP source code was copied or ported back to older versions. This source code resides in ``src/sp_php_compat.c`` and ``src/sp_php_compat.h`` and retains its original license -`The PHP License, version 3.01 `, also included with the sources as ``PHP_LICENSE``. +`The PHP License, version 3.01 `_, also included with the sources as ``PHP_LICENSE``. What is the different between SNuffleupaugs and a (WAF) like ModSecurity? -- cgit v1.3 From 3f558f11805225f335d700db6076ce75ae4b17b3 Mon Sep 17 00:00:00 2001 From: Ben Fuhrmannek Date: Wed, 18 Aug 2021 14:49:35 +0200 Subject: document allowed linebreaks and sim() alias to simulation() --- doc/source/config.rst | 55 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 22 deletions(-) (limited to 'doc/source') diff --git a/doc/source/config.rst b/doc/source/config.rst index 84e3fa9..9e9fb83 100644 --- a/doc/source/config.rst +++ b/doc/source/config.rst @@ -1,16 +1,6 @@ Configuration ============= -Options are chainable by using dots (``.``) and string parameters -**must** be quoted, while booleans and integers aren't. - -Comments are prefixed either with ``#``, or ``;``. - -Some rules apply in a specific ``function`` (context) on a specific ``variable`` -(data), like ``disable_function``. Others can only be enabled/disabled, like -``harden_random``. - - .. warning:: If you configure Snuffleupagus incorrectly, your website *might* not work @@ -21,17 +11,6 @@ Some rules apply in a specific ``function`` (context) on a specific ``variable`` read the present documentation about how to configure them, evaluate your threat model and write your configuration file accordingly. -Most of the features can be used in ``simulation`` mode by appending the -``.simulation()`` option to them (eg. ``sp.readonly_exec.simulation().enable();``) to see -whether or not they could break your website. The simulation mode won't block the request, -but will write a warning in the log. - -The rules are evaluated in the order that they are written, the **first** one -to match will terminate the evaluation (except for rules in simulation mode). - -Configuration file format -------------------------- - Since PHP *ini-like* configuration model isn't flexible enough, Snuffleupagus is using its own format in the file specified by the directive ``sp.configuration_file`` **in** your ``php.ini`` file, @@ -61,6 +40,38 @@ your logs of course. We do **not** recommend to use it of course, but sometimes it might be useful to be able to "debug in production" without breaking your website. +Configuration file format +------------------------- + +Options are chainable by using dots (``.``). + +Some options have a string parameter, that **must** be quoted with double quotes, e.g. ``"string"``. + +Comments are prefixed either with ``#``, or ``;``. + +Some rules apply in a specific ``function`` (context) on a specific ``variable`` +(data), like ``disable_function``. Others can only be enabled/disabled, like +``harden_random``. + +Most of the features can be used in ``simulation`` mode by appending the +``.simulation()`` or ``.sim()`` option to them (eg. ``sp.readonly_exec.simulation().enable();``) to see +whether or not they could break your website. The simulation mode won't block the request, +but will write a warning in the log. + +The rules are evaluated in the order that they are written, the **first** one +to match will terminate the evaluation (except for rules in simulation mode). + +Rules can be split into lines and contain whitespace for easier readability and maintenance: (This feature is available since version 0.8.0.) + +:: + + sp.disable_function.function("mail") + .param("to").value_r("\\n") + .alias("newline in mail() To:") + .drop(); + +The terminating ``;`` is optional for now, but it should be used for future compatibility. + Miscellaneous ------------- @@ -380,7 +391,7 @@ It's currently not possible to: `for now `__). This is why hooked ``print`` will be displayed as ``echo`` in the logs. - Hook `strlen`, since in latest PHP versions, this function is usually - optimized away by the compiled. + optimized away by the compiler. Examples -- cgit v1.3 From ebf9ee33374e59920da6977f7b7e6b3a5d9a4ce5 Mon Sep 17 00:00:00 2001 From: Ben Fuhrmannek Date: Wed, 18 Aug 2021 15:13:55 +0200 Subject: documentation for the ini protection feature --- doc/source/config.rst | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'doc/source') diff --git a/doc/source/config.rst b/doc/source/config.rst index 9e9fb83..10b0afd 100644 --- a/doc/source/config.rst +++ b/doc/source/config.rst @@ -189,6 +189,70 @@ Cookies-related mitigations Since snuffleupagus is providing several hardening features for cookies, there is a dedicated web page :ref:`here ` about them. +INI Settings Protection +^^^^^^^^^^^^^^^^^^^^^^^ +INI settings can be forced to a value, limited by min/max value or regular expression and set read-only mode. + +First, this feature can be enabled or disabled: + +:: + + sp.ini_protection.enable(); + sp.ini_protection.disable(); + +The INI protection feature can be set to simulation mode, where violations are only reported, but rules are not enforced: + +:: + + sp.ini_protection.simulation(); + +Rule violations can be set to drop as a global policy, or alternatively be set on individual rules using ``.drop()``. + +:: + + sp.ini_protection.policy_drop(); + +Rules can be set to fail silently without logging anything: + +:: + + sp.ini_protection.policy_silent_fail(); + ## or write sp.ini_protection.policy_no_log(); as an alias + +Read-only settings are implemented in a way that the PHP system itself can block the setting, which is very efficient. If you do not need to log read-only violations, these can be set to silent separately: + +:: + + sp.ini_protection.policy_silent_ro(); + +A global access policy can be set to either read-only or read-write. Individual entries can be set to read-only/read-write as well using ``.ro()``/``.rw()``. + +:: + + sp.ini_protection.policy_readonly(); + sp.ini_protection.policy_readwrite(); + +Individual rules are specified using ``sp.ini``. These entries can have the following attributes: + +- ``.key("...")``: mandatory ini name. +- ``.set("...")``: set the initial value. This overrides php.ini. checks are not performed for this initial value. +- ``.min("...")`` / ``.max("...")``: value must be an integer between .min and .max. shorthand notation (e.g. 1k = 1024) is allowed +- ``.regexp("...")``: value must match the regular expression +- ``.allow_null()``: allow setting a NULL-value +- ``.msg("...")``: message is shown in logs on rule violation instead of default message +- ``.readonly()`` / ``.ro()`` / .readwrite() / .rw(): set entry to read-only or read-write respectively. If no access keyword is provided, the entry inherits the default policy set by ``sp.ini_protection.policy_*``-rules. +- ``.drop()``: drop request on rule violation for this entry +- ``.simulation()``: only log rule violation for this entry + +Examples: + +:: + + sp.ini.key("display_errors").set("0").ro(); + sp.ini.key("default_socket_timeout").min("1").max("300").rw(); + sp.ini.key("highlight.comment").regexp("^#[0-9a-fA-F]{6}$"); + +For more examples, check out the ``config`` directory. readonly_exec ^^^^^^^^^^^^^ -- cgit v1.3 From 713cb08b58d4e5dd5e7e80b1f82e27cbe52d4381 Mon Sep 17 00:00:00 2001 From: Ben Fuhrmannek Date: Thu, 11 Nov 2021 13:15:52 +0100 Subject: inverted logic. set xxe_protection.enable() instead of disable_xxe.disable() --- config/default.rules | 2 +- config/default_php8.rules | 2 +- config/suhosin.rules | 2 +- doc/source/config.rst | 7 ++++--- src/php_snuffleupagus.h | 2 +- src/snuffleupagus.c | 2 +- src/sp_config.c | 2 +- src/sp_config.h | 4 ++-- src/tests/xxe/config/disable_xxe.ini | 2 +- src/tests/xxe/config/disable_xxe_disable.ini | 2 +- src/tests/xxe/disable_xxe_dom_disabled.phpt | 4 ++-- src/tests/xxe/disable_xxe_simplexml.phpt | 3 ++- src/tests/xxe/disable_xxe_simplexml_oop.phpt | 3 ++- src/tests/xxe/disable_xxe_xml_parse.phpt | 5 ++++- 14 files changed, 24 insertions(+), 18 deletions(-) (limited to 'doc/source') diff --git a/config/default.rules b/config/default.rules index b964073..2de703b 100644 --- a/config/default.rules +++ b/config/default.rules @@ -7,7 +7,7 @@ sp.harden_random.enable(); # Disabled XXE -sp.disable_xxe.enable(); +sp.xxe_protection.enable(); # Global configuration variables # sp.global.secret_key("YOU _DO_ NEED TO CHANGE THIS WITH SOME RANDOM CHARACTERS."); diff --git a/config/default_php8.rules b/config/default_php8.rules index de2da5c..1d16191 100644 --- a/config/default_php8.rules +++ b/config/default_php8.rules @@ -8,7 +8,7 @@ sp.harden_random.enable(); # Disabled XXE -sp.disable_xxe.enable(); +sp.xxe_protection.enable(); # Global configuration variables # sp.global.secret_key("YOU _DO_ NEED TO CHANGE THIS WITH SOME RANDOM CHARACTERS."); diff --git a/config/suhosin.rules b/config/suhosin.rules index 4beb4c8..0bdc453 100644 --- a/config/suhosin.rules +++ b/config/suhosin.rules @@ -276,6 +276,6 @@ sp.harden_random.enable(); sp.auto_cookie_secure.enable(); #sp.cookie.name("cookie1").samesite("lax"); #sp.cookie.name("cookie2").samesite("strict");; -sp.disable_xxe.enable(); +sp.xxe_protection.enable(); #sp.sloppy_comparison.enable(); diff --git a/doc/source/config.rst b/doc/source/config.rst index 10b0afd..63ddf7b 100644 --- a/doc/source/config.rst +++ b/doc/source/config.rst @@ -293,14 +293,15 @@ It can either be ``enabled`` or ``disabled`` and can be used in ``simulation`` m sp.upload_validation.script("/var/www/is_valid_php.py").enable(); -disable_xxe +xxe_protection ^^^^^^^^^^^ -:ref:`disable_xxe `, enabled by default, will prevent XXE attacks by disabling the loading of external entities (``libxml_disable_entity_loader``) in the XML parser. +:ref:`xxe_protection `, disabled by default, will prevent XXE attacks by disabling the loading of external entities (``libxml_disable_entity_loader``) in the XML parser. :: - sp.disable_xxe.enable(); + sp.xxe_protection.enable(); + sp.xxe_protection.disable(); Whitelist of stream-wrappers diff --git a/src/php_snuffleupagus.h b/src/php_snuffleupagus.h index 308031b..03c9bb6 100644 --- a/src/php_snuffleupagus.h +++ b/src/php_snuffleupagus.h @@ -116,7 +116,7 @@ sp_config_upload_validation config_upload_validation; sp_config_cookie config_cookie; sp_config_auto_cookie_secure config_auto_cookie_secure; sp_config_global_strict config_global_strict; -sp_config_disable_xxe config_disable_xxe; +sp_config_xxe_protection config_xxe_protection; sp_config_eval config_eval; sp_config_wrapper config_wrapper; sp_config_session config_session; diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c index 6fd6f25..c96a911 100644 --- a/src/snuffleupagus.c +++ b/src/snuffleupagus.c @@ -314,7 +314,7 @@ static PHP_INI_MH(OnUpdateConfiguration) { hook_upload(); } - if (SPCFG(disable_xxe).enable == 0) { + if (SPCFG(xxe_protection).enable) { hook_libxml_disable_entity_loader(); } diff --git a/src/sp_config.c b/src/sp_config.c index ec6c5a8..bc9aa0d 100644 --- a/src/sp_config.c +++ b/src/sp_config.c @@ -17,7 +17,7 @@ static zend_result sp_process_config_root(sp_parsed_keyword *parsed_rule) { {parse_cookie, SP_TOKEN_COOKIE_ENCRYPTION, NULL}, {parse_global, SP_TOKEN_GLOBAL, NULL}, {parse_enable, SP_TOKEN_AUTO_COOKIE_SECURE, &(SPCFG(auto_cookie_secure).enable)}, - {parse_enable, SP_TOKEN_DISABLE_XXE, &(SPCFG(disable_xxe).enable)}, + {parse_enable, SP_TOKEN_XXE_PROTECTION, &(SPCFG(xxe_protection).enable)}, {parse_eval_filter_conf, SP_TOKEN_EVAL_BLACKLIST, &(SPCFG(eval).blacklist)}, {parse_eval_filter_conf, SP_TOKEN_EVAL_WHITELIST, &(SPCFG(eval).whitelist)}, {parse_session, SP_TOKEN_SESSION_ENCRYPTION, &(SPCFG(session))}, diff --git a/src/sp_config.h b/src/sp_config.h index 262050b..a557105 100644 --- a/src/sp_config.h +++ b/src/sp_config.h @@ -57,7 +57,7 @@ typedef struct { typedef struct { bool enable; -} sp_config_disable_xxe; +} sp_config_xxe_protection; typedef struct { enum samesite_type { strict = 1, lax = 2 } samesite; @@ -202,7 +202,7 @@ typedef struct { #define SP_TOKEN_READONLY_EXEC "readonly_exec" #define SP_TOKEN_UNSERIALIZE_HMAC "unserialize_hmac" #define SP_TOKEN_UPLOAD_VALIDATION "upload_validation" -#define SP_TOKEN_DISABLE_XXE "disable_xxe" +#define SP_TOKEN_XXE_PROTECTION "xxe_protection" #define SP_TOKEN_EVAL_BLACKLIST "eval_blacklist" #define SP_TOKEN_EVAL_WHITELIST "eval_whitelist" #define SP_TOKEN_SLOPPY_COMPARISON "sloppy_comparison" diff --git a/src/tests/xxe/config/disable_xxe.ini b/src/tests/xxe/config/disable_xxe.ini index bc9d1f2..a50a3b9 100644 --- a/src/tests/xxe/config/disable_xxe.ini +++ b/src/tests/xxe/config/disable_xxe.ini @@ -1 +1 @@ -sp.disable_xxe.enable(); +sp.xxe_protection.enable(); diff --git a/src/tests/xxe/config/disable_xxe_disable.ini b/src/tests/xxe/config/disable_xxe_disable.ini index bb1e432..eaf5755 100644 --- a/src/tests/xxe/config/disable_xxe_disable.ini +++ b/src/tests/xxe/config/disable_xxe_disable.ini @@ -1 +1 @@ -sp.disable_xxe.disable(); +sp.xxe_protection.disable(); diff --git a/src/tests/xxe/disable_xxe_dom_disabled.phpt b/src/tests/xxe/disable_xxe_dom_disabled.phpt index a49e094..107171c 100644 --- a/src/tests/xxe/disable_xxe_dom_disabled.phpt +++ b/src/tests/xxe/disable_xxe_dom_disabled.phpt @@ -1,10 +1,10 @@ --TEST-- -Disable XXE +Disable XXE (feature enabled) --SKIPIF-- = 80000) print "skip"; ?> --INI-- -sp.configuration_file={PWD}/config/disable_xxe_disable.ini +sp.configuration_file={PWD}/config/disable_xxe.ini --EXTENSIONS-- dom --FILE-- diff --git a/src/tests/xxe/disable_xxe_simplexml.phpt b/src/tests/xxe/disable_xxe_simplexml.phpt index 1d3ef4c..9560156 100644 --- a/src/tests/xxe/disable_xxe_simplexml.phpt +++ b/src/tests/xxe/disable_xxe_simplexml.phpt @@ -2,8 +2,9 @@ Disable XXE --SKIPIF-- += 80000) print "skip"; ?> --INI-- -sp.configuration_file={PWD}/config/disable_xxe.ini +sp.configuration_file={PWD}/config/disable_xxe_disable.ini --EXTENSIONS-- simplexml --XFAIL-- diff --git a/src/tests/xxe/disable_xxe_simplexml_oop.phpt b/src/tests/xxe/disable_xxe_simplexml_oop.phpt index e101337..1b2c4ca 100644 --- a/src/tests/xxe/disable_xxe_simplexml_oop.phpt +++ b/src/tests/xxe/disable_xxe_simplexml_oop.phpt @@ -2,8 +2,9 @@ Disable XXE --SKIPIF-- += 80000) print "skip"; ?> --INI-- -sp.configuration_file={PWD}/config/disable_xxe.ini +sp.configuration_file={PWD}/config/disable_xxe_disable.ini --EXTENSIONS-- simplexml --XFAIL-- diff --git a/src/tests/xxe/disable_xxe_xml_parse.phpt b/src/tests/xxe/disable_xxe_xml_parse.phpt index 6b48bea..bc7e338 100644 --- a/src/tests/xxe/disable_xxe_xml_parse.phpt +++ b/src/tests/xxe/disable_xxe_xml_parse.phpt @@ -70,7 +70,8 @@ $parser = create_parser(); $doc = xml_parse($parser, $xml, true); xml_parser_free($parser); ---EXPECT-- +--EXPECTF-- +Warning: [snuffleupagus][0.0.0.0][xxe][log] A call to libxml_disable_entity_loader was tried and nopped in %a.php on line 41 string(4) "TEST" array(0) { @@ -81,6 +82,8 @@ array(0) { } string(7) "TESTING" string(4) "TEST" + +Warning: [snuffleupagus][0.0.0.0][xxe][log] A call to libxml_disable_entity_loader was tried and nopped in %a.php on line 46 string(4) "TEST" array(0) { -- cgit v1.3