Skip to content

Commit 275e0ed

Browse files
committed
Split the new and legacy firewall rules. Made several performance improvements. Work on new firewall rules processor.
1 parent de6071e commit 275e0ed

2 files changed

Lines changed: 69 additions & 163 deletions

File tree

src/Extensions/WordPress/Extension.php

Lines changed: 15 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -236,17 +236,6 @@ public function isWhitelisted($whitelistRules, $request)
236236

237237
foreach ($whitelistRules as $whitelist) {
238238
$whitelist_rule = json_decode($whitelist['rule']);
239-
$matched_rules = 0;
240-
241-
// If matches on all request methods, only 1 rule match is required to whitelist.
242-
if ($whitelist_rule->method === 'ALL') {
243-
$count_rules = 1;
244-
} else {
245-
if (!is_null($whitelist_rule)) {
246-
$count_rules = $whitelist_rule->rules;
247-
$count_rules = $this->countRules($count_rules);
248-
}
249-
}
250239

251240
// If an IP address match is given, determine if it matches.
252241
$ip = isset($whitelist_rule->rules, $whitelist_rule->rules->ip_address) ? $whitelist_rule->rules->ip_address : null;
@@ -275,58 +264,26 @@ public function isWhitelisted($whitelistRules, $request)
275264

276265
if ($whitelist_rule->method == $requests['method'] || $whitelist_rule->method == 'ALL') {
277266
$test = strtolower(preg_replace('/(?!^)[A-Z]{2,}(?=[A-Z][a-z])|[A-Z][a-z]/', '->$0', $key));
278-
$rule = array_reduce(
279-
explode('->', $test),
280-
function ($o, $p) {
281-
if (!isset($o->$p)) {
282-
return null;
283-
}
284-
285-
return $o->$p;
286-
},
287-
$whitelist_rule
288-
);
289-
290-
if (!is_null($rule) && substr($key, 0, 4) == 'rule' && $this->isRuleMatch($rule, $request)) {
291-
$matched_rules++;
267+
$exp = explode('->', $test);
268+
269+
// Determine if a rule exists for this request.
270+
$rule = $whitelist_rule;
271+
foreach ($exp as $var){
272+
if(!isset($rule->$var)){
273+
$rule = null;
274+
continue;
275+
}
276+
$rule = $rule->$var;
292277
}
293-
}
294-
}
295-
296-
if ($matched_rules >= $count_rules && $whitelisted_ip) {
297-
return true;
298-
}
299-
}
300278

301-
return false;
302-
}
303-
304-
/**
305-
* Count the number of rules.
306-
*
307-
* @param array $array
308-
* @return integer
309-
*/
310-
private function countRules($array)
311-
{
312-
$counter = 0;
313-
if (is_object($array)) {
314-
$array = (array) $array;
315-
}
316-
317-
if ($array['uri']) {
318-
$counter++;
319-
}
320-
321-
foreach (array('body', 'params', 'headers') as $type) {
322-
foreach ($array[$type] as $key => $value) {
323-
if (!is_null($value)) {
324-
$counter++;
279+
if (!is_null($rule) && substr($key, 0, 4) == 'rule' && $this->isRuleMatch($rule, $request) && $whitelisted_ip) {
280+
return true;
281+
}
325282
}
326283
}
327284
}
328285

329-
return $counter;
286+
return false;
330287
}
331288

332289
/**
@@ -340,7 +297,7 @@ private function isRuleMatch($rule, $request)
340297
{
341298
$is_matched = false;
342299
if (is_array($request)) {
343-
foreach ($request as $key => $value) {
300+
foreach ($request as $value) {
344301
$is_matched = $this->isRuleMatch($rule, $value);
345302
if ($is_matched) {
346303
return $is_matched;

src/Processor.php

Lines changed: 54 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ class Processor
1717
*/
1818
private $firewallRules = array();
1919

20+
/**
21+
* The legacy firewall rules to process.
22+
*
23+
* @var array
24+
*/
25+
private $firewallRulesLegacy = array();
26+
2027
/**
2128
* The whitelist rules to process.
2229
*
@@ -61,17 +68,20 @@ class Processor
6168
* Creates a new processor instance.
6269
*
6370
* @param array $firewallRules
71+
* @param array $firewallRulesLegacy
6472
* @param array $whitelistRules
6573
* @param array $options
6674
* @param ExtensionInterface $extension
6775
*/
6876
public function __construct(
6977
$firewallRules,
78+
$firewallRulesLegacy,
7079
$whitelistRules,
7180
$options,
7281
ExtensionInterface $extension
7382
) {
7483
$this->firewallRules = $firewallRules;
84+
$this->firewallRulesLegacy = $firewallRulesLegacy;
7585
$this->whitelistRules = $whitelistRules;
7686
$this->options = array_merge($this->options, $options);
7787
$this->extension = $extension;
@@ -104,7 +114,7 @@ public function launch()
104114

105115
// Determine if the user is temporarily blocked from the site before we do anything else.
106116
if ($this->extension->isBlocked($this->autoblockMinutes, $this->autoblockTime, $this->autoblockAttempts) && !$this->extension->canBypass()) {
107-
//$this->extension->forceExit(22);
117+
$this->extension->forceExit(22);
108118
}
109119

110120
// Since the Opis/Closure package does not support PHP 8.1+,
@@ -121,38 +131,23 @@ public function launch()
121131
return;
122132
}
123133

124-
// Run the legacy firewall rules processor.
125-
// Only used for general firewall rules.
126-
$this->legacyProcessor();
127-
128-
129-
130-
134+
// Run the legacy firewall rules processor for backwards compatibility.
135+
if (count($this->firewallRulesLegacy) > 0){
136+
$this->legacyProcessor();
137+
}
131138

132139
$ip = $this->extension->getIpAddress();
133-
134-
SerializableClosure::setSecretKey('secret');
135-
136-
$test = function () {
137-
if (!isset($_GET['test'])) {
138-
return false;
139-
}
140-
141-
$decode = json_decode(base64_decode($_GET['test']), true);
142-
return $decode && isset($decode['test']);
143-
};
144-
145-
// Wrap the closure
146-
$wrapper = new SerializableClosure($test);
147-
148-
// Now it can be serialized
149-
$serialized = serialize($wrapper);
150140
SerializableClosure::setSecretKey('secret');
141+
151142
foreach ($this->firewallRules as $rule) {
152143

153144
// Get the firewall rule and extract it.
154-
$firewall_rule = json_decode($rule['rule']);
155-
$vpatch = unserialize($serialized);
145+
$vpatch = base64_decode($rule->rule);
146+
if (!$vpatch) {
147+
continue;
148+
}
149+
150+
$vpatch = unserialize($vpatch);
156151
if (!$vpatch) {
157152
continue;
158153
}
@@ -167,20 +162,19 @@ public function launch()
167162
}
168163

169164
// If the payload did not match the rule, continue.
170-
var_dump($rule_hit);
171165
if (!$rule_hit) {
172166
continue;
173167
}
174168

175169
// Determine what action to perform.
176-
if ($firewall_rule->type == 'BLOCK') {
177-
$this->extension->logRequest($rule['id'], $request, 'BLOCK');
178-
$this->extension->forceExit($rule['id']);
179-
} elseif ($firewall_rule->type == 'LOG') {
180-
$this->extension->logRequest($rule['id'], $request, 'LOG');
181-
} elseif ($firewall_rule->type == 'REDIRECT') {
182-
$this->extension->logRequest($rule['id'], $request, 'REDIRECT');
183-
$this->response->redirect($firewall_rule->type_params);
170+
if ($rule->type == 'BLOCK') {
171+
$this->extension->logRequest($rule->id, $request, 'BLOCK');
172+
$this->extension->forceExit($rule->id);
173+
} elseif ($rule->type == 'LOG') {
174+
$this->extension->logRequest($rule->id, $request, 'LOG');
175+
} elseif ($rule->type == 'REDIRECT') {
176+
$this->extension->logRequest($rule->id, $request, 'REDIRECT');
177+
$this->response->redirect($rule->type_params);
184178
exit;
185179
}
186180
}
@@ -199,8 +193,7 @@ public function legacyProcessor($mustExit = true)
199193
$requests = $this->request->capture();
200194

201195
// Iterate through all root objects.
202-
foreach ($this->firewallRules as $firewall_rule) {
203-
$blocked_count = 0;
196+
foreach ($this->firewallRulesLegacy as $firewall_rule) {
204197
$rule_terms = json_decode($firewall_rule['rule']);
205198

206199
// Determine if we should match the IP address.
@@ -222,14 +215,6 @@ public function legacyProcessor($mustExit = true)
222215
}
223216
}
224217

225-
// If matches on all request methods, only 1 rule match is required to block
226-
if ($rule_terms->method === 'ALL') {
227-
$count_rules = 1;
228-
} else {
229-
$count_rules = json_decode(json_encode($rule_terms->rules), true);
230-
$count_rules = $this->countRules($count_rules);
231-
}
232-
233218
// Loop through all request data that we captured.
234219
foreach ($requests as $key => $request) {
235220

@@ -244,41 +229,33 @@ public function legacyProcessor($mustExit = true)
244229
$exp = explode('->', $test);
245230

246231
// Determine if a rule exists for this request.
247-
$rule = array_reduce(
248-
$exp,
249-
function ($o, $p) {
250-
if (!isset($o->$p)) {
251-
return null;
252-
}
253-
254-
return $o->$p;
255-
},
256-
$rule_terms
257-
);
232+
$rule = $rule_terms;
233+
foreach ($exp as $var){
234+
if(!isset($rule->$var)){
235+
$rule = null;
236+
continue;
237+
}
238+
$rule = $rule->$var;
239+
}
258240

259241
// Determine if the rule matches the request.
260242
if (!is_null($rule) && substr($key, 0, 4) == 'rule' && $this->isRuleMatch($rule, $request)) {
261-
$blocked_count++;
262-
}
263-
}
264-
}
265-
266-
// Determine if the user should be blocked.
267-
if ($blocked_count >= $count_rules) {
268-
if ($rule_terms->type == 'BLOCK') {
269-
$this->extension->logRequest($firewall_rule['id'], $request, 'BLOCK');
270-
271-
// Do we have to exit the page or simply return false?
272-
if($mustExit){
273-
$this->extension->forceExit($firewall_rule['id']);
274-
}else{
275-
return false;
243+
if ($rule_terms->type == 'BLOCK') {
244+
$this->extension->logRequest($firewall_rule['id'], $request, 'BLOCK');
245+
246+
// Do we have to exit the page or simply return false?
247+
if($mustExit){
248+
$this->extension->forceExit($firewall_rule['id']);
249+
}else{
250+
return false;
251+
}
252+
} elseif ($rule_terms->type == 'LOG') {
253+
$this->extension->logRequest($firewall_rule['id'], $request, 'LOG');
254+
} elseif ($rule_terms->type == 'REDIRECT') {
255+
$this->extension->logRequest($firewall_rule['id'], $request, 'REDIRECT');
256+
$this->response->redirect($rule_terms->type_params, $mustExit);
257+
}
276258
}
277-
} elseif ($rule_terms->type == 'LOG') {
278-
$this->extension->logRequest($firewall_rule['id'], $request, 'LOG');
279-
} elseif ($rule_terms->type == 'REDIRECT') {
280-
$this->extension->logRequest($firewall_rule['id'], $request, 'REDIRECT');
281-
$this->response->redirect($rule_terms->type_params, $mustExit);
282259
}
283260
}
284261
}
@@ -308,32 +285,4 @@ private function isRuleMatch( $rule, $request ) {
308285

309286
return $is_matched;
310287
}
311-
312-
/**
313-
* Count the number of rules.
314-
*
315-
* @param array $array
316-
* @return integer
317-
*/
318-
private function countRules($array)
319-
{
320-
$counter = 0;
321-
if (is_object($array)) {
322-
$array = (array) $array;
323-
}
324-
325-
if ($array['uri']) {
326-
$counter++;
327-
}
328-
329-
foreach (array('body', 'params', 'headers') as $type) {
330-
foreach ($array[$type] as $key => $value) {
331-
if (!is_null($value)) {
332-
$counter++;
333-
}
334-
}
335-
}
336-
337-
return $counter;
338-
}
339288
}

0 commit comments

Comments
 (0)