PluginProbe
NETSENSAI Shield / trunk
NETSENSAI Shield vtrunk
1.6.1 trunk 1.1 1.2 1.3 1.4 1.4.1 1.4.2 1.4.3 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.6.0
netsensai-shield / netsensai-shield.php

netsensai-shield.php in NETSENSAI Shield trunk, at netsensai-shield.php

437 lines 20.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * NETSENSAI Shield
4 *
5 * Plugin Name: NETSENSAI Shield
6 * Plugin URI: https://www.netsensai.pl/store/
7 * Description: NETSENSAI Shield is a security plugin designed to enhance WordPress site protection by offering essential security features based on best practice principles.
8 * Version: 1.6.1
9 * Author: Rafał Gierlicki
10 * Author URI: https://www.netsensai.pl
11 * Text Domain: netsensai-shield
12 * Contributors: netsensai
13 * License: GPLv2 or later
14 * License URI: http://www.gnu.org/licenses/gpl-2.0.html
15 */
16
17 // Exit if accessed directly.
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit;
20 }
21
22 define( 'NS_SHIELD_VERSION', '1.6.1' );
23
24 /**
25 * Filter admin title so it's never null.
26 *
27 * @param string $title Admin page title.
28 * @return string Modified title.
29 */
30 function ns_shield_fix_admin_title( $title ) {
31 return (string) $title;
32 }
33 add_filter( 'admin_title', 'ns_shield_fix_admin_title', 0, 1 );
34
35 /**
36 * Define tooltip function for login URL explanation.
37 */
38 if ( ! function_exists( 'ns_shield_get_login_url_tooltip' ) ) {
39 function ns_shield_get_login_url_tooltip() {
40 return __(
41 'Changing the login URL helps protect your site from brute-force attacks aimed at the default wp-login.php endpoint. If the default URL remains unchanged, attackers could easily target it to attempt password cracking or credential stuffing attacks.',
42 'netsensai-shield'
43 );
44 }
45 }
46
47 /**
48 * Include plugin function files.
49 */
50 require_once plugin_dir_path( __FILE__ ) . 'includes/login_url_functions.php';
51 require_once plugin_dir_path( __FILE__ ) . 'includes/disable_wp_api_json.php';
52 require_once plugin_dir_path( __FILE__ ) . 'includes/disable_username_enumeration.php';
53 require_once plugin_dir_path( __FILE__ ) . 'includes/disable_xml_rpc.php';
54 require_once plugin_dir_path( __FILE__ ) . 'includes/disable_app_passwords.php';
55 require_once plugin_dir_path( __FILE__ ) . 'includes/disable_file_editor.php';
56 require_once plugin_dir_path( __FILE__ ) . 'includes/apply_security_headers.php';
57 require_once plugin_dir_path( __FILE__ ) . 'includes/disable_directory_indexing.php';
58 require_once plugin_dir_path( __FILE__ ) . 'includes/disable_default_admin.php';
59 require_once plugin_dir_path( __FILE__ ) . 'includes/apply_hsts_header.php';
60 //require_once plugin_dir_path( __FILE__ ) . 'includes/integrations/class-ns-shield-cache-integrator.php';
61 // Wczytaj integracje
62 function ns_shield_load_integrations() {
63 $ns_shield_integration_dir = plugin_dir_path( __FILE__ ) . 'includes/integrations/';
64
65 if ( is_dir( $ns_shield_integration_dir ) ) {
66 foreach ( glob( $ns_shield_integration_dir . '*.php' ) as $ns_shield_integration_file ) {
67 require_once $ns_shield_integration_file;
68 }
69 }
70 }
71 ns_shield_load_integrations();
72
73 // Inicjalizacja integratora cache
74 function ns_shield_init_cache_integrator() {
75 new NS_Shield_Cache_Integrator();
76 }
77 add_action( 'plugins_loaded', 'ns_shield_init_cache_integrator' );
78
79 /**
80 * Add settings link on the plugins page.
81 *
82 * @param array $links Array of action links.
83 * @return array Modified links.
84 */
85 function ns_shield_settings_link( $links ) {
86 $settings_link = '<a href="options-general.php?page=secure-options">' . esc_html__( 'Settings', 'netsensai-shield' ) . '</a>';
87 array_unshift( $links, $settings_link );
88 return $links;
89 }
90 add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'ns_shield_settings_link' );
91
92 /* =====================
93 SETTINGS PAGE
94 ===================== */
95
96 /**
97 * Display the settings page.
98 */
99 function ns_shield_secure_options_page() {
100 if ( ! current_user_can( 'manage_options' ) ) {
101 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'netsensai-shield' ) );
102 }
103
104 // Ścieżka do logo
105 $logo_url = plugin_dir_url( __FILE__ ) . 'assets/ns_logo.png';
106
107 // Tworzymy treść dla popupu z możliwości�
108 tłumaczenia.
109 // Używamy __() dla tekstu zawieraj�
110 cego znaczniki <a> aby linki były działaj�
111 ce.
112 $enable_html = sprintf(
113 '<div class="ns-popup-logo-container">
114 <img src="%1$s" alt="%2$s" class="ns-popup-logo" />
115 </div>
116 <div class="ns-popup-text">
117 <p>%3$s</p>
118 <p>%4$s</p>
119 <p>%5$s</p>
120 <p><strong>%6$s</strong> <a href="https://netsensai.pl/store" target="_blank">netsensai.pl/store</a></p>
121 </div>
122 <div class="ns-popup-button-container">
123 <button id="ns-shield-modal-ok" class="ns-modal-ok-button">%7$s</button>
124 </div>',
125 esc_url( $logo_url ),
126 esc_attr__( 'Netsensai Shield Logo', 'netsensai-shield' ),
127 // Używamy __() zamiast esc_html__() dla akapitu ze znacznikami <a>
128 __( 'Your website now achieves top scores in the most popular security scanners. (Check <a href="https://securityheaders.com" target="_blank">securityheaders.com</a> or <a href="https://observatory.mozilla.org" target="_blank">Mozilla Observatory</a>) Great job!', 'netsensai-shield' ),
129 esc_html__( 'But in the PRO Club, we do even more: we detect, block, and support.', 'netsensai-shield' ),
130 esc_html__( 'If an attack occurs, you get 3 months of assistance on us!', 'netsensai-shield' ),
131 esc_html__( 'Check out:', 'netsensai-shield' ),
132 esc_html__( 'OK', 'netsensai-shield' )
133 );
134
135 $disable_html = sprintf(
136 '<h2 style="text-align:center; font-size:1.3em; margin-bottom:10px; color:#000;">%1$s</h2>
137 <p style="font-size:1.1em; line-height:1.4; color:#000;">%2$s</p>',
138 esc_html__( 'Oops...', 'netsensai-shield' ),
139 esc_html__( 'The option has been disabled.', 'netsensai-shield' )
140 );
141 ?>
142 <div id="netsensai-shield-plugin" class="wrap">
143
144 <!-- PRO version banner -->
145 <div style="border: 1px solid #555; padding: 5px; margin: 5px 0; background-color: transparent; text-align: center; color: #fff;">
146 <p style="font-size: 1.2em; font-weight: bold; margin-bottom: 5px;">
147 <?php esc_html_e( 'Upgrade to NETSENSAI-SHIELD PRO for enhanced protection and advanced features.', 'netsensai-shield' ); ?>
148 </p>
149 <p>
150 <?php esc_html_e( 'Get yours now at:', 'netsensai-shield' ); ?>
151 <a href="https://netsensai.pl/store" target="_blank" rel="noopener noreferrer" style="color: #fff; text-decoration: underline;">
152 https://netsensai.pl/store
153 </a>
154 </p>
155 </div>
156
157 <h2><?php echo esc_html__( 'Security Options', 'netsensai-shield' ); ?></h2>
158
159 <!-- Logos display -->
160 <div style="display: flex; align-items: center; gap: 20px; margin-bottom: 10px;">
161 <a href="https://www.netsensai.pl/store/" target="_blank" style="display: flex; align-items: center; gap: 20px;">
162 <img src="<?php echo esc_url( plugin_dir_url( __FILE__ ) . 'assets/ns_logo.png' ); ?>"
163 alt="<?php echo esc_attr__( 'Netsensai-Shield Logo', 'netsensai-shield' ); ?>"
164 style="width: 200px; height: auto; margin-bottom: -20px;">
165 <img src="<?php echo esc_url( plugin_dir_url( __FILE__ ) . 'assets/netsensai.pl_logo.png' ); ?>"
166 alt="<?php echo esc_attr__( 'Netsensai-Logo', 'netsensai-shield' ); ?>"
167 style="width: 220px; height: auto; margin-bottom: -20px;">
168 </a>
169 </div>
170
171 <!-- Konfiguracja modala -->
172 <script type="text/javascript">
173 window.nsShieldModalConfig = {
174 modalShownFor: '',
175 enableContentHTML: <?php echo wp_json_encode( $enable_html ); ?>,
176 disableContentHTML: <?php echo wp_json_encode( $disable_html ); ?>
177 };
178 </script>
179
180 <form method="post" action="options.php">
181 <?php
182 settings_fields( 'ns_shield_options_group' );
183 do_settings_sections( 'secure-options' );
184 ?>
185 <div class="ns-shield-settings-actions">
186 <?php
187 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Core function returns complete escaped button markup.
188 echo get_submit_button( __( 'Save Changes', 'netsensai-shield' ), 'primary', 'submit', false );
189 if ( function_exists( 'ns_shield_access_details_current_guide_button' ) ) {
190 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Helper returns fixed, internally escaped button markup.
191 echo ns_shield_access_details_current_guide_button();
192 }
193 ?>
194 </div>
195 <?php
196 ?>
197 </form>
198
199 <!-- Include modal popup file from includes/ -->
200 <?php
201 $modal_file = plugin_dir_path( __FILE__ ) . 'includes/modal_popup.php';
202 if ( file_exists( $modal_file ) ) {
203 include $modal_file;
204 }
205 // Jeśli plik nie zostanie znaleziony, nie rób nic – error_log() zostało usunięte.
206 ?>
207 </div> <!-- End of wrap -->
208 <?php
209 }
210
211 /**
212 * Add settings menu in the admin.
213 */
214 function ns_shield_secure_options_menu() {
215 add_options_page(
216 esc_html__( 'Security Options', 'netsensai-shield' ),
217 esc_html__( 'Security Options', 'netsensai-shield' ),
218 'manage_options',
219 'secure-options',
220 'ns_shield_secure_options_page'
221 );
222 }
223 add_action( 'admin_menu', 'ns_shield_secure_options_menu' );
224
225 /**
226 * Register settings and fields.
227 */
228 function ns_shield_secure_options_settings() {
229 register_setting( 'ns_shield_options_group', 'ns_shield_login_url', 'sanitize_text_field' );
230 register_setting( 'ns_shield_options_group', 'ns_shield_login_url_enabled', 'sanitize_text_field' );
231 register_setting( 'ns_shield_options_group', 'ns_shield_wp_api_json', 'sanitize_text_field' );
232 register_setting(
233 'ns_shield_options_group',
234 'ns_shield_username_protection',
235 array(
236 'type' => 'boolean',
237 'sanitize_callback' => 'ns_shield_sanitize_username_protection',
238 'default' => false,
239 )
240 );
241 register_setting( 'ns_shield_options_group', 'ns_shield_xml_rpc', 'sanitize_text_field' );
242 register_setting( 'ns_shield_options_group', 'ns_shield_file_editor', 'sanitize_text_field' );
243 register_setting( 'ns_shield_options_group', 'ns_shield_app_passwords', 'absint' );
244 register_setting( 'ns_shield_options_group', 'ns_shield_security_headers', 'sanitize_text_field' );
245 register_setting( 'ns_shield_options_group', 'ns_shield_directory_indexing', 'sanitize_text_field' );
246 register_setting( 'ns_shield_options_group', 'ns_shield_default_admin', 'absint' );
247 register_setting( 'ns_shield_options_group', 'ns_shield_new_admin_login', 'sanitize_text_field' );
248 register_setting( 'ns_shield_options_group', 'ns_shield_debug', 'sanitize_text_field' );
249 // Register new settings for CSP headers.
250 register_setting( 'ns_shield_options_group', 'ns_shield_csp_header_light', 'intval' );
251 register_setting( 'ns_shield_options_group', 'ns_shield_csp_header_hard', 'ns_shield_sanitize_csp_hard' );
252
253 // Level 1: Basic Security (login URL options etc.)
254 add_settings_section( 'ns_shield_level_1', esc_html__( 'Level 1: Basic Security', 'netsensai-shield' ), null, 'secure-options' );
255 add_settings_field( 'ns_shield_login_url', esc_html__( 'Change Login URL', 'netsensai-shield' ), 'ns_shield_change_login_url', 'secure-options', 'ns_shield_level_1' );
256 add_settings_field( 'ns_shield_default_admin', esc_html__( 'Disable Default Admin', 'netsensai-shield' ), 'ns_shield_disable_default_admin', 'secure-options', 'ns_shield_level_1' );
257 add_settings_field( 'ns_shield_xml_rpc', esc_html__( 'Disable XML-RPC', 'netsensai-shield' ), 'ns_shield_field_xml_rpc', 'secure-options', 'ns_shield_level_1' );
258
259 // Level 2: Intermediate Security
260 add_settings_section( 'ns_shield_level_2', esc_html__( 'Level 2: Intermediate Security', 'netsensai-shield' ), null, 'secure-options' );
261 add_settings_field( 'ns_shield_wp_api_json', esc_html__( 'Disable WP API JSON', 'netsensai-shield' ), 'ns_shield_field_wp_api_json', 'secure-options', 'ns_shield_level_2' );
262 add_settings_field( 'ns_shield_file_editor', esc_html__( 'Disable File Editor', 'netsensai-shield' ), 'ns_shield_field_file_editor', 'secure-options', 'ns_shield_level_2' );
263 add_settings_field( 'ns_shield_app_passwords', esc_html__( 'Disable WordPress Application Passwords', 'netsensai-shield' ), 'ns_shield_field_app_passwords', 'secure-options', 'ns_shield_level_2' );
264
265 // Level 3: Advanced Security
266 add_settings_section( 'ns_shield_level_3', esc_html__( 'Level 3: Advanced Security', 'netsensai-shield' ), null, 'secure-options' );
267 add_settings_field( 'ns_shield_directory_indexing', esc_html__( 'Disable Directory Indexing', 'netsensai-shield' ), 'ns_shield_field_directory_indexing', 'secure-options', 'ns_shield_level_3' );
268 add_settings_field( 'ns_shield_username_protection', esc_html__( 'Protect Usernames', 'netsensai-shield' ), 'ns_shield_field_username_protection', 'secure-options', 'ns_shield_level_3' );
269 add_settings_field( 'ns_shield_security_headers', esc_html__( 'Apply Security Headers', 'netsensai-shield' ), 'ns_shield_field_security_headers', 'secure-options', 'ns_shield_level_3' );
270 }
271 add_action( 'admin_init', 'ns_shield_secure_options_settings' );
272
273 /* =====================
274 FIELD CALLBACKS
275 ===================== */
276
277 function ns_shield_field_xml_rpc() {
278 $checked = get_option( 'ns_shield_xml_rpc', 0 ) ? 'checked' : '';
279 echo '<label class="switch">';
280 echo '<input type="checkbox" name="ns_shield_xml_rpc" value="1" ' . esc_attr( $checked ) . '>';
281 echo '<span class="slider round"></span>';
282 echo '<div class="tooltip">' .
283 esc_html__(
284 'Disabling XML-RPC blocks unauthorized remote access attempts, which can enhance security. If XML-RPC remains enabled, hackers might attempt to perform DDoS attacks by sending multiple requests or brute-force password attacks to gain control over your site.',
285 'netsensai-shield'
286 ) .
287 '</div>';
288 echo '</label>';
289 }
290
291 function ns_shield_field_wp_api_json() {
292 $checked = get_option( 'ns_shield_wp_api_json', 0 ) ? 'checked' : '';
293 echo '<label class="switch">';
294 echo '<input type="checkbox" name="ns_shield_wp_api_json" value="1" ' . esc_attr( $checked ) . '>';
295 echo '<span class="slider round"></span>';
296 echo '<div class="tooltip">' .
297 esc_html__(
298 'Disabling WP API JSON can protect your site from unauthorized access to sensitive data through the API. If left enabled, hackers may exploit WP API JSON to gather information about your site’s structure or perform enumeration attacks on users, which can lead to brute-force attacks.',
299 'netsensai-shield'
300 ) .
301 '</div>';
302 echo '</label>';
303 }
304
305 function ns_shield_field_file_editor() {
306 $checked = get_option( 'ns_shield_file_editor', 0 ) ? 'checked' : '';
307 echo '<label class="switch">';
308 echo '<input type="checkbox" name="ns_shield_file_editor" value="1" ' . esc_attr( $checked ) . '>';
309 echo '<span class="slider round"></span>';
310 echo '<div class="tooltip">' .
311 esc_html__(
312 'Disabling the file editor in the WP dashboard prevents unauthorized or accidental code changes. If left enabled, attackers who gain access to your admin panel could inject malicious code into your theme or plugin files, leading to a defacement of the site or the deployment of malware.',
313 'netsensai-shield'
314 ) .
315 '</div>';
316 echo '</label>';
317 }
318
319 function ns_shield_field_app_passwords() {
320 $checked = get_option( 'ns_shield_app_passwords', 0 ) ? 'checked' : '';
321 echo '<label class="switch">';
322 echo '<input type="checkbox" name="ns_shield_app_passwords" value="1" ' . esc_attr( $checked ) . '>';
323 echo '<span class="slider round"></span>';
324 echo '<div class="tooltip">' .
325 esc_html__(
326 'Disabling application passwords secures against creating unauthorized accesses to your site’s API. If left enabled, hackers may exploit application passwords to gain persistent access to your site, enabling them to execute unauthorized API requests or even escalate their privileges.',
327 'netsensai-shield'
328 ) .
329 '</div>';
330 echo '</label>';
331 }
332
333 function ns_shield_field_security_headers() {
334 $checked = get_option( 'ns_shield_security_headers', 0 ) ? 'checked' : '';
335 echo '<label class="switch">';
336 echo '<input type="checkbox" name="ns_shield_security_headers" id="ns-shield-preload-checkbox" value="1" ' . esc_attr( $checked ) . '>';
337 echo '<span class="slider round"></span>';
338 echo '<div class="tooltip">' .
339 esc_html__(
340 'Applying security headers can protect your site from XSS attacks and other threats. On HTTPS pages, this also enables basic HSTS for the current domain only. Without these headers, your site could be vulnerable to cross-site scripting (XSS) attacks or clickjacking, allowing attackers to steal sensitive data or trick users into executing malicious actions.',
341 'netsensai-shield'
342 ) .
343 '</div>';
344 echo '</label>';
345 }
346
347 /**
348 * FIELD CALLBACK: Directory Indexing.
349 */
350 function ns_shield_field_directory_indexing() {
351 $checked = get_option( 'ns_shield_directory_indexing', 0 ) ? 'checked' : '';
352 echo '<label class="switch">';
353 echo '<input type="checkbox" name="ns_shield_directory_indexing" value="1" ' . esc_attr( $checked ) . '>';
354 echo '<span class="slider round"></span>';
355 echo '<div class="tooltip">' .
356 esc_html__(
357 'Directory indexing allows attackers to list and access files in directories that lack an index file, exposing sensitive files and configurations. If directory indexing is not disabled, attackers can execute Directory Traversal attacks, gaining access to configuration files, logs, or even databases.',
358 'netsensai-shield'
359 ) .
360 '</div>';
361 echo '</label>';
362 }
363
364 /**
365 * FIELD CALLBACK: Username protection.
366 *
367 * @return void
368 */
369 function ns_shield_field_username_protection() {
370 $checked = ns_shield_username_protection_is_enabled() ? 'checked' : '';
371
372 echo '<label class="switch">';
373 echo '<input type="checkbox" name="ns_shield_username_protection" value="1" ' . esc_attr( $checked ) . '>';
374 echo '<span class="slider round"></span>';
375 echo '<div class="tooltip">' . esc_html__( 'Makes it harder for unauthenticated visitors to discover account usernames and IDs through author archives and the REST API. It does not hide author names intentionally published on the website.', 'netsensai-shield' ) . '</div>';
376 echo '</label>';
377 }
378
379 /**
380 * Flush rewrite rules on plugin activation.
381 */
382 function ns_shield_activation_flush() {
383 ns_shield_migrate_legacy_hsts();
384
385 if ( function_exists( 'ns_shield_add_rewrite_rule' ) ) {
386 ns_shield_add_rewrite_rule();
387 }
388 flush_rewrite_rules();
389 }
390 register_activation_hook( __FILE__, 'ns_shield_activation_flush' );
391
392 /**
393 * Flush rewrite rules on plugin deactivation.
394 */
395 function ns_shield_deactivation_flush() {
396 flush_rewrite_rules();
397 }
398 register_deactivation_hook( __FILE__, 'ns_shield_deactivation_flush' );
399
400
401 // Wskazanie uruchomienia pliku script.js
402 function ns_shield_admin_enqueue_assets( $hook_suffix ) {
403 // tylko na /wp-admin/options-general.php?page=secure-options
404 if ( 'settings_page_secure-options' !== $hook_suffix ) {
405 return;
406 }
407
408 // a) Styl
409 wp_enqueue_style(
410 'ns_shield-style',
411 plugin_dir_url( __FILE__ ) . 'assets/style.css',
412 [],
413 filemtime( plugin_dir_path( __FILE__ ) . 'assets/style.css' )
414 );
415
416 // b) Skrypt z filemtime jako wersj�
417
418 $script_path = plugin_dir_path( __FILE__ ) . 'assets/script.js';
419 $ver = file_exists( $script_path ) ? filemtime( $script_path ) : false;
420 wp_enqueue_script(
421 'ns_shield-script',
422 plugin_dir_url( __FILE__ ) . 'assets/script.js',
423 ['jquery'],
424 $ver,
425 true
426 );
427
428 // c) Przekazanie ustawień do JS
429 wp_localize_script( 'ns_shield-script', 'nsShieldSettings', [
430 'pageSlug' => 'secure-options',
431 ] );
432 }
433 add_action( 'admin_enqueue_scripts', 'ns_shield_admin_enqueue_assets' );
434
435
436 ?>
437