← All changes
|
app/Services/FluentConversational/Classes/Form.php
+27
-2
6.2.7
→
6.2.15
View file →
| @@ -569,9 +569,12 @@ | ||
| 569 | 569 | $instanceId = $form->instance_index; |
| 570 | 570 | $varName = 'fluent_forms_global_var_' . $instanceId; |
| 571 | 571 | |
| 572 | 572 | $localizedVars = [ |
| 573 | - 'fluent_forms_admin_nonce' => wp_create_nonce('fluent_forms_admin_nonce'), | |
| 573 | + // SECURITY (H-01): do not emit the admin AJAX nonce on the public conversational form | |
| 574 | + // page — the public form JS never uses it, and on a page an admin happens to view it | |
| 575 | + // would embed that admin's own valid nonce. Emit it only for a viewer who could use it. | |
| 576 | + 'fluent_forms_admin_nonce' => current_user_can('fluentform_dashboard_access') ? wp_create_nonce('fluent_forms_admin_nonce') : '', | |
| 574 | 577 | 'ajaxurl' => admin_url('admin-ajax.php'), |
| 575 | 578 | 'nonce' => wp_create_nonce(), |
| 576 | 579 | 'form' => $this->getLocalizedForm($form), |
| 577 | 580 | 'assetBaseUrl' => FLUENT_CONVERSATIONAL_FORM_DIR_URL . 'public', |
| @@ -682,8 +685,27 @@ | ||
| 682 | 685 | '_fluentform_' . $formId . '_fluentformnonce' => wp_create_nonce('fluentform-submit-form'), |
| 683 | 686 | '_wp_http_referer' => esc_attr(wp_unslash(wpFluentForm('request')->server('REQUEST_URI'))), |
| 684 | 687 | ]; |
| 685 | 688 | |
| 689 | + // SECURITY (FINDING-25): carry the anti-spam honeypot field (empty) and a freshly minted | |
| 690 | + // token in the conversational submission. The conversational JS forwards every extra_input | |
| 691 | + // into the submission payload, so these reach the server-side checks — which no longer skip | |
| 692 | + // conversational forms — WITHOUT any client/JS change. This closes the bypass where adding | |
| 693 | + // isFFConversational=1 disabled honeypot + token protection entirely. | |
| 694 | + // | |
| 695 | + // The token itself is disabled for conversational forms server-side (see the | |
| 696 | + // fluentform/token_based_spam_protection_status filter in actions.php): its ~1h TTL cannot be | |
| 697 | + // refreshed by the conversational JS, so behind a full-page cache the baked-in token would | |
| 698 | + // expire and reject every submission. getConversationalTokenInput() therefore returns [] for | |
| 699 | + // conversational forms; the honeypot (static empty field) still applies. | |
| 700 | + $honeyPot = new \FluentForm\App\Modules\Form\HoneyPot(wpFluentForm()); | |
| 701 | + $inputs = array_merge($inputs, $honeyPot->getConversationalHoneypotInput($formId)); | |
| 702 | + | |
| 703 | + $inputs = array_merge( | |
| 704 | + $inputs, | |
| 705 | + \FluentForm\App\Modules\Form\TokenBasedSpamProtection::getConversationalTokenInput($formId) | |
| 706 | + ); | |
| 707 | + | |
| 686 | 708 | return apply_filters('fluentform/conversational_extra_inputs', $inputs, $formId); |
| 687 | 709 | } |
| 688 | 710 | |
| 689 | 711 | public function getRandomPhoto() |
| @@ -774,9 +796,12 @@ | ||
| 774 | 796 | |
| 775 | 797 | $designSettings = $this->getDesignSettings($formId); |
| 776 | 798 | |
| 777 | 799 | $localizedVars = [ |
| 778 | - 'fluent_forms_admin_nonce' => wp_create_nonce('fluent_forms_admin_nonce'), | |
| 800 | + // SECURITY (H-01): do not emit the admin AJAX nonce on the public conversational form | |
| 801 | + // page — the public form JS never uses it, and on a page an admin happens to view it | |
| 802 | + // would embed that admin's own valid nonce. Emit it only for a viewer who could use it. | |
| 803 | + 'fluent_forms_admin_nonce' => current_user_can('fluentform_dashboard_access') ? wp_create_nonce('fluent_forms_admin_nonce') : '', | |
| 779 | 804 | 'ajaxurl' => admin_url('admin-ajax.php'), |
| 780 | 805 | 'nonce' => wp_create_nonce(), |
| 781 | 806 | 'form' => $this->getLocalizedForm($form), |
| 782 | 807 | 'form_id' => $form->id, |