PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
← All changes | src/component/security-tweaks/class-security-key.php +131 -42 5.9.0 → 6.3.0 View file →
@@ -71,8 +71,15 @@
71 71 */
72 72 private bool $is_autogenerate_keys = true;
73 73
74 74 /**
75 + * Flag set when the user explicitly reverts this tweak via the Edit button.
76 + *
77 + * @var bool
78 + */
79 + private bool $is_reverted = false;
80 +
81 + /**
75 82 * Constructor for Security_Key.
76 83 */
77 84 public function __construct() {
78 85 $this->file = defender_wp_config_path();
@@ -85,8 +92,12 @@
85 92 *
86 93 * @return bool|void
87 94 */
88 95 public function check() {
96 + if ( $this->is_reverted ) {
97 + return false;
98 + }
99 +
89 100 if ( ! $this->is_salts_exist() ) {
90 101 return false;
91 102 }
92 103
@@ -105,8 +116,9 @@
105 116 private function get_options(): void {
106 117 $options = get_site_option( 'defender_security_tweaks_' . $this->slug );
107 118 $this->reminder_date = $options['reminder_date'] ?? 0;
108 119 $this->reminder_duration = isset( $options['reminder_duration'] ) && '' !== $options['reminder_duration'] ? $options['reminder_duration'] : $this->default_days;
120 + $this->is_reverted = isset( $options['is_reverted'] ) && true === (bool) $options['is_reverted'];
109 121
110 122 $last_modified = $this->get_wp_config_last_modified_time();
111 123 if ( false === $last_modified ) {
112 124 $last_modified = $options['last_modified'] ?? null;
@@ -165,8 +177,13 @@
165 177 *
166 178 * @return bool
167 179 */
168 180 public function revert(): bool {
181 + $values = get_site_option( 'defender_security_tweaks_' . $this->slug, array() );
182 + $values['is_reverted'] = true;
183 + update_site_option( 'defender_security_tweaks_' . $this->slug, $values );
184 + $this->is_reverted = true;
185 +
169 186 return true;
170 187 }
171 188
172 189 /**
@@ -178,24 +195,20 @@
178 195 return true;
179 196 }
180 197
181 198 /**
182 - * Get salts to be placed in wp-config.php.
199 + * Parse and validate salt definitions string.
183 200 *
184 - * @return array|WP_Error
201 + * @param string $body API response body.
202 + * @param array $constants Expected constant keys.
203 + *
204 + * @return array|false
185 205 */
186 - private function get_salts(): WP_Error|array {
187 - $response = wp_safe_remote_get( 'https://api.wordpress.org/secret-key/1.1/salt/' );
188 -
189 - if ( is_wp_error( $response ) ) {
190 - return new WP_Error(
191 - 'defender_salts_not_found',
192 - esc_html__( 'Unable to generate salts. Please try again.', 'defender-security' )
193 - );
194 - }
195 -
196 - $raw_salts = explode( "\n", wp_remote_retrieve_body( $response ) );
206 + private function parse_and_validate_salts( string $body, array $constants ): array|false {
207 + $raw_salts = explode( "\n", $body );
197 208 $salts = array();
209 + $values = array();
210 + $index = 0;
198 211
199 212 foreach ( $raw_salts as $salt ) {
200 213 $salt = trim( $salt );
201 214 if ( '' === $salt ) {
@@ -200,15 +213,68 @@
200 213 $salt = trim( $salt );
201 214 if ( '' === $salt ) {
202 215 continue;
203 216 }
204 - $salts[] = stripslashes( $salt );
217 +
218 + $salt = stripslashes( $salt );
219 + $expected_const = $constants[ $index ] ?? null;
220 +
221 + if (
222 + null === $expected_const ||
223 + 0 !== strpos( $salt, 'define(' ) ||
224 + 1 !== preg_match( '/^define\(\s*[\'"]' . preg_quote( $expected_const, '/' ) . '[\'"]\s*,\s*[\'"](.*?)[\'"]\s*\)\s*;$/s', $salt, $matches )
225 + ) {
226 + return false;
227 + }
228 +
229 + $val = $matches[1];
230 + if ( 'put your unique phrase here' === $val || strlen( $val ) < 64 || isset( $values[ $val ] ) ) {
231 + return false;
232 + }
233 +
234 + $values[ $val ] = true;
235 + $salts[] = $salt;
236 + ++$index;
205 237 }
206 238
239 + if ( count( $salts ) !== count( $constants ) ) {
240 + return false;
241 + }
242 +
207 243 return $salts;
208 244 }
209 245
210 246 /**
247 + * Get salts to be placed in wp-config.php.
248 + *
249 + * @return array|WP_Error
250 + */
251 + private function get_salts(): WP_Error|array {
252 + $constants = $this->get_constants();
253 + $max_attempts = 3;
254 + $attempt = 0;
255 +
256 + while ( $attempt < $max_attempts ) {
257 + ++$attempt;
258 + $response = wp_safe_remote_get( 'https://api.wordpress.org/secret-key/1.1/salt/' );
259 +
260 + if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
261 + continue;
262 + }
263 +
264 + $salts = $this->parse_and_validate_salts( wp_remote_retrieve_body( $response ), $constants );
265 + if ( false !== $salts ) {
266 + return $salts;
267 + }
268 + }
269 +
270 + return new WP_Error(
271 + 'defender_salts_not_found',
272 + esc_html__( 'Unable to generate salts. Please try again.', 'defender-security' )
273 + );
274 + }
275 +
276 + /**
211 277 * Get how long the wp-config file is last updated.
212 278 *
213 279 * @return int|string
214 280 */
@@ -241,9 +307,9 @@
241 307 *
242 308 * @return string
243 309 */
244 310 public function get_label(): string {
245 - return esc_html__( 'Update old security keys', 'defender-security' );
311 + return esc_html__( 'Update security keys', 'defender-security' );
246 312 }
247 313
248 314 /**
249 315 * Get the error reason.
@@ -250,30 +316,40 @@
250 316 *
251 317 * @return string
252 318 */
253 319 public function get_error_reason(): string {
254 - $get_last_modified_days = $this->get_last_modified_days();
255 -
256 - if ( 'unknown' === $get_last_modified_days ) {
257 - $error_message = esc_html__(
258 - 'We can\'t tell how old your security keys are, perhaps it\'s time to update them?',
259 - 'defender-security'
320 + if ( ! $this->is_salts_exist() ) {
321 + return wp_kses(
322 + __( '<strong>Your security keys are outdated</strong> <br /> and one or more security salts are missing in wp-config.php.', 'defender-security' ),
323 + array(
324 + 'strong' => array(),
325 + 'br' => array(),
326 + )
260 327 );
261 328 }
262 - if ( ! $this->is_salts_exist() ) {
263 - $error_message = esc_html__(
264 - 'One or more security salts aren\'t defined in wp-config.php. Time to regenerate them!',
265 - 'defender-security'
329 +
330 + $days = $this->get_last_modified_days();
331 + if ( 'unknown' === $days ) {
332 + return wp_kses(
333 + __( '<strong>Your security keys are outdated</strong> <br /> and we can\'t tell how old they are.', 'defender-security' ),
334 + array(
335 + 'strong' => array(),
336 + 'br' => array(),
337 + )
266 338 );
267 - } else {
268 - $error_message = sprintf(
269 - /* translators: %s: number of days */
270 - esc_html__( 'Your current security keys are %s days old. Time to update them!', 'defender-security' ),
271 - $get_last_modified_days
272 - );
273 339 }
274 340
275 - return $error_message;
341 + return wp_kses(
342 + sprintf(
343 + /* translators: %1$s: opening strong tag, %2$s: closing strong tag */
344 + __( '%1$sYour security keys are outdated.%2$s', 'defender-security' ),
345 + '<strong>',
346 + '</strong>'
347 + ),
348 + array(
349 + 'strong' => array(),
350 + )
351 + );
276 352 }
277 353
278 354 /**
279 355 * Return a summary data of this tweak.
@@ -280,25 +356,36 @@
280 356 *
281 357 * @return array
282 358 */
283 359 public function to_array(): array {
360 + $auto_regenerate = $this->get_is_autogenerate_keys();
361 + $auto_status = $auto_regenerate
362 + ? __( 'Auto-regenerate is turned on.', 'defender-security' )
363 + : __( 'Auto-regenerate is turned off.', 'defender-security' );
364 + $saved_duration = $this->get_option( 'reminder_duration' );
365 + $reminder_duration = ( is_string( $saved_duration ) && '' !== $saved_duration )
366 + ? $saved_duration
367 + : $this->default_days;
368 +
284 369 return array(
285 370 'slug' => $this->slug,
286 371 'title' => $this->get_label(),
287 372 'errorReason' => $this->get_error_reason(),
288 - 'successReason' => sprintf(
289 - /* translators: %s: number of days */
290 - esc_html__( 'Your security keys are less than %s days old, nice work.', 'defender-security' ),
291 - $this->get_last_modified_days()
373 + 'successReason' => wp_strip_all_tags(
374 + sprintf(
375 + /* translators: %1$s: reminder duration, %2$s: auto-regenerate status */
376 + __( 'Security keys regenerate every %1$s. %2$s', 'defender-security' ),
377 + $reminder_duration,
378 + $auto_status
379 + )
292 380 ),
293 381 'misc' => array(
294 - 'reminder' => $this->reminder_duration,
382 + 'show_revert_button' => true,
383 + 'reminder' => $reminder_duration,
384 + 'show_action_button' => true,
295 385 ),
296 - 'bulk_description' => esc_html__(
297 - 'Your current security keys are unknown days old. Time to update them! We will update the frequency to 60 days.',
298 - 'defender-security'
299 - ),
300 - 'bulk_title' => esc_html__( 'Security Keys', 'defender-security' ),
386 + 'bulk_description' => wp_strip_all_tags( __( 'WordPress security keys help protect user sessions and cookies. Update security keys with strong, unique values to improve encryption and reduce the risk of unauthorized access.', 'defender-security' ) ),
387 + 'bulk_title' => wp_strip_all_tags( __( 'Security Keys', 'defender-security' ) ),
301 388 );
302 389 }
303 390
304 391 /**
@@ -528,8 +615,10 @@
528 615 }
529 616 $values = get_site_option( 'defender_security_tweaks_' . $this->slug, array() );
530 617 $this->last_modified = defender_get_current_time();
531 618 $values['last_modified'] = defender_get_current_time();
619 + unset( $values['is_reverted'] );
620 + $this->is_reverted = false;
532 621 update_site_option( 'defender_security_tweaks_' . $this->slug, $values );
533 622 $this->log( 'Security keys are updated.', Security_Tweak::LOG_FILE_NAME );
534 623 if ( is_multisite() && ! headers_sent() ) {
535 624 // Delete cookies forced.