PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/better-wp-security/core/modules/ipcheck/class-itsec-ipcheck.php

https://gitlab.com/sokeara/ayana-journeys
PHP | 392 lines | 172 code | 117 blank | 103 comment | 44 complexity | 3d33e01f517f8ab2eaefa4c1e4040b18 MD5 | raw file
  1. <?php
  2. /**
  3. * iThemes IPCheck API Wrapper.
  4. *
  5. * Provides static calls to the iThemes IPCheck API
  6. *
  7. * @package iThemes_Security
  8. *
  9. * @since 4.5
  10. *
  11. */
  12. class ITSEC_IPCheck {
  13. private $endpoint = 'http://ipcheck-api.ithemes.com/?action=';
  14. private $settings;
  15. function run() {
  16. $this->settings = get_site_option( 'itsec_ipcheck' );
  17. //Execute API Brute force protection
  18. if ( isset( $this->settings['api_ban'] ) && $this->settings['api_ban'] === true ) {
  19. add_action( 'wp_login', array( $this, 'wp_login' ), 10, 2 );
  20. add_action( 'wp_login_failed', array( $this, 'wp_login_failed' ), 1, 1 );
  21. add_filter( 'authenticate', array( $this, 'authenticate' ), 10, 3 );
  22. add_filter( 'itsec_logger_modules', array( $this, 'itsec_logger_modules' ) );
  23. }
  24. }
  25. /**
  26. * Sends to lockout class when login form isn't completely filled out and process xml_rpc username
  27. *
  28. * @since 4.5
  29. *
  30. * @param object $user user or wordpress error
  31. * @param string $username username attempted
  32. * @param string $password password attempted
  33. *
  34. * @return user object or WordPress error
  35. */
  36. public function authenticate( $user, $username = '', $password = '' ) {
  37. global $itsec_logger, $itsec_lockout;
  38. //Execute brute force if username or password are empty
  39. if ( isset( $_POST['wp-submit'] ) && ( empty( $username ) || empty( $password ) ) ) {
  40. if ( $this->report_ip() === 1 ) {
  41. $itsec_logger->log_event( 'ipcheck', 10, array(), ITSEC_Lib::get_ip() );
  42. $itsec_lockout->execute_lock( false, true );
  43. }
  44. }
  45. return $user;
  46. }
  47. /**
  48. * Set transient for caching IPs
  49. *
  50. * @since 4.5
  51. *
  52. * @param string $ip IP Address
  53. * @param bool $status if the IP is blocked or not
  54. * @param int $time length, in seconds, to cache
  55. *
  56. * @return void
  57. */
  58. private function cache_ip( $ip, $status, $time ) {
  59. //@todo one size fits all is too long. Need to adjust time
  60. set_site_transient( 'itsec_ip_cache_' . esc_sql( $ip ), $status, $time );
  61. }
  62. /**
  63. * IP to check for blacklist
  64. *
  65. * @since 4.5
  66. *
  67. * @param string|null $ip ip to report
  68. *
  69. * @return bool true if successfully reported else false
  70. */
  71. public function check_ip( $ip = null ) {
  72. global $itsec_globals, $itsec_logger;
  73. //get current IP if needed
  74. if ( $ip === null ) {
  75. $ip = ITSEC_Lib::get_ip();
  76. } else {
  77. $ip = trim( sanitize_text_field( $ip ) );
  78. }
  79. if ( ITSEC_Lib::is_ip_whitelisted( $ip ) ) {
  80. return false;
  81. }
  82. //See if we've checked this IP in the last hour
  83. $cache_check = get_site_transient( 'itsec_ip_cache_' . esc_sql( $ip ) );
  84. if ( is_array( $cache_check ) && isset( $cache_check['status'] ) ) {
  85. return $cache_check['status'];
  86. }
  87. $action = 'check-ip';
  88. if ( ITSEC_Lib_IP_Tools::validate( $ip ) ) { //verify IP address is valid
  89. if ( ! isset( $this->settings['api_key'] ) || ! isset( $this->settings['api_secret'] ) ) {
  90. return false; //invalid key or secret
  91. }
  92. $args = json_encode(
  93. array(
  94. 'apikey' => $this->settings['api_key'], //the api key
  95. 'behavior' => 'brute-force-login', //type of behanvior we're reporting
  96. 'ip' => $ip, //the ip to report
  97. 'site' => home_url( '', 'http' ), //the current site URL
  98. 'timestamp' => $itsec_globals['current_time_gmt'], //current time (GMT)
  99. )
  100. );
  101. //Build the request parameters
  102. $request = array(
  103. 'body' => array(
  104. 'request' => $args,
  105. 'signature' => $this->hmac_sha1( $this->settings['api_secret'], $action . $args ),
  106. ),
  107. );
  108. $response = wp_remote_post( $this->endpoint . $action, $request );
  109. //Make sure the request was valid and has a valid body
  110. if ( ! is_wp_error( $response ) && isset( $response['body'] ) ) {
  111. $response = json_decode( $response['body'], true );
  112. if ( is_array( $response ) && isset( $response['success'] ) && $response['success'] == true ) {
  113. $cache = isset( $response['cache_ttl'] ) ? absint( $response['cache_ttl'] ) : 3600;
  114. if ( isset( $response['block'] ) && $response['block'] == true ) {
  115. $expiration = date( 'Y-m-d H:i:s', $itsec_globals['current_time'] + $cache );
  116. $expiration_gmt = date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] + $cache );
  117. $itsec_logger->log_event( __( 'lockout', 'better-wp-security' ), 10, array(
  118. 'expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => 'host'
  119. ), $ip );
  120. $this->cache_ip( $ip, array( 'status' => true ), $cache );
  121. return true; //API reports IP is blocked
  122. } else {
  123. $this->cache_ip( $ip, array( 'status' => false ), $cache );
  124. return false; //API reports IP is not blocked or no report (default to no block)
  125. }
  126. }
  127. }
  128. }
  129. return false;
  130. }
  131. /**
  132. * Calculates the HMAC of a string using SHA1.
  133. *
  134. * there is a native PHP hmac function, but we use this one for
  135. * the widest compatibility with older PHP versions
  136. *
  137. * @param string $key the shared secret key used to generate the mac
  138. * @param string $data data to be signed
  139. *
  140. *
  141. * @return string base64 encoded hmac
  142. */
  143. private function hmac_sha1( $key, $data ) {
  144. if ( strlen( $key ) > 64 ) {
  145. $key = pack( 'H*', sha1( $key ) );
  146. }
  147. $key = str_pad( $key, 64, chr( 0x00 ) );
  148. $ipad = str_repeat( chr( 0x36 ), 64 );
  149. $opad = str_repeat( chr( 0x5c ), 64 );
  150. $hmac = pack( 'H*', sha1( ( $key ^ $opad ) . pack( 'H*', sha1( ( $key ^ $ipad ) . $data ) ) ) );
  151. return base64_encode( $hmac );
  152. }
  153. /**
  154. * Register IPCheck for logger
  155. *
  156. * @since 4.5
  157. *
  158. * @param array $logger_modules array of logger modules
  159. *
  160. * @return array array of logger modules
  161. */
  162. public function itsec_logger_modules( $logger_modules ) {
  163. $logger_modules['ipcheck'] = array(
  164. 'type' => 'ipcheck',
  165. 'function' => __( 'IP Flagged as bad by iThemes IPCheck', 'better-wp-security' ),
  166. );
  167. return $logger_modules;
  168. }
  169. /**
  170. * Send offending IP to IPCheck API
  171. *
  172. * @since 4.5
  173. *
  174. * @param string|null $ip ip to report
  175. * @param int $type type of behavior to report
  176. *
  177. * @return int -1 on failure, 0 if report successful and IP not blocked, 1 if IP successful and IP blocked
  178. */
  179. public function report_ip( $ip = null, $type = 1 ) {
  180. global $itsec_globals, $itsec_logger;
  181. $action = 'report-ip';
  182. /**
  183. * Switch types or return false if no valid type
  184. *
  185. * Valid types:
  186. * 1 = invalid/failed login
  187. *
  188. */
  189. switch ( $type ) {
  190. case 1:
  191. $behavior = 'brute-force-login';
  192. break;
  193. default:
  194. return -1;
  195. }
  196. //get current IP if needed
  197. if ( $ip === null ) {
  198. $ip = ITSEC_Lib::get_ip();
  199. } else {
  200. $ip = trim( sanitize_text_field( $ip ) );
  201. }
  202. if ( ITSEC_Lib::is_ip_whitelisted( $ip ) ) {
  203. return 0;
  204. }
  205. if ( ITSEC_Lib_IP_Tools::validate( $ip ) ) { //verify IP address is valid
  206. if ( ! isset( $this->settings['api_key'] ) || ! isset( $this->settings['api_secret'] ) ) {
  207. return -1; //invalid key or secret
  208. }
  209. $args = json_encode(
  210. array(
  211. 'apikey' => $this->settings['api_key'], //the api key
  212. 'behavior' => $behavior, //type of behanvior we're reporting
  213. 'ip' => $ip, //the ip to report
  214. 'site' => home_url( '', 'http' ), //the current site URL
  215. 'timestamp' => $itsec_globals['current_time_gmt'], //current time (GMT)
  216. )
  217. );
  218. //Build the request parameters
  219. $request = array(
  220. 'body' => array(
  221. 'request' => $args,
  222. 'signature' => $this->hmac_SHA1( $this->settings['api_secret'], $action . $args ),
  223. ),
  224. );
  225. $response = wp_remote_post( $this->endpoint . $action, $request );
  226. //Make sure the request was valid and has a valid body
  227. if ( ! is_wp_error( $response ) && isset( $response['body'] ) ) {
  228. $response = json_decode( $response['body'], true );
  229. if ( is_array( $response ) && isset( $response['success'] ) && $response['success'] == true ) {
  230. if ( isset( $response['block'] ) && $response['block'] == true ) {
  231. $cache = isset( $response['cache_ttl'] ) ? absint( $response['cache_ttl'] ) : 3600;
  232. $expiration = date( 'Y-m-d H:i:s', $itsec_globals['current_time'] + $cache );
  233. $expiration_gmt = date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] + $cache );
  234. $itsec_logger->log_event( __( 'lockout', 'better-wp-security' ), 10, array(
  235. 'expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => 'host'
  236. ), $ip );
  237. $this->cache_ip( $ip, array( 'status' => true ), $cache );
  238. return 1; //ip report success. Just return true for now
  239. } else {
  240. return 0;
  241. }
  242. }
  243. }
  244. }
  245. return -1;
  246. }
  247. /**
  248. * Make sure user isn't already locked out even on successful form submission
  249. *
  250. * @since 4.5
  251. *
  252. * @return void
  253. */
  254. public function wp_login() {
  255. global $itsec_logger, $itsec_lockout;
  256. if ( $this->check_ip() === true ) {
  257. $itsec_logger->log_event( 'ipcheck', 10, array(), ITSEC_Lib::get_ip() );
  258. $itsec_lockout->execute_lock( false, true );
  259. }
  260. }
  261. /**
  262. * Sends to lockout class when username and password are filled out and wrong
  263. *
  264. * @since 4.5
  265. *
  266. * @return void
  267. */
  268. public function wp_login_failed() {
  269. global $itsec_logger, $itsec_lockout;
  270. if ( $this->report_ip() === 1 ) {
  271. $itsec_logger->log_event( 'ipcheck', 10, array(), ITSEC_Lib::get_ip() );
  272. $itsec_lockout->execute_lock( false, true );
  273. }
  274. }
  275. }