All_in_One_SEO_Pack_Bad_Robots

Class All_in_One_SEO_Pack_Bad_Robots.

Defined (1)

The class is defined in the following location(s).

/modules/aioseop_bad_robots.php  
  1. class All_in_One_SEO_Pack_Bad_Robots extends All_in_One_SEO_Pack_Module { 
  2.  
  3. /** 
  4. * All_in_One_SEO_Pack_Bad_Robots constructor. 
  5. */ 
  6. function __construct() { 
  7. $this->name = __( 'Bad Bot Blocker', 'all-in-one-seo-pack' ); // Human-readable name of the plugin. 
  8. $this->prefix = 'aiosp_bad_robots_'; // Option prefix. 
  9. $this->file = __FILE__; // The current file. 
  10. parent::__construct(); 
  11.  
  12. $help_text = array( 
  13. 'block_bots' => __( 'Block requests from user agents that are known to misbehave with 503.', 'all-in-one-seo-pack' ),  
  14. 'block_refer' => __( 'Block Referral Spam using HTTP.', 'all-in-one-seo-pack' ),  
  15. 'track_blocks' => __( 'Log and show recent requests from blocked bots.', 'all-in-one-seo-pack' ),  
  16. 'htaccess_rules' => __( 'Block bad robots via Apache .htaccess rules. Warning: this will change your web server configuration, make sure you are able to edit this file manually as well.', 'all-in-one-seo-pack' ),  
  17. 'edit_blocks' => __( 'Check this to edit the list of disallowed user agents for blocking bad bots.', 'all-in-one-seo-pack' ),  
  18. 'blocklist' => __( 'This is the list of disallowed user agents used for blocking bad bots.', 'all-in-one-seo-pack' ),  
  19. 'referlist' => __( 'This is the list of disallowed referers used for blocking bad bots.', 'all-in-one-seo-pack' ),  
  20. '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' ),  
  21. ); 
  22.  
  23. $this->default_options = array( 
  24. 'block_bots' => array( 'name' => __( 'Block Bad Bots using HTTP', 'all-in-one-seo-pack' ) ),  
  25. 'block_refer' => array( 'name' => __( 'Block Referral Spam using HTTP', 'all-in-one-seo-pack' ) ),  
  26. 'track_blocks' => array( 'name' => __( 'Track Blocked Bots', 'all-in-one-seo-pack' ) ),  
  27. 'htaccess_rules' => array( 'name' => __( 'Block Bad Bots using .htaccess', 'all-in-one-seo-pack' ) ),  
  28. 'edit_blocks' => array( 'name' => __( 'Use Custom Blocklists', 'all-in-one-seo-pack' ) ),  
  29. 'blocklist' => array( 
  30. 'name' => __( 'User Agent Blocklist', 'all-in-one-seo-pack' ),  
  31. 'type' => 'textarea',  
  32. 'rows' => 5,  
  33. 'cols' => 120,  
  34. 'condshow' => array( "{$this->prefix}edit_blocks" => 'on' ),  
  35. 'default' => join( "\n", $this->default_bad_bots() ),  
  36. ),  
  37. 'referlist' => array( 
  38. 'name' => __( 'Referer Blocklist', 'all-in-one-seo-pack' ),  
  39. 'type' => 'textarea',  
  40. 'rows' => 5,  
  41. 'cols' => 120,  
  42. 'condshow' => array( 
  43. "{$this->prefix}edit_blocks" => 'on',  
  44. "{$this->prefix}block_refer" => 'on',  
  45. ),  
  46. 'default' => join( "\n", $this->default_bad_referers() ),  
  47. ),  
  48. 'blocked_log' => array( 
  49. 'name' => __( 'Log Of Blocked Bots', 'all-in-one-seo-pack' ),  
  50. 'default' => __( 'No requests yet.', 'all-in-one-seo-pack' ),  
  51. 'type' => 'esc_html',  
  52. 'disabled' => 'disabled',  
  53. 'save' => false,  
  54. 'label' => 'top',  
  55. 'rows' => 5,  
  56. 'cols' => 120,  
  57. 'style' => 'min-width:950px',  
  58. 'condshow' => array( "{$this->prefix}track_blocks" => 'on' ),  
  59. ),  
  60. ); 
  61. $is_apache = false; 
  62. if ( ! empty( $_SERVER['SERVER_SOFTWARE'] ) && stristr( $_SERVER['SERVER_SOFTWARE'], 'Apache' ) !== false ) { 
  63. $is_apache = true; 
  64. add_action( $this->prefix . 'settings_update', array( $this, 'generate_htaccess_blocklist' ), 10 ); 
  65. } else { 
  66. unset( $this->default_options['htaccess_rules'] ); 
  67. unset( $help_text['htaccess_rules'] ); 
  68.  
  69. if ( ! empty( $help_text ) ) { 
  70. foreach ( $help_text as $k => $v ) { 
  71. $this->default_options[ $k ]['help_text'] = $v; 
  72.  
  73. add_filter( $this->prefix . 'display_options', array( $this, 'filter_display_options' ) ); 
  74.  
  75. // Load initial options / set defaults,  
  76. $this->update_options(); 
  77.  
  78. if ( $this->option_isset( 'edit_blocks' ) ) { 
  79. add_filter( $this->prefix . 'badbotlist', array( $this, 'filter_bad_botlist' ) ); 
  80. if ( $this->option_isset( 'block_refer' ) ) { 
  81. add_filter( $this->prefix . 'badreferlist', array( $this, 'filter_bad_referlist' ) ); 
  82.  
  83. if ( $this->option_isset( 'block_bots' ) ) { 
  84. if ( ! $this->allow_bot() ) { 
  85. status_header( 503 ); 
  86. $ip = $this->validate_ip( $_SERVER['REMOTE_ADDR'] ); 
  87. $user_agent = $_SERVER['HTTP_USER_AGENT']; 
  88. $this->blocked_message( sprintf( __( 'Blocked bot with IP %s -- matched user agent %s found in blocklist.', 'all-in-one-seo-pack' ), $ip, $user_agent ) ); 
  89. exit(); 
  90. } elseif ( $this->option_isset( 'block_refer' ) && $this->is_bad_referer() ) { 
  91. status_header( 503 ); 
  92. $ip = $this->validate_ip( $_SERVER['REMOTE_ADDR'] ); 
  93. $referer = $_SERVER['HTTP_REFERER']; 
  94. $this->blocked_message( sprintf( __( 'Blocked bot with IP %s -- matched referer %s found in blocklist.', 'all-in-one-seo-pack' ), $ip, $referer ) ); 
  95.  
  96. /** 
  97. * Validate IP. 
  98. * @param $ip 
  99. * @since 2.3.7 
  100. * @return string 
  101. */ 
  102. function validate_ip( $ip ) { 
  103.  
  104. if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) { 
  105. // Valid IPV4. 
  106. return $ip; 
  107.  
  108. if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) { 
  109. // Valid IPV6. 
  110. return $ip; 
  111.  
  112. // Doesn't seem to be a valid IP. 
  113. return 'invalid IP submitted'; 
  114.  
  115.  
  116. function generate_htaccess_blocklist() { 
  117. if ( ! $this->option_isset( 'htaccess_rules' ) ) { 
  118.  
  119. if ( insert_with_markers( get_home_path() . '.htaccess', $this->name, '' ) ) { 
  120. aioseop_output_notice( __( 'Updated .htaccess rules.', 'all-in-one-seo-pack' ) ); 
  121. } else { 
  122. aioseop_output_notice( __( 'Failed to update .htaccess rules!', 'all-in-one-seo-pack' ), '', 'error' ); 
  123.  
  124. return; 
  125.  
  126.  
  127. if ( function_exists( 'apache_get_modules' ) ) { 
  128. $modules = apache_get_modules(); 
  129. foreach ( array( 'mod_authz_host', 'mod_setenvif' ) as $m ) { 
  130. if ( ! in_array( $m, $modules ) ) { 
  131. aioseop_output_notice( sprintf( __( 'Apache module %s is required!', 'all-in-one-seo-pack' ), $m ), '', 'error' ); 
  132. $botlist = $this->default_bad_bots(); 
  133. $botlist = apply_filters( $this->prefix . 'badbotlist', $botlist ); 
  134. if ( ! empty( $botlist ) ) { 
  135. $regex = $this->quote_list_for_regex( $botlist, '"' ); 
  136. $htaccess = array(); 
  137. $htaccess[] = 'SetEnvIfNoCase User-Agent "' . $regex . '" bad_bot'; 
  138. if ( $this->option_isset( 'edit_blocks' ) && $this->option_isset( 'block_refer' ) && $this->option_isset( 'referlist' ) ) { 
  139. $referlist = $this->default_bad_referers(); 
  140. $referlist = apply_filters( $this->prefix . 'badreferlist', $botlist ); 
  141. if ( ! empty( $referlist ) ) { 
  142. $regex = $this->quote_list_for_regex( $referlist, '"' ); 
  143. $htaccess[] = 'SetEnvIfNoCase Referer "' . $regex . '" bad_bot'; 
  144. $htaccess[] = 'Deny from env=bad_bot'; 
  145. if ( insert_with_markers( get_home_path() . '.htaccess', $this->name, $htaccess ) ) { 
  146. aioseop_output_notice( __( 'Updated .htaccess rules.', 'all-in-one-seo-pack' ) ); 
  147. } else { 
  148. aioseop_output_notice( __( 'Failed to update .htaccess rules!', 'all-in-one-seo-pack' ), '', 'error' ); 
  149. } else { 
  150. aioseop_output_notice( __( 'No rules to update!', 'all-in-one-seo-pack' ), '', 'error' ); 
  151.  
  152. /** 
  153. * @param $referlist 
  154. * @return array 
  155. */ 
  156. function filter_bad_referlist( $referlist ) { 
  157. if ( $this->option_isset( 'edit_blocks' ) && $this->option_isset( 'block_refer' ) && $this->option_isset( 'referlist' ) ) { 
  158. $referlist = explode( "\n", $this->options["{$this->prefix}referlist"] ); 
  159.  
  160. return $referlist; 
  161.  
  162. /** 
  163. * @param $botlist 
  164. * @return array 
  165. */ 
  166. function filter_bad_botlist( $botlist ) { 
  167. if ( $this->option_isset( 'edit_blocks' ) && $this->option_isset( 'blocklist' ) ) { 
  168. $botlist = explode( "\n", $this->options["{$this->prefix}blocklist"] ); 
  169.  
  170. return $botlist; 
  171.  
  172.  
  173. /** 
  174. * Updates blocked message. 
  175. * @param string $msg 
  176. */ 
  177. function blocked_message( $msg ) { 
  178. if ( empty( $this->options["{$this->prefix}blocked_log"] ) ) { 
  179. $this->options["{$this->prefix}blocked_log"] = ''; 
  180. $this->options["{$this->prefix}blocked_log"] = date( 'Y-m-d H:i:s' ) . " {$msg}\n" . $this->options["{$this->prefix}blocked_log"]; 
  181. if ( $this->strlen( $this->options["{$this->prefix}blocked_log"] ) > 4096 ) { 
  182. $end = $this->strrpos( $this->options["{$this->prefix}blocked_log"], "\n" ); 
  183. if ( false === $end ) { 
  184. $end = 4096; 
  185. $this->options["{$this->prefix}blocked_log"] = $this->substr( $this->options["{$this->prefix}blocked_log"], 0, $end ); 
  186. $this->update_class_option( $this->options ); 
  187.  
  188.  
  189. /** 
  190. * Filter display options. 
  191. * Add in options for status display on settings page, sitemap rewriting on multisite. 
  192. * @param $options 
  193. * @return mixed 
  194. */ 
  195. function filter_display_options( $options ) { 
  196.  
  197. if ( $this->option_isset( 'blocked_log' ) ) { 
  198. if ( preg_match( '/\<(\?php|script)/', $options["{$this->prefix}blocked_log"] ) ) { 
  199. $options["{$this->prefix}blocked_log"] = "Probable XSS attempt detected!\n" . $options["{$this->prefix}blocked_log"]; 
  200.  
  201. return $options;