PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.6
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.6
6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 All 196 releases
fluentform / app / Modules / Form / TokenBasedSpamProtection.php

TokenBasedSpamProtection.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 6.2.6, at app/Modules/Form/TokenBasedSpamProtection.php

158 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace FluentForm\App\Modules\Form;
3
4 defined('ABSPATH') or die;
5
6 use FluentForm\App\Helpers\Helper;
7 use FluentForm\App\Helpers\Protector;
8 use FluentForm\Framework\Helpers\ArrayHelper as Arr;
9
10 class TokenBasedSpamProtection
11 {
12
13 public function __construct($app)
14 {
15 if (!$this->isEnabled()) {
16 return;
17 }
18
19 $app->addAction('wp_ajax_fluentform_generate_protection_token', [$this, 'ajaxGenerateToken']);
20 $app->addAction('wp_ajax_nopriv_fluentform_generate_protection_token', [$this, 'ajaxGenerateToken']);
21
22 add_filter('fluentform/global_form_vars', function ($vars){
23 $vars['token_nonce'] = wp_create_nonce('fluentform_generate_token_nonce');
24 return $vars;
25 });
26
27 }
28
29 public function renderTokenField($form)
30 {
31 if (!$this->isEnabled($form->id)) {
32 return;
33 }
34
35 $fieldName = $this->getFieldName($form->id);
36 ?>
37 <input type="hidden" id="<?php echo esc_attr($fieldName); ?>" class="fluent-form-token-field" name="<?php echo esc_attr($fieldName); ?>">
38 <?php
39 }
40
41 public function ajaxGenerateToken()
42 {
43 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified on next line
44 $nonce = sanitize_text_field(Arr::get($_POST, 'nonce'));
45 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified on next line
46 $formId = (int)Arr::get($_POST,'form_id');
47
48 $nonceVerified = wp_verify_nonce($nonce, 'fluentform_generate_token_nonce');
49 if (!$formId || !$nonceVerified) {
50 wp_send_json_error([
51 'message' => __('Invalid request', 'fluentform')
52 ]);
53 }
54
55 $token = $this->generateToken($formId);
56 $response = apply_filters('fluentform/token_based_protection_response', [
57 'token' => $token
58 ], $formId);
59
60 wp_send_json_success($response);
61 }
62
63 private function generateToken($formId)
64 {
65 $timeStamp = current_time('timestamp');
66 $fieldName = $this->getFieldName($formId);
67 $data = implode('|', [$timeStamp, $formId, $fieldName]);
68
69 return apply_filters('fluentform/generated_protection_token', Protector::encrypt($data), $formId, $timeStamp);
70 }
71
72 public function verify($insertData, $requestData, $formId)
73 {
74 if (
75 !$this->isEnabled($formId) ||
76 (
77 Helper::isConversionForm($formId) &&
78 Arr::isTrue($requestData, 'isFFConversational')
79 )
80 ) {
81 return;
82 }
83
84 $fieldName = $this->getFieldName($formId);
85 $token = sanitize_text_field(Arr::get($requestData, $fieldName));
86 if (!$token || !$this->validateToken($token, $formId)) {
87 $errorMessage = apply_filters(
88 'fluentform/token_based_validation_error_message',
89 __('Suspicious activity detected. Form submission blocked', 'fluentform'),
90 $formId
91 );
92
93 $this->handleSpam($errorMessage);
94 }
95 }
96
97 private function validateToken($token, $formId)
98 {
99 try {
100
101 $decrypted = Protector::decrypt($token);
102 if (!$decrypted) {
103 return false;
104 }
105
106 $parts = explode('|', $decrypted);
107 if (count($parts) !== 3) {
108 return false;
109 }
110
111 [$timestamp, $tokenFormId, $fieldName] = $parts;
112
113 // Ensure all components are valid
114 if (!is_numeric($timestamp) || !is_numeric($tokenFormId)) {
115 return false;
116 }
117
118 $expirationTime = apply_filters('fluentform/token_expiration_time', 3600, $formId); //1 hour
119 if ($timestamp + $expirationTime < current_time('timestamp')) {
120 return false;
121 }
122
123 $isValid = (int)$tokenFormId === $formId && $fieldName === $this->getFieldName($formId);
124 return apply_filters('fluentform/token_based_validation_result',
125 $isValid,
126 $timestamp,
127 $tokenFormId,
128 $formId);
129
130 } catch (\Exception $e) {
131 return false;
132 }
133 }
134
135
136 private function handleSpam($reason)
137 {
138 do_action('fluentform/spam_attempt_caught', $reason);
139
140 wp_send_json([
141 'errors' => $reason
142 ], 422);
143 }
144
145 public function isEnabled($formId = false)
146 {
147 $option = get_option('_fluentform_global_form_settings');
148 $status = 'yes' === Arr::get($option, 'misc.tokenBasedProtectionStatus');
149 return apply_filters('fluentform/token_based_spam_protection_status', $status, $formId);
150 }
151
152 private function getFieldName($formId)
153 {
154 $tokenInputName = '__fluent_protection_token_'. $formId;
155 return apply_filters('fluentform/token_protection_name', $tokenInputName, $formId);
156 }
157 }
158