← All changes
|
includes/widgets/Form_Builder/helpers/Create_Submission.php
+82
-1
51.1.81
→
51.1.87
View file →
| @@ -10,10 +10,21 @@ | ||
| 10 | 10 | * Stores Form Builder submissions from the public AJAX endpoint. |
| 11 | 11 | */ |
| 12 | 12 | class Create_Submission |
| 13 | 13 | { |
| 14 | + /** | |
| 15 | + * HMAC of the one-time access secret for this submission. | |
| 16 | + */ | |
| 17 | + public const META_ACCESS_SECRET = 'king_addons_fb_access_secret'; | |
| 14 | 18 | |
| 15 | 19 | /** |
| 20 | + * Plain secrets issued in this request, keyed by submission ID. | |
| 21 | + * | |
| 22 | + * @var array<int,string> | |
| 23 | + */ | |
| 24 | + private static array $issued_secrets = []; | |
| 25 | + | |
| 26 | + /** | |
| 16 | 27 | * Registers submission AJAX hooks and admin meta updates. |
| 17 | 28 | */ |
| 18 | 29 | public function __construct() |
| 19 | 30 | { |
| @@ -66,11 +77,11 @@ | ||
| 66 | 77 | if ($post_id) { |
| 67 | 78 | wp_send_json_success(array( |
| 68 | 79 | 'action' => 'king_addons_form_builder_submissions', |
| 69 | 80 | 'post_id' => $post_id, |
| 81 | + 'access_secret' => self::issued_access_secret($post_id), | |
| 70 | 82 | 'message' => esc_html__('Submission created successfully', 'king-addons'), |
| 71 | 83 | 'status' => 'success' |
| 72 | - // Security fix: Removed unsanitized form_content from response to prevent XSS | |
| 73 | 84 | )); |
| 74 | 85 | } else { |
| 75 | 86 | wp_send_json_success(array( |
| 76 | 87 | 'action' => 'king_addons_form_builder_submissions', |
| @@ -139,10 +150,80 @@ | ||
| 139 | 150 | update_post_meta($post_id, 'king_addons_form_page_id', $form_page_id); |
| 140 | 151 | $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_textarea_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : ''; |
| 141 | 152 | update_post_meta($post_id, 'king_addons_user_agent', $user_agent); |
| 142 | 153 | update_post_meta($post_id, 'king_addons_user_ip', Core::getClientIP()); |
| 154 | + self::issue_access_secret($post_id); | |
| 143 | 155 | |
| 144 | 156 | return $post_id; |
| 157 | + } | |
| 158 | + | |
| 159 | + /** | |
| 160 | + * Create a secret that later public requests must present to touch this submission. | |
| 161 | + * | |
| 162 | + * Only the HMAC is stored. The plaintext is kept for this request so the | |
| 163 | + * creator can send it with the payment call. | |
| 164 | + * | |
| 165 | + * @param int $submission_id Submission post ID. | |
| 166 | + * @return string Plaintext secret. | |
| 167 | + */ | |
| 168 | + public static function issue_access_secret(int $submission_id): string | |
| 169 | + { | |
| 170 | + if ($submission_id < 1) { | |
| 171 | + return ''; | |
| 172 | + } | |
| 173 | + | |
| 174 | + try { | |
| 175 | + $secret = bin2hex(random_bytes(32)); | |
| 176 | + } catch (\Exception $e) { | |
| 177 | + $secret = wp_generate_password(64, false, false); | |
| 178 | + } | |
| 179 | + | |
| 180 | + update_post_meta($submission_id, self::META_ACCESS_SECRET, hash_hmac('sha256', $secret, self::access_secret_key())); | |
| 181 | + self::$issued_secrets[$submission_id] = $secret; | |
| 182 | + | |
| 183 | + return $secret; | |
| 184 | + } | |
| 185 | + | |
| 186 | + /** | |
| 187 | + * Plaintext secret issued for this submission in the current request. | |
| 188 | + * | |
| 189 | + * @param int $submission_id Submission post ID. | |
| 190 | + * @return string | |
| 191 | + */ | |
| 192 | + public static function issued_access_secret(int $submission_id): string | |
| 193 | + { | |
| 194 | + return self::$issued_secrets[$submission_id] ?? ''; | |
| 195 | + } | |
| 196 | + | |
| 197 | + /** | |
| 198 | + * Whether the posted secret matches the one stored for this submission. | |
| 199 | + * | |
| 200 | + * @param int $submission_id Submission post ID. | |
| 201 | + * @param string $secret Plaintext from the request. | |
| 202 | + * @return bool | |
| 203 | + */ | |
| 204 | + public static function verify_access_secret(int $submission_id, string $secret): bool | |
| 205 | + { | |
| 206 | + if ($submission_id < 1 || '' === $secret) { | |
| 207 | + return false; | |
| 208 | + } | |
| 209 | + | |
| 210 | + $stored = (string) get_post_meta($submission_id, self::META_ACCESS_SECRET, true); | |
| 211 | + if ('' === $stored) { | |
| 212 | + return false; | |
| 213 | + } | |
| 214 | + | |
| 215 | + return hash_equals($stored, hash_hmac('sha256', $secret, self::access_secret_key())); | |
| 216 | + } | |
| 217 | + | |
| 218 | + /** | |
| 219 | + * Key used to HMAC submission access secrets. | |
| 220 | + * | |
| 221 | + * @return string | |
| 222 | + */ | |
| 223 | + private static function access_secret_key(): string | |
| 224 | + { | |
| 225 | + return 'king-addons-fb-sub|' . wp_salt('auth'); | |
| 145 | 226 | } |
| 146 | 227 | |
| 147 | 228 | /** |
| 148 | 229 | * Saves admin edits to an existing submission. |