name = __( 'Bad Bot Blocker', 'all-in-one-seo-pack' ); // Human-readable name of the plugin. $this->prefix = 'aiosp_bad_robots_'; // Option prefix. $this->file = __FILE__; // The current file. parent::__construct(); $help_text = array( 'block_bots' => __( 'Block requests from user agents that are known to misbehave with 503.', 'all-in-one-seo-pack' ), 'block_refer' => __( 'Block Referral Spam using HTTP.', 'all-in-one-seo-pack' ), 'track_blocks' => __( 'Log and show recent requests from blocked bots.', 'all-in-one-seo-pack' ), 'edit_blocks' => __( 'Check this to edit the list of disallowed user agents for blocking bad bots.', 'all-in-one-seo-pack' ), 'blocklist' => __( 'This is the list of disallowed user agents used for blocking bad bots.', 'all-in-one-seo-pack' ), 'referlist' => __( 'This is the list of disallowed referers used for blocking bad bots.', 'all-in-one-seo-pack' ), 'blocked_log' => __( 'Shows log of most recent requests from blocked bots. Note: this will not track any bots that were already blocked at the web server / .htaccess level.', 'all-in-one-seo-pack' ), ); $this->default_options = array( 'block_bots' => array( 'name' => __( 'Block Bad Bots using HTTP', 'all-in-one-seo-pack' ) ), 'block_refer' => array( 'name' => __( 'Block Referral Spam using HTTP', 'all-in-one-seo-pack' ) ), 'track_blocks' => array( 'name' => __( 'Track Blocked Bots', 'all-in-one-seo-pack' ) ), 'edit_blocks' => array( 'name' => __( 'Use Custom Blocklists', 'all-in-one-seo-pack' ) ), 'blocklist' => array( 'name' => __( 'User Agent Blocklist', 'all-in-one-seo-pack' ), 'type' => 'textarea', 'rows' => 5, 'cols' => 120, 'condshow' => array( "{$this->prefix}edit_blocks" => 'on' ), 'default' => join( "\n", $this->default_bad_bots() ), ), 'referlist' => array( 'name' => __( 'Referer Blocklist', 'all-in-one-seo-pack' ), 'type' => 'textarea', 'rows' => 5, 'cols' => 120, 'condshow' => array( "{$this->prefix}edit_blocks" => 'on', "{$this->prefix}block_refer" => 'on', ), 'default' => join( "\n", $this->default_bad_referers() ), ), 'blocked_log' => array( 'name' => __( 'Log Of Blocked Bots', 'all-in-one-seo-pack' ), 'default' => __( 'No requests yet.', 'all-in-one-seo-pack' ), 'type' => 'esc_html', 'disabled' => 'disabled', 'save' => false, 'label' => 'top', 'rows' => 5, 'cols' => 120, 'style' => 'min-width:950px', 'condshow' => array( "{$this->prefix}track_blocks" => 'on' ), ), ); if ( ! empty( $help_text ) ) { foreach ( $help_text as $k => $v ) { $this->default_options[ $k ]['help_text'] = $v; } } add_filter( $this->prefix . 'display_options', array( $this, 'filter_display_options' ) ); // Load initial options / set defaults, $this->update_options(); if ( $this->option_isset( 'edit_blocks' ) ) { add_filter( $this->prefix . 'badbotlist', array( $this, 'filter_bad_botlist' ) ); if ( $this->option_isset( 'block_refer' ) ) { add_filter( $this->prefix . 'badreferlist', array( $this, 'filter_bad_referlist' ) ); } } if ( $this->option_isset( 'block_bots' ) ) { if ( ! $this->allow_bot() ) { status_header( 503 ); $ip = $this->validate_ip( $_SERVER['REMOTE_ADDR'] ); $user_agent = $_SERVER['HTTP_USER_AGENT']; $this->blocked_message( sprintf( __( 'Blocked bot with IP %1$s -- matched user agent %2$s found in blocklist.', 'all-in-one-seo-pack' ), $ip, $user_agent ) ); exit(); } elseif ( $this->option_isset( 'block_refer' ) && $this->is_bad_referer() ) { status_header( 503 ); $ip = $this->validate_ip( $_SERVER['REMOTE_ADDR'] ); $referer = $_SERVER['HTTP_REFERER']; $this->blocked_message( sprintf( __( 'Blocked bot with IP %1$s -- matched referer %2$s found in blocklist.', 'all-in-one-seo-pack' ), $ip, $referer ) ); } } } /** * Validate IP. * * @param $ip * * @since 2.3.7 * * @return string */ function validate_ip( $ip ) { if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) { // Valid IPV4. return $ip; } if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) { // Valid IPV6. return $ip; } // Doesn't seem to be a valid IP. return 'invalid IP submitted'; } /** * @param $referlist * * @return array */ function filter_bad_referlist( $referlist ) { if ( $this->option_isset( 'edit_blocks' ) && $this->option_isset( 'block_refer' ) && $this->option_isset( 'referlist' ) ) { $referlist = preg_split( '/\r\n|[\r\n]/', $this->options[ "{$this->prefix}referlist" ] ); } return $referlist; } /** * @param $botlist * * @return array */ function filter_bad_botlist( $botlist ) { if ( $this->option_isset( 'edit_blocks' ) && $this->option_isset( 'blocklist' ) ) { $botlist = preg_split( '/\r\n|[\r\n]/', $this->options[ "{$this->prefix}blocklist" ] ); } return $botlist; } /** * Updates blocked message. * * @param string $msg */ function blocked_message( $msg ) { if ( ! $this->option_isset( 'track_blocks' ) ) { return; // Only log if track blocks is checked. } if ( empty( $this->options[ "{$this->prefix}blocked_log" ] ) ) { $this->options[ "{$this->prefix}blocked_log" ] = ''; } $this->options[ "{$this->prefix}blocked_log" ] = date( 'Y-m-d H:i:s' ) . " {$msg}\n" . $this->options[ "{$this->prefix}blocked_log" ]; if ( $this->strlen( $this->options[ "{$this->prefix}blocked_log" ] ) > 4096 ) { $end = $this->strrpos( $this->options[ "{$this->prefix}blocked_log" ], "\n" ); if ( false === $end ) { $end = 4096; } $this->options[ "{$this->prefix}blocked_log" ] = $this->substr( $this->options[ "{$this->prefix}blocked_log" ], 0, $end ); } $this->update_class_option( $this->options ); } /** * Filter display options. * * Add in options for status display on settings page, sitemap rewriting on multisite. * * @param $options * * @return mixed */ function filter_display_options( $options ) { if ( $this->option_isset( 'blocked_log' ) ) { if ( preg_match( '/\<(\?php|script)/', $options[ "{$this->prefix}blocked_log" ] ) ) { $options[ "{$this->prefix}blocked_log" ] = "Probable XSS attempt detected!\n" . $options[ "{$this->prefix}blocked_log" ]; } } return $options; } } }