Skip to content

Commit 2a89af6

Browse files
authored
Merge pull request #1063 from dvdnwoke/honeypot
Honeypot Filter
2 parents 2720620 + 33e431b commit 2a89af6

File tree

10 files changed

+415
-6
lines changed

10 files changed

+415
-6
lines changed

application/Config/Filters.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@ class Filters extends BaseConfig
99
public $aliases = [
1010
'csrf' => \App\Filters\CSRF::class,
1111
'toolbar' => \App\Filters\DebugToolbar::class,
12+
'honeypot' => \App\Filters\Honeypot::class
1213
];
1314

1415
// Always applied before every request
1516
public $globals = [
1617
'before' => [
17-
// 'csrf'
18+
//'honeypot'
19+
// 'csrf',
1820
],
1921
'after' => [
20-
'toolbar'
22+
'toolbar',
23+
//'honeypot'
2124
]
2225
];
2326

application/Config/Honeypot.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php namespace Config;
2+
3+
use CodeIgniter\Config\BaseConfig;
4+
5+
class Honeypot extends BaseConfig
6+
{
7+
8+
/**
9+
* Makes Honeypot visible or not to human
10+
*
11+
* @var boolean
12+
*/
13+
public $hidden = true;
14+
/**
15+
* Honeypot Label Content
16+
* @var String
17+
*/
18+
public $label = 'Fill This Field';
19+
20+
/**
21+
* Honeypot Field Name
22+
* @var String
23+
*/
24+
public $name = 'honeypot';
25+
26+
/**
27+
* Honeypot HTML Template
28+
* @var String
29+
*/
30+
public $template = '<label>{label}</label><input type="text" name="{name}" value=""/>';
31+
}

application/Config/Services.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php namespace Config;
22

33
use CodeIgniter\Config\Services as CoreServices;
4+
use CodeIgniter\Config\BaseConfig;
45

56
require_once BASEPATH.'Config/Services.php';
67

@@ -30,5 +31,20 @@ class Services extends CoreServices
3031
// return new \CodeIgniter\Example();
3132
// }
3233

34+
public static function honeypot(BaseConfig $config = null, $getShared = true)
35+
{
36+
if ($getShared)
37+
{
38+
return self::getSharedInstance('honeypot', $config);
39+
}
40+
41+
if (is_null($config))
42+
{
43+
$config = new \Config\Honeypot();
44+
}
45+
46+
return new \CodeIgniter\Honeypot\Honeypot($config);
47+
}
48+
3349

3450
}

application/Filters/Honeypot.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php namespace App\Filters;
2+
3+
use CodeIgniter\Filters\FilterInterface;
4+
use CodeIgniter\HTTP\RequestInterface;
5+
use CodeIgniter\HTTP\ResponseInterface;
6+
use Config\Services;
7+
use CodeIgniter\Honeypot\Exceptions\HoneypotException;
8+
9+
class Honeypot implements FilterInterface
10+
{
11+
12+
/**
13+
* Checks if Honeypot field is empty, if so
14+
* then the requester is a bot,show a blank
15+
* page
16+
*
17+
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
18+
*
19+
* @return mixed
20+
*/
21+
22+
public function before (RequestInterface $request)
23+
{
24+
25+
// Checks honeypot field if value was entered then show blank if so.
26+
27+
$honeypot = Services::honeypot(new \Config\Honeypot());
28+
if($honeypot->hasContent($request))
29+
{
30+
throw HoneypotException::isBot();
31+
}
32+
33+
}
34+
35+
/**
36+
* Checks if Honeypot field is empty, if so
37+
* then the requester is a bot,show a blank
38+
* page
39+
*
40+
* @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
41+
* @param ResponseInterface|\CodeIgniter\HTTP\Response $response
42+
* @return mixed
43+
*/
44+
45+
public function after (RequestInterface $request, ResponseInterface $response)
46+
{
47+
48+
$honeypot = Services::honeypot(new \Config\Honeypot());
49+
$honeypot->attachHoneypot($response);
50+
}
51+
}

env

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,12 @@
7676
# contentsecuritypolicy.reportURI = null
7777
# contentsecuritypolicy.sandbox = false
7878
# contentsecuritypolicy.upgradeInsecureRequests = false
79+
80+
#--------------------------------------------------------------------
81+
# HONEYPOT
82+
#--------------------------------------------------------------------
83+
84+
# honeypot.hidden = 'true'
85+
# honeypot.label = 'Fill This Field'
86+
# honeypot.name = 'honeypot'
87+
# honeypot.template = '<label>{label}</label><input type="text" name="{name}" value=""/>'
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php namespace CodeIgniter\Honeypot\Exceptions;
2+
3+
use CodeIgniter\Exceptions\ConfigException;
4+
use CodeIgniter\Exceptions\ExceptionInterface;
5+
6+
class HoneypotException extends ConfigException implements ExceptionInterface
7+
{
8+
public static function forNoTemplate()
9+
{
10+
return new static(lang('Honeypot.noTemplate'));
11+
}
12+
13+
public static function forNoNameField()
14+
{
15+
return new static(lang('Honeypot.noNameField'));
16+
}
17+
18+
public static function forNoHiddenValue()
19+
{
20+
return new static(lang('Honeypot.noHiddenValue'));
21+
}
22+
23+
public static function isBot()
24+
{
25+
return new static(lang('Honeypot.theClientIsABot'));
26+
}
27+
28+
}

system/Honeypot/Honeypot.php

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?php namespace CodeIgniter\Honeypot;
2+
3+
/**
4+
* CodeIgniter
5+
*
6+
* An open source application development framework for PHP
7+
*
8+
* This content is released under the MIT License (MIT)
9+
*
10+
* Copyright (c) 2014-2018 British Columbia Institute of Technology
11+
*
12+
* Permission is hereby granted, free of charge, to any person obtaining a copy
13+
* of this software and associated documentation files (the "Software"), to deal
14+
* in the Software without restriction, including without limitation the rights
15+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16+
* copies of the Software, and to permit persons to whom the Software is
17+
* furnished to do so, subject to the following conditions:
18+
*
19+
* The above copyright notice and this permission notice shall be included in
20+
* all copies or substantial portions of the Software.
21+
*
22+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28+
* THE SOFTWARE.
29+
*
30+
* @package CodeIgniter
31+
* @author CodeIgniter Dev Team
32+
* @copyright 2014-2018 British Columbia Institute of Technology (https://bcit.ca/)
33+
* @license https://opensource.org/licenses/MIT MIT License
34+
* @link https://codeigniter.com
35+
* @since Version 3.0.0
36+
* @filesource
37+
*/
38+
39+
use CodeIgniter\Config\BaseConfig;
40+
use CodeIgniter\HTTP\RequestInterface;
41+
use CodeIgniter\HTTP\ResponseInterface;
42+
use Config\Honeypot as HoneypotConfig;
43+
use CodeIgniter\Honeypot\Exceptions\HoneypotException;
44+
45+
class Honeypot
46+
{
47+
48+
/**
49+
* Honeypot Template
50+
* @var String
51+
*/
52+
protected $template;
53+
54+
/**
55+
* Honeypot text field name
56+
* @var String
57+
*/
58+
protected $name;
59+
60+
/**
61+
* Honeypot lable content
62+
* @var String
63+
*/
64+
protected $label;
65+
66+
/**
67+
* Self Instance of Class
68+
* @var Honeypot
69+
*/
70+
protected $config;
71+
72+
//--------------------------------------------------------------------
73+
74+
function __construct (BaseConfig $config) {
75+
$this->config = $config;
76+
77+
if($this->config->hidden === '')
78+
{
79+
throw HoneypotException::forNoHiddenValue();
80+
}
81+
82+
if($this->config->template === '')
83+
{
84+
throw HoneypotException::forNoTemplate();
85+
}
86+
87+
if($this->config->name === '')
88+
{
89+
throw HoneypotException::forNoNameField();
90+
}
91+
}
92+
93+
//--------------------------------------------------------------------
94+
95+
/**
96+
* Checks the request if honeypot field has data.
97+
*
98+
* @param \CodeIgniter\HTTP\RequestInterface $request
99+
*
100+
*/
101+
public function hasContent(RequestInterface $request)
102+
{
103+
if($request->getVar($this->config->name))
104+
{
105+
return true;
106+
}
107+
return false;
108+
}
109+
110+
/**
111+
* Attachs Honeypot template to response.
112+
*
113+
* @param \CodeIgniter\HTTP\ResponseInterface $response
114+
*/
115+
public function attachHoneypot(ResponseInterface $response)
116+
{
117+
$prep_field = $this->prepareTemplate($this->config->template);
118+
119+
$body = $response->getBody();
120+
$body = str_ireplace('</form>', $prep_field, $body);
121+
$response->setBody($body);
122+
}
123+
124+
/**
125+
* Prepares the template by adding label
126+
* content and field name.
127+
*
128+
* @param string $template
129+
* @return string
130+
*/
131+
protected function prepareTemplate($template): string
132+
{
133+
$template = str_ireplace('{label}', $this->config->label, $template);
134+
$template = str_ireplace('{name}', $this->config->name, $template);
135+
136+
if($this->config->hidden)
137+
{
138+
$template = '<div style="display:none">'. $template . '</div>';
139+
}
140+
return $template;
141+
}
142+
143+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php namespace CodeIgniter\Honeypot;
2+
3+
use CodeIgniter\Test\CIUnitTestCase;
4+
use CodeIgniter\Honeypot\Honeypot;
5+
use CodeIgniter\Config\Services;
6+
7+
class HoneypotTest extends CIUnitTestCase
8+
{
9+
10+
protected $request;
11+
protected $response;
12+
protected $honeypot;
13+
14+
public function setUp()
15+
{
16+
parent::setUp();
17+
$this->request = Services::request();
18+
$this->response = Services::response();
19+
$config = new \Config\Honeypot();
20+
$this->honeypot = new Honeypot($config);
21+
22+
}
23+
24+
public function testAttachHoneypot()
25+
{
26+
27+
$this->response->setBody('<form></form>');
28+
$this->honeypot->attachHoneypot($this->response);
29+
$this->assertContains('honeypot', $this->response->getBody());
30+
$this->response->setBody('<div></div>');
31+
$this->assertNotContains('honeypot', $this->response->getBody());
32+
}
33+
34+
public function testHasHoneypot()
35+
{
36+
37+
$_REQUEST['honeypot'] = 'hey';
38+
$this->assertEquals(true, $this->honeypot->hasContent($this->request));
39+
$_POST['honeypot'] = 'hey';
40+
$this->assertEquals(true, $this->honeypot->hasContent($this->request));
41+
$_GET['honeypot'] = 'hey';
42+
$this->assertEquals(true, $this->honeypot->hasContent($this->request));
43+
}
44+
}

user_guide_src/source/index.rst

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,7 @@ General Topics
6161
Library Reference
6262
*****************
6363

64-
.. toctree::
65-
:titlesonly:
66-
67-
libraries/index
64+
* ``Honeypot``
6865

6966
******************
7067
Database Reference

0 commit comments

Comments
 (0)