diff --git a/application/Config/Filters.php b/application/Config/Filters.php index cb16f8137b64..a3ed0861b9d7 100644 --- a/application/Config/Filters.php +++ b/application/Config/Filters.php @@ -9,15 +9,18 @@ class Filters extends BaseConfig public $aliases = [ 'csrf' => \App\Filters\CSRF::class, 'toolbar' => \App\Filters\DebugToolbar::class, + 'honeypot' => \App\Filters\Honeypot::class ]; // Always applied before every request public $globals = [ 'before' => [ - // 'csrf' + //'honeypot' + // 'csrf', ], 'after' => [ - 'toolbar' + 'toolbar', + //'honeypot' ] ]; diff --git a/application/Config/Honeypot.php b/application/Config/Honeypot.php new file mode 100644 index 000000000000..d35b0c7ee861 --- /dev/null +++ b/application/Config/Honeypot.php @@ -0,0 +1,31 @@ +{label}'; +} \ No newline at end of file diff --git a/application/Config/Services.php b/application/Config/Services.php index 543c2c4aa68b..58a93a0a4e5e 100644 --- a/application/Config/Services.php +++ b/application/Config/Services.php @@ -1,6 +1,7 @@ hasContent($request)) + { + throw HoneypotException::isBot(); + } + + } + + /** + * Checks if Honeypot field is empty, if so + * then the requester is a bot,show a blank + * page + * + * @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request + * @param ResponseInterface|\CodeIgniter\HTTP\Response $response + * @return mixed + */ + + public function after (RequestInterface $request, ResponseInterface $response) + { + + $honeypot = Services::honeypot(new \Config\Honeypot()); + $honeypot->attachHoneypot($response); + } +} diff --git a/env b/env index 816e118faeea..d5559450fb3f 100644 --- a/env +++ b/env @@ -76,3 +76,12 @@ # contentsecuritypolicy.reportURI = null # contentsecuritypolicy.sandbox = false # contentsecuritypolicy.upgradeInsecureRequests = false + +#-------------------------------------------------------------------- +# HONEYPOT +#-------------------------------------------------------------------- + +# honeypot.hidden = 'true' +# honeypot.label = 'Fill This Field' +# honeypot.name = 'honeypot' +# honeypot.template = '' diff --git a/system/Honeypot/Exceptions/HoneypotException.php b/system/Honeypot/Exceptions/HoneypotException.php new file mode 100644 index 000000000000..c6085655c905 --- /dev/null +++ b/system/Honeypot/Exceptions/HoneypotException.php @@ -0,0 +1,28 @@ +config = $config; + + if($this->config->hidden === '') + { + throw HoneypotException::forNoHiddenValue(); + } + + if($this->config->template === '') + { + throw HoneypotException::forNoTemplate(); + } + + if($this->config->name === '') + { + throw HoneypotException::forNoNameField(); + } + } + + //-------------------------------------------------------------------- + + /** + * Checks the request if honeypot field has data. + * + * @param \CodeIgniter\HTTP\RequestInterface $request + * + */ + public function hasContent(RequestInterface $request) + { + if($request->getVar($this->config->name)) + { + return true; + } + return false; + } + + /** + * Attachs Honeypot template to response. + * + * @param \CodeIgniter\HTTP\ResponseInterface $response + */ + public function attachHoneypot(ResponseInterface $response) + { + $prep_field = $this->prepareTemplate($this->config->template); + + $body = $response->getBody(); + $body = str_ireplace('', $prep_field, $body); + $response->setBody($body); + } + + /** + * Prepares the template by adding label + * content and field name. + * + * @param string $template + * @return string + */ + protected function prepareTemplate($template): string + { + $template = str_ireplace('{label}', $this->config->label, $template); + $template = str_ireplace('{name}', $this->config->name, $template); + + if($this->config->hidden) + { + $template = '
'. $template . '
'; + } + return $template; + } + +} \ No newline at end of file diff --git a/tests/system/Honeypot/HoneypotTest.php b/tests/system/Honeypot/HoneypotTest.php new file mode 100644 index 000000000000..df134c2da04e --- /dev/null +++ b/tests/system/Honeypot/HoneypotTest.php @@ -0,0 +1,44 @@ +request = Services::request(); + $this->response = Services::response(); + $config = new \Config\Honeypot(); + $this->honeypot = new Honeypot($config); + + } + + public function testAttachHoneypot() + { + + $this->response->setBody('
'); + $this->honeypot->attachHoneypot($this->response); + $this->assertContains('honeypot', $this->response->getBody()); + $this->response->setBody('
'); + $this->assertNotContains('honeypot', $this->response->getBody()); + } + + public function testHasHoneypot() + { + + $_REQUEST['honeypot'] = 'hey'; + $this->assertEquals(true, $this->honeypot->hasContent($this->request)); + $_POST['honeypot'] = 'hey'; + $this->assertEquals(true, $this->honeypot->hasContent($this->request)); + $_GET['honeypot'] = 'hey'; + $this->assertEquals(true, $this->honeypot->hasContent($this->request)); + } +} \ No newline at end of file diff --git a/user_guide_src/source/index.rst b/user_guide_src/source/index.rst index 85d6c3d2ec56..4e6c4673b132 100644 --- a/user_guide_src/source/index.rst +++ b/user_guide_src/source/index.rst @@ -61,10 +61,7 @@ General Topics Library Reference ***************** -.. toctree:: - :titlesonly: - - libraries/index +* ``Honeypot`` ****************** Database Reference diff --git a/user_guide_src/source/libraries/honeypot.rst b/user_guide_src/source/libraries/honeypot.rst new file mode 100644 index 000000000000..36fac6f3f1b4 --- /dev/null +++ b/user_guide_src/source/libraries/honeypot.rst @@ -0,0 +1,87 @@ +===================== +Honeypot Class +===================== + +The Honeypot Class makes it possible to determine when a Bot makes a request to a CodeIgniter4 application, +If it's enabled in ``Application\Config\Filters.php`` file. This is done by attaching form fields to any form, +and this form field is hidden from human but accessible to Bot. When data is entered into the field it's +assumed the request is coming from a Bot, then an execption is thrown. + +.. contents:: Page Contents + +Enabling Honeypot +===================== + +To enable Honeypot changes has to be made to the ``Application\Config\Filters.php``. Just uncomment honeypot +from the ``$globals`` Array.:: + + public $globals = [ + 'before' => [ + //'honeypot' + // 'csrf', + ], + 'after' => [ + 'toolbar', + //'honeypot' + ] + ]; + +Customizing Honeypot +===================== + +Honeypot can be customized. It allows the following customization. Customization file can found in +``Application\Config\Honeypot.php`` and ``.env``. + +* ``Display`` +* ``Label`` +* ``Field Name`` +* ``Template`` + +**Display** + +Display can contain values of ``True`` or ``False``, meaning display the template and hide the template +respectively. The value for display is called ``hidden``.:: + + public $hidden = true; + +The above is for ``Application\Config\Honeypot.php``.:: + + honeypot.hidden = 'true' + +The above is for ``.env`` + +**Label** + +This the label for the input field. The value for label is called ``label``.:: + + public $label = 'Fill This Field'; + +The above is for ``Application\Config\Honeypot.php``.:: + + honeypot.label = 'Fill This Field' + +The above is for ``.env`` + +**Field Name** + +This the field name for the input field. The value for the field name is called ``name``.:: + + public $name = 'honeypot'; + +The above is for ``Application\Config\Honeypot.php``.:: + + honeypot.name = 'honeypot' + +The above is for ``.env`` + +**Template** + +This is the template of the honeypot. The value for the template is called ``template``.:: + + public $template = ''; + +The above is for ``Application\Config\Honeypot.php``.:: + + honeypot.template = '' + +The above is for ``.env`` \ No newline at end of file