PluginProbe ʕ •ᴥ•ʔ
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) / 9.5.7
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) v9.5.7
9.5.11 9.5.10.1 9.5.10 trunk 9.4.0 9.4.1 9.4.2 9.4.3 9.5.0 9.5.0.1 9.5.0.2 9.5.1 9.5.2 9.5.2.2 9.5.2.3 9.5.3 9.5.3.1 9.5.3.2 9.5.4 9.5.5 9.5.6 9.5.7 9.5.8 9.5.9
really-simple-ssl / class-site-health.php
really-simple-ssl Last commit date
assets 3 months ago core 3 months ago languages 3 months ago lets-encrypt 4 months ago lib 6 months ago mailer 7 months ago modal 3 months ago placeholders 9 months ago progress 1 year ago security 3 months ago settings 3 months ago testssl 5 years ago upgrade 7 months ago .wp-env.json 10 months ago SECURITY.md 9 months ago class-admin.php 3 months ago class-cache.php 4 months ago class-certificate.php 2 years ago class-front-end.php 6 months ago class-installer.php 10 months ago class-mixed-content-fixer.php 3 years ago class-multisite.php 4 months ago class-server.php 4 months ago class-site-health.php 1 year ago class-wp-cli.php 5 months ago compatibility.php 1 year ago force-deactivate.txt 1 year ago functions.php 5 months ago index.php 2 years ago readme.txt 3 months ago rector.php 1 year ago rlrsssl-really-simple-ssl.php 3 months ago rsssl-auto-loader.php 1 year ago sbom.json.gz 3 months ago ssl-test-page.php 2 years ago system-status.php 8 months ago uninstall.php 4 months ago upgrade.php 4 months ago
class-site-health.php
380 lines
1 <?php defined( 'ABSPATH' ) or die();
2
3 if ( ! class_exists( 'rsssl_site_health' ) ) {
4 class rsssl_site_health {
5 private static $_this;
6 public function __construct() {
7
8 if ( isset( self::$_this ) ) {
9 wp_die( 'you cannot create a second instance.' );
10 }
11
12 add_filter( 'site_status_tests', array( $this, 'health_check' ), 1, 10 );
13 self::$_this = $this;
14 }
15
16 public static function this() {
17 return self::$_this;
18 }
19
20
21 /**
22 * Add SSL dedicated health check
23 * @param array $tests
24 *
25 * @return array
26 */
27 public function health_check( $tests ) {
28 unset( $tests['async']['https_status'] );
29 if ( ! rsssl_get_option( 'dismiss_all_notices' ) ) {
30 $tests['direct']['rsssl_ssl_health'] = array(
31 'label' => __( 'SSL Status Test', 'really-simple-ssl' ),
32 'test' => array( $this, 'ssl_tests' ),
33 );
34
35 $tests['direct']['headers_test'] = array(
36 'label' => __( 'Security Headers Test', 'really-simple-ssl' ),
37 'test' => array( $this, 'headers_test' ),
38 );
39
40 unset( $tests['direct']['debug_enabled'] );
41 if ( rsssl_is_debugging_enabled() && rsssl_debug_log_value_is_default() ) {
42 $tests['direct']['rsssl_debug_log'] = array(
43 'test' => array( $this, 'site_health_debug_log_test' ),
44 );
45 }
46
47 if ( rsssl_maybe_disable_404_blocking() ) {
48 $tests['direct']['rsssl_404_test'] = array(
49 'test' => array( $this, 'site_health_404_display' ),
50 );
51 }
52
53 if ( rsssl_get_option( 'enable_vulnerability_scanner' ) ) {
54 $vulnerabilities = new rsssl_vulnerabilities();
55 $tests['direct']['rsssl_vulnerabilities'] = array(
56 'test' => [ $vulnerabilities, 'get_site_health_notice' ],
57 );
58 }
59 // Two-Factor Authentication (2FA) test
60 $tests['direct']['rsssl_2fa_test'] = array(
61 'label' => __( 'Two-Factor Authentication', 'really-simple-ssl' ),
62 'test' => array( $this, 'two_factor_auth_test' ),
63 );
64
65 // Limit Login Attempts (LLA) test
66 $tests['direct']['rsssl_lla_test'] = array(
67 'label' => __( 'Limit Login Attempts Protection', 'really-simple-ssl' ),
68 'test' => array( $this, 'limit_login_attempts_test' ),
69 );
70
71 // Firewall Protection test
72 $tests['direct']['rsssl_firewall_test'] = array(
73 'label' => __( 'Firewall Protection', 'really-simple-ssl' ),
74 'test' => array( $this, 'firewall_test' ),
75 );
76
77 }
78
79 return $tests;
80 }
81
82 /**
83 * Test for Two-Factor Authentication (2FA)
84 * @return array
85 */
86 public function two_factor_auth_test() {
87 $status = 'recommended';
88 $description = __( 'We recommend to enable Two-Factor Authentication at least for administrators.', 'really-simple-ssl' );
89
90 // Check if RSSSL 2FA, WordFence, Solid Security, AIOS are installed and 2FA is enabled
91 if ( rsssl_get_option('login_protection_enabled') == '1' || is_plugin_active('wordfence/wordfence.php') || is_plugin_active('two-factor/two-factor.php') || is_plugin_active('all-in-one-wp-security-and-firewall/wp-security.php') || is_plugin_active('better-wp-security/better-wp-security.php') ) {
92 $status = 'good';
93 $description = __( 'Your site is protected by Two-Factor Authentication (2FA).', 'really-simple-ssl' );
94 }
95
96 return array(
97 'label' => __( 'Protect your user logins with Two-Factor Authentication (at least for Administrator accounts)', 'really-simple-ssl' ),
98 'status' => $status,
99 'badge' => array(
100 'label' => __( 'Security', 'really-simple-ssl' ),
101 'color' => 'blue',
102 ),
103 'description' => sprintf( '<p>%s</p>', $description ),
104 'actions' => sprintf(
105 '<p><a href="%s" target="_blank">%s</a></p>',
106 esc_url( admin_url( 'admin.php?page=really-simple-security#settings/two-fa' ) ),
107 __( 'Read more', 'really-simple-ssl' )
108 ),
109 'test' => 'rsssl_2fa_test',
110 );
111 }
112
113 /**
114 * Test for Limit Login Attempts (LLA)
115 * @return array
116 */
117 public function limit_login_attempts_test() {
118 $status = 'recommended';
119 $description = __( 'Enable Limit Login Attempts to protect the login form against brute-force attacks.', 'really-simple-ssl' );
120
121 // Check if RSSSL LLA or Limit Login Attempts Reloaded is installed and active
122 if ( rsssl_get_option('enable_limited_login_attempts') == '1' || is_plugin_active('wordfence/wordfence.php') || is_plugin_active('limit-login-attempts-reloaded/limit-login-attempts-reloaded.php') || is_plugin_active('better-wp-security/better-wp-security.php') ) {
123 $status = 'good';
124 $description = __( 'Your site is protected by Limit Login Attempts.', 'really-simple-ssl' );
125 }
126
127 return array(
128 'label' => __( 'Protect your login form with Limit Login Attempts', 'really-simple-ssl' ),
129 'status' => $status,
130 'badge' => array(
131 'label' => __( 'Security', 'really-simple-ssl' ),
132 'color' => 'blue',
133 ),
134 'description' => sprintf( '<p>%s</p>', $description ),
135 'actions' => sprintf(
136 '<p><a href="%s" target="_blank">%s</a></p>',
137 esc_url( admin_url( 'admin.php?page=really-simple-security#settings/limit_login_attempts' ) ),
138 __( 'Read more', 'really-simple-ssl' )
139 ),
140 'test' => 'rsssl_lla_test',
141 );
142 }
143
144 /**
145 * Test for Firewall Protection
146 * @return array
147 */
148 public function firewall_test() {
149 $status = 'recommended';
150 $description = __( 'Secure your site with the performant Firewall.', 'really-simple-ssl' );
151
152 // Check if WordFence, AIOS, or Solid Security is installed
153 if ( rsssl_get_option('enable_firewall') || is_plugin_active('wordfence/wordfence.php') || is_plugin_active('all-in-one-wp-security-and-firewall/wp-security.php') || is_plugin_active('better-wp-security/better-wp-security.php') ) {
154 $status = 'good';
155 $description = __( 'Your site is protected by a firewall.', 'really-simple-ssl' );
156 }
157
158 return array(
159 'label' => __( 'Secure your site with a Firewall', 'really-simple-ssl' ),
160 'status' => $status,
161 'badge' => array(
162 'label' => __( 'Security', 'really-simple-ssl' ),
163 'color' => 'blue',
164 ),
165 'description' => sprintf( '<p>%s</p>', $description ),
166 'actions' => sprintf(
167 '<p><a href="%s" target="_blank">%s</a></p>',
168 esc_url( admin_url( 'admin.php?page=really-simple-security#settings/firewall' ) ),
169 __( 'Read more', 'really-simple-ssl' )
170 ),
171 'test' => 'rsssl_firewall_test',
172 );
173 }
174
175 /**
176 * Generate the WP_DEBUG notice
177 *
178 */
179 public function site_health_debug_log_test() {
180 $result = array(
181 'label' => __( 'Your site is set to log errors to a potentially public file' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
182 'status' => 'recommended',
183 'badge' => array(
184 'label' => __( 'Security' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
185 'color' => 'blue',
186 ),
187 'description' => sprintf(
188 '<p>%s</p>',
189 __( 'The value, WP_DEBUG_LOG, has been added to this website’s configuration file. This means any errors on the site will be written to a file which is potentially available to all users.', 'really-simple-ssl' )
190 ),
191 'actions' => sprintf(
192 '<p><a href="%s" target="_blank" rel="noopener noreferrer">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>',
193 /* translators: Documentation explaining debugging in WordPress. */
194 esc_url( rsssl_admin_url([], '#settings/hardening') ),
195 __( 'Remove from public location with Really Simple Security', 'really-simple-ssl' ),
196 /* translators: Accessibility text. */
197 __( '(opens in a new tab)' )// phpcs:ignore WordPress.WP.I18n.MissingArgDomain
198 ),
199 'test' => 'rsssl_debug_log',
200 );
201
202 return $result;
203 }
204
205 /**
206 * Explain users about risks of debug display
207 *
208 */
209 public function site_health_debug_display_test() {
210 $result = array(
211 'label' => __( 'Your site is set to display errors on your website', 'really-simple-ssl' ),
212 'status' => 'critical',
213 'badge' => array(
214 'label' => __( 'Security' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
215 'color' => 'blue',
216 ),
217 'description' => sprintf(
218 '<p>%s</p>',
219 __( 'The value, WP_DEBUG_DISPLAY, has either been enabled by WP_DEBUG or added to your configuration file. This will make errors display on the front end of your site.', 'really-simple-ssl' )
220 ),
221 'actions' => sprintf(
222 '<p><a href="%s" target="_blank" rel="noopener noreferrer">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>',
223 /* translators: Documentation explaining debugging in WordPress. */
224 esc_url( rsssl_link('security/debug-display-enabled') ),
225 __( 'Read more about security concerns with debug display enabled', 'really-simple-ssl' ),
226 /* translators: Accessibility text. */
227 __( '(opens in a new tab)' )// phpcs:ignore WordPress.WP.I18n.MissingArgDomain
228 ),
229 'test' => 'rsssl_debug_display',
230 );
231
232 return $result;
233 }
234
235 /**
236 * Check for 404 errors.
237 *
238 */
239 public function site_health_404_display() {
240 $result = array(
241 'label' => __( '404 errors detected on your homepage', 'really-simple-ssl' ),
242 'status' => 'critical',
243 'badge' => array(
244 'label' => __( 'Security' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
245 'color' => 'blue',
246 ),
247 'description' => sprintf(
248 '<p>%s</p>',
249 __( '404 errors detected on your homepage. This means that the page requests images, scripts or other resources that are no longer available. It can interfere with your Firewall as well.', 'really-simple-ssl' )
250 ),
251 'actions' => sprintf(
252 '<p><a href="%s" target="_blank" rel="noopener noreferrer">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>',
253 /* translators: Documentation explaining debugging in WordPress. */
254 esc_url( rsssl_link('404-not-found-errors') ),
255 __( 'Read more', 'really-simple-ssl' ),
256 /* translators: Accessibility text. */
257 __( '(opens in a new tab)' )// phpcs:ignore WordPress.WP.I18n.MissingArgDomain
258 ),
259 'test' => 'rsssl_404_test',
260 );
261
262 return $result;
263 }
264
265 /**
266 * Test to check if the recommended security headers are present
267 * @return array
268 */
269
270 public function headers_test() {
271 $result = array(
272 'label' => __( 'Essential security headers installed', 'really-simple-ssl' ),
273 'status' => 'good',
274 'badge' => array(
275 'label' => __( 'Security' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
276 'color' => 'blue',
277 ),
278 'description' => sprintf(
279 '<p>%s</p>',
280 __( 'The essential security headers are detected on your site.', 'really-simple-ssl' )
281 ),
282 'actions' => '',
283 'test' => 'headers_test',
284 );
285
286 //returns empty for sites without .htaccess, or if all headers are already in use
287 $recommended_headers = RSSSL()->admin->get_recommended_security_headers();
288 if ( ! empty( $recommended_headers ) ) {
289 $style = '<style>.rsssl-sec-headers-list li {list-style-type:disc;margin-left:20px;}</style>';
290 $list = '<ul class="rsssl-sec-headers-list"><li>' . implode( '</li><li>', $recommended_headers ) . '</li></ul>';
291 $result['status'] = 'recommended';
292 $result['label'] = __( 'Not all essential security headers are installed', 'really-simple-ssl' );
293 $result['description'] = sprintf( '<p>%s</p>', __( 'Your website does not send all essential security headers.', 'really-simple-ssl' ) . $style . $list );
294 $result['actions'] = sprintf(
295 '<p><a href="%s" target="_blank" rel="noopener noreferrer">%s</a></p>',
296 rsssl_link('site-health-recommended-security-headers/'),
297 __( 'Read more', 'really-simple-ssl' )
298 );
299 }
300
301 return $result;
302 }
303
304 /**
305 * Some basic SSL health checks
306 * @return array
307 */
308 public function ssl_tests() {
309 $url = rsssl_admin_url();
310
311 $result = array(
312 'label' => __( '301 SSL redirect enabled', 'really-simple-ssl' ),
313 'status' => 'good',
314 'badge' => array(
315 'label' => __( 'Security' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
316 'color' => 'blue',
317 ),
318 'description' => sprintf(
319 '<p>%s</p>',
320 __( 'You have set a 301 redirect to SSL. This is important for SEO purposes', 'really-simple-ssl' )
321 ),
322 'actions' => '',
323 'test' => 'rsssl_ssl_health',
324 );
325
326 if ( ! rsssl_get_option( 'ssl_enabled' ) ) {
327 if ( rsssl_get_option( 'site_has_ssl' ) ) {
328 $result['status'] = 'recommended';
329 $result['label'] = __( 'SSL is not enabled.', 'really-simple-ssl' );
330 $result['description'] = sprintf(
331 '<p>%s</p>',
332 __(
333 'Really Simple Security detected an SSL certificate, but has not been configured to enforce SSL.',
334 'really-simple-ssl'
335 )
336 );
337 $result['actions'] .= sprintf(
338 '<p><a href="%s">%s</a></p>',
339 $url,
340 __( 'Activate SSL', 'really-simple-ssl' )
341 );
342 } else {
343 $result['status'] = 'recommended';
344 $result['label'] = __( 'No SSL detected', 'really-simple-ssl' );
345 $result['description'] = sprintf(
346 '<p>%s</p>',
347 __( 'Really Simple Security is installed, but no valid SSL certificate is detected.', 'really-simple-ssl' )
348 );
349 }
350 } else {
351 if ( ! RSSSL()->admin->has_301_redirect() ) {
352 $result['status'] = 'recommended';
353 $result['label'] = __( 'No 301 redirect to SSL enabled.', 'really-simple-ssl' );
354 $result['description'] = sprintf(
355 '<p>%s</p>',
356 __( 'To ensure all traffic passes through SSL, please enable a 301 redirect.', 'really-simple-ssl' )
357 );
358 $result['actions'] .= sprintf(
359 '<p><a href="%s">%s</a></p>',
360 $url,
361 __( 'Enable 301 redirect', 'really-simple-ssl' )
362 );
363 } elseif ( RSSSL()->server->uses_htaccess() && rsssl_get_option( 'redirect' ) !== 'htaccess' ) {
364 $result['status'] = 'recommended';
365 $result['label'] = __( '301 .htaccess redirect is not enabled.', 'really-simple-ssl' );
366 $result['description'] = sprintf(
367 '<p>%s</p>',
368 __( 'The 301 .htaccess redirect is the fastest and most reliable redirect option.', 'really-simple-ssl' )
369 );
370 $result['actions'] .= sprintf(
371 '<p><a href="%s">%s</a></p>',
372 $url,
373 __( 'Enable 301 .htaccess redirect', 'really-simple-ssl' )
374 );
375 }
376 }
377 return $result;
378 }
379 }
380 }