-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathBotprotection.php
153 lines (141 loc) · 6.13 KB
/
Botprotection.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
<?php
/**
.---------------------------------------------------------------------------.
| Software: PHP class Botprotection |
| Version: 1.0 |
| Date : 2023-03-31 |
| ------------------------------------------------------------------------- |
| Copyright (c) 2023, Peter Junk. All Rights Reserved. |
| ------------------------------------------------------------------------- |
| License: Distributed under the Lesser General Public License (LGPL) |
| http://www.gnu.org/copyleft/lesser.html |
| This program is distributed in the hope that it will be useful - WITHOUT |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. |
'---------------------------------------------------------------------------'
*/
class Botprotection {
const SESSKEY = 'Botprotection_v1';
//min + max. Zeit fuer Formular
private $minInputTime = 5; //min time in seconds for formular input
private $maxInputTime = 1200; //max time in seconds for formular input
private $nameset = [];
/*
* Class constructor
* @throws Exception
*/
public function __construct() {
//Verification of a valid SESSION exists
$sessionStatus = session_status();
if(session_status() === PHP_SESSION_NONE AND !headers_sent()) {
session_start();
}
if(session_status() !== PHP_SESSION_ACTIVE) {
$msg = __Class__.' needs a session_start() before the first output';
throw new \Exception($msg);
}
if(!array_key_exists(self::SESSKEY,$_SESSION)){
$_SESSION[self::SESSKEY] = [];
}
}
/*
* Set min input time in seconds
* @param int $minSeconds, defdault 5
* @return $this
*/
public function setMinInputTime(int $minSeconds = 5) : self {
$this->minInputTime = $minSeconds;
return $this;
}
/*
* Set max input time in seconds
* @param int $maxSeconds, defdault 1200 (20 Minutes)
* @return $this
*/
public function setMaxInputTime(int $maxSeconds = 1200) : self {
$this->maxInputTime = $maxSeconds;
return $this;
}
/*
* create ptrotection input element
* @param string $name
* @param bool $withoutJavascript, default false
* @return string html
*/
public function protectionInput(string $name, bool $withoutJavascript = false) : string {
$this->nameset[] = $name;
$uid = strtr(uniqid("k",true),["." => ""]);
$rnd = dechex(rand(4096,65535)); //random string
$_SESSION[self::SESSKEY][$name] = [
'id' => $uid,
'time' => microtime(true),
'token' => $rnd
];
$html = '<label for="Id_'.$name.'_0" id="Id_'.$name.'_2" > Ihre Eingabe';
$html .= '<input name="'.$name.'[0]" id="Id_'.$name.'_0" type="text" value=""></label>';
$value = $withoutJavascript ? $rnd : "";
$html .= '<input name="'.$name.'['.$uid.']" id="Id_'.$name.'_1" type="text" value="'.$value.'">';
if(!$withoutJavascript){
$html .= '<script>
(function() {
var el0 = document.getElementById("Id_'.$name.'_0"); el0.style.display="none";
var el1 = document.getElementById("Id_'.$name.'_1"); el1.style.display="none";el1.value="'.$rnd.'";
document.getElementById("Id_'.$name.'_2").style.display="none";
})();
</script>';
}
return $html;
}
/*
* check status
* @param string $name: The same name must be used here as when calling protectionInput
* @param bool $setIdInvalid, sets entry from name to invalid, default true,
* @return int : status
* @throws ErrorException
*/
public function status(string $name, bool $setNameInvalid = false) : int {
if(in_array($name,$this->nameset)){
$msg = 'status must be called before generating a new protectionInput';
throw new \ErrorException($msg);
}
if(empty($_POST)) return -1; //No form submitted yet
//Status 1: $name has expired, the name is unknown, or there is a session error
if(!array_key_exists($name,$_SESSION[self::SESSKEY])) return 1;
$sessInfo = $_SESSION[self::SESSKEY][$name];
if($setNameInvalid) {
unset($_SESSION[self::SESSKEY][$name]);
}
//Status 2: $name not exists in $_POST
if(!array_key_exists($name,$_POST)) return 2;
$postInfo = $_POST[$name];
//Status 3: $_POST[$name] is not a array
if(!is_array($postInfo)) return 3;
//Status 4: Keys not 0 and TokenId
if(array_keys($postInfo) != [0,$sessInfo['id']]) return 4;
//Status 5: Honeypot not empty
if($postInfo[0] !== "") return 5;
//Status 6: invalid Token
if($postInfo[$sessInfo['id']] !== $sessInfo['token']) return 6;
//Time-based check
$reaactionTime = microtime(true) - $sessInfo['time'];
//Status 7: Time < minInputTime
if($reaactionTime < $this->minInputTime) return 7;
//Status 8: Time > maxInputTime
if($reaactionTime > $this->maxInputTime) return 8;
//Ok
return 0;
}
/*
* check if is a bot
* @param string $name: The same name must be used here as when calling protectionInput
* @param bool $setIdInvalid, sets entry from name to invalid, default true,
* @return bool
*/
public function isBot(string $name, bool $setNameInvalid = false) : bool {
if(in_array($name,$this->nameset)){
$msg = 'isBot must be called before generating a new protection input';
throw new \ErrorException($msg);
}
return $this->status($name,$setNameInvalid) > 0;
}
}