PluginProbe
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… / 2.11.8
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… v2.11.8
3.0.0 2.11.12 2.11.11 2.11.10 2.11.9 2.11.7 2.11.8 2.11.6 2.11.5 2.11.4 2.11.3 2.11.1 2.11.2 2.11.0 2.10.5 2.10.4 2.10.3 2.10.2 2.10.1 2.10.0 2.9.9 2.9.8 2.9.6 2.9.7 2.9.5 All 88 releases
← All changes | includes/class-comment-security.php +95 -0 2.9.62.11.8 View file →
@@ -48,8 +48,31 @@
48 48 /**
49 49 * Initialize hooks
50 50 */
51 51 private function init_hooks() {
52 + // XML-RPC exposure: one three-way setting. Lives here rather than under
53 + // Login because this class already owns the xmlrpc_methods filter, and
54 + // because the module gate for this tab is modules.wp_hardening: keeping
55 + // the setting and the code it drives under the same gate avoids the trap
56 + // of a switch that looks on while the class that reads it never runs.
57 + $xmlrpc_mode = self::resolve_xmlrpc_mode( $this->settings );
58 +
59 + if ( 'full' === $xmlrpc_mode ) {
60 + // Answer the XML-RPC endpoint here, before core gets as far as
61 + // building the server object. Until 2.9.9 this pointed the
62 + // wp_xmlrpc_server_class filter at a class that does not exist,
63 + // which is not a block but an uncaught Error: xmlrpc.php answered
64 + // 500 with an empty body and wrote a PHP fatal to the log on every
65 + // hit, and that endpoint is one of the most hammered by bots.
66 + $this->block_xmlrpc_request();
67 +
68 + add_filter( 'xmlrpc_enabled', '__return_false' );
69 + remove_action( 'wp_head', 'rsd_link' );
70 + remove_action( 'wp_head', 'wlwmanifest_link' );
71 + } elseif ( 'pingback' === $xmlrpc_mode ) {
72 + add_filter( 'xmlrpc_methods', array( $this, 'disable_pingback_methods' ) );
73 + }
74 +
52 75 // Disable pingbacks/trackbacks
53 76 if ( ! empty( $this->options['disable_pingbacks'] ) ) {
54 77 add_filter( 'xmlrpc_methods', array( $this, 'disable_pingback_methods' ) );
55 78 add_filter( 'wp_headers', array( $this, 'remove_pingback_header' ) );
@@ -92,8 +115,80 @@
92 115 *
93 116 * @param array $methods XML-RPC methods.
94 117 * @return array
95 118 */
119 + /**
120 + * Resolve the XML-RPC mode.
121 + *
122 + * The setting used to be two independent checkboxes under Login that could
123 + * be on at the same time and contradict each other ("disable everything"
124 + * plus "disable only pingback"). It is now a single three-way choice stored
125 + * in wp_hardening.xmlrpc_mode, next to the pingback settings it relates to.
126 + *
127 + * Sites upgrading have neither, only the old pair under login_security, so
128 + * their choice is read from there and nothing changes for them until they
129 + * save the tab. An install with none of the three gets 'full', which is what
130 + * the old defaults did (disable_xmlrpc shipped on and took precedence).
131 + *
132 + * Public and static so this class, the settings screen and the Security
133 + * Check all resolve the value the same way and cannot drift apart.
134 + *
135 + * @param Vigilante_Settings $settings Settings instance.
136 + * @return string 'full', 'pingback' or 'none'.
137 + */
138 + public static function resolve_xmlrpc_mode( $settings ) {
139 + $hardening = $settings->get_section( 'wp_hardening' );
140 + $mode = isset( $hardening['xmlrpc_mode'] ) ? (string) $hardening['xmlrpc_mode'] : '';
141 +
142 + if ( in_array( $mode, array( 'full', 'pingback', 'none' ), true ) ) {
143 + return $mode;
144 + }
145 +
146 + // Legacy pair under Login, only meaningful when one of them was stored.
147 + $login = $settings->get_section( 'login_security' );
148 + if ( array_key_exists( 'disable_xmlrpc', $login )
149 + || array_key_exists( 'disable_xmlrpc_pingback', $login ) ) {
150 + if ( ! empty( $login['disable_xmlrpc'] ) ) {
151 + return 'full';
152 + }
153 + if ( ! empty( $login['disable_xmlrpc_pingback'] ) ) {
154 + return 'pingback';
155 + }
156 + return 'none';
157 + }
158 +
159 + return 'full';
160 + }
161 +
162 + /**
163 + * Answer an XML-RPC request with a plain 403 and stop
164 + *
165 + * Does nothing outside an XML-RPC request, so it is safe to call while the
166 + * module is wiring its hooks. XMLRPC_REQUEST is defined at the top of
167 + * xmlrpc.php, before wp-load.php, so it is already there by the time
168 + * plugins load.
169 + *
170 + * The modules are built on init priority 1, which is inside init, so the
171 + * translation functions are safe to use here.
172 + *
173 + * @since 2.9.9
174 + */
175 + private function block_xmlrpc_request() {
176 + if ( ! defined( 'XMLRPC_REQUEST' ) || ! XMLRPC_REQUEST ) {
177 + return;
178 + }
179 +
180 + if ( ! headers_sent() ) {
181 + status_header( 403 );
182 + nocache_headers();
183 + $charset = sanitize_text_field( (string) get_option( 'blog_charset', 'UTF-8' ) );
184 + header( 'Content-Type: text/plain; charset=' . ( '' !== $charset ? $charset : 'UTF-8' ) );
185 + }
186 +
187 + echo esc_html__( 'XML-RPC services are disabled on this site.', 'vigilante' );
188 + exit;
189 + }
190 +
96 191 public function disable_pingback_methods( $methods ) {
97 192 unset( $methods['pingback.ping'] );
98 193 unset( $methods['pingback.extensions.getPingbacks'] );
99 194 return $methods;