SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/RateLimit.php

  1. <?php
  2. /*
  3.  * Copyright (C) 2017 Karmabunny Pty Ltd.
  4.  *
  5.  * This file is a part of SproutCMS.
  6.  *
  7.  * SproutCMS is free software: you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation, either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * For more information, visit <http://getsproutcms.com>.
  12.  */
  13.  
  14. namespace Sprout\Helpers;
  15.  
  16.  
  17. /**
  18.  * Rate-limiting system, to prefent people form doing hacky stuff
  19.  */
  20. class RateLimit
  21. {
  22.  
  23. /**
  24.   * Log a successful hit against the rate-limit system (e.g. user was logged in)
  25.   *
  26.   * @param string $event The event being done by the user, e.g. 'user-auth-action' or 'form-submit-action'
  27.   * @param string $user The user who performed the event, may be an empty string for unauthenticated events
  28.   */
  29. public static function logHitSuccess($event, $username = '')
  30. {
  31. self::logHit($event, true, $username);
  32. }
  33.  
  34.  
  35. /**
  36.   * Log a failure hit against the rate-limit system (e.g. password incorrect)
  37.   *
  38.   * @param string $event The event being done by the user, e.g. 'user-auth-action' or 'form-submit-action'
  39.   * @param string $user The user who performed the event, may be an empty string for unauthenticated events
  40.   */
  41. public static function logHitFailure($event, $username = '')
  42. {
  43. self::logHit($event, false, $username);
  44. }
  45.  
  46.  
  47. /**
  48.   * Log a hit against the rate-limit system
  49.   *
  50.   * @param string $event The event being done by the user, e.g. 'user-auth-action' or 'form-submit-action'
  51.   * @param bool $success The status of the event, true for success, false for failure
  52.   * @param string $user The user who performed the event
  53.   */
  54. protected static function logHit($event, $success, $username)
  55. {
  56. $data = array();
  57. $data['date_added'] = Pdb::now();
  58. $data['event'] = trim($event);
  59. $data['success'] = ($success ? '1' : '0');
  60. $data['username'] = trim($username);
  61. $data['ip_address'] = Request::userIp();
  62. Pdb::insert('rate_limit_hits', $data);
  63. }
  64.  
  65.  
  66. /**
  67.   * Return the number of hits matching given conditions for a given time period
  68.   *
  69.   * @param array $conditions Conditions to match, e.g. 'event', 'success', 'username', 'ip_address' fields
  70.   * @param int $time Time to check in seconds, e.g. 3600 for one hour
  71.   * @return int Number of hits
  72.   */
  73. public static function getHitCount(array $conditions, $time)
  74. {
  75. $time = (int) $time;
  76.  
  77. $params = [$time];
  78. $where = Pdb::buildClause($conditions, $params);
  79.  
  80. $q = "SELECT COUNT(id) AS C
  81. FROM ~rate_limit_hits
  82. WHERE date_added > DATE_SUB(NOW(), INTERVAL ? SECOND)
  83. AND {$where}";
  84. return Pdb::q($q, $params, 'val');
  85. }
  86.  
  87.  
  88. /**
  89.   * Check that a given event has only been hit by the request ip address an allowable number of times
  90.   *
  91.   * @param string $event Event to check
  92.   * @param bool|null $success TRUE to check only successes, FALSE only failures, NULL all events
  93.   * @param int $limit Maximum number of events
  94.   * @param int $time Time to search, in seconds
  95.   * @return bool TRUE if under the limit, FALSE if over the limit
  96.   */
  97. public static function checkLimitIP($event, $success, $limit, $time)
  98. {
  99. $ip_address = Request::userIp();
  100. $conditions = ['event' => $event, 'ip_address' => $ip_address];
  101. if ($success !== null) $conditions['success'] = ($success ? '1' : '0');
  102. $count = self::getHitCount($conditions, $time);
  103. return ($count <= $limit);
  104. }
  105.  
  106. }
  107.