diff --git a/src/MD_STD_SEC.php b/src/MD_STD_SEC.php index c23bd5c..b39d387 100644 --- a/src/MD_STD_SEC.php +++ b/src/MD_STD_SEC.php @@ -14,9 +14,9 @@ final class MD_STD_SEC { const REFRESH_TIME_IP = 180; // Time until the comp. with the same IP is cleared. This should be lower than the user-level one, as people working together may be using a common IP. const BRUTE_FORCE_DELAY_DEFAULT = 2000; // 2000 microseconds = 2 milliseconds - const BRUTE_FORCE_DELAY_MULTIPLIER_COMMON = 1.08; - const BRUTE_FORCE_DELAY_MULTIPLIER_PER_USER = 2.8; - const BRUTE_FORCE_DELAY_MULTIPLIER_PER_IP = 2; + const BRUTE_FORCE_DELAY_MULTIPLIER_COMMON = 1.04; + const BRUTE_FORCE_DELAY_MULTIPLIER_PER_USER = 2.2; + const BRUTE_FORCE_DELAY_MULTIPLIER_PER_IP = 1.8; /** * Function for retrieving the anti-csrf token or generating it if need be. @@ -56,14 +56,51 @@ final class MD_STD_SEC { } /** - * Prevent brute force attacks by delaying the login . + * Computes the delay for preventing brute force attacks. * - * @param string $tool_name Identifier of the login. - * @param string $username Username to look for. + * @param integer $counter_common General counter. + * @param integer $counter_by_user Username-specific counter. + * @param integer $counter_by_ip IP-specific counter. + * @param integer $max Optional script-specific maximum execution time. + * + * @return integer + */ + public static function computeAntiBruteForceDelay(int $counter_common, int $counter_by_user, int $counter_by_ip, int $max = 0):int { + + $counter_common = min($counter_common, 40); + $counter_by_user = min($counter_by_user, 40); + $counter_by_ip = min($counter_by_ip, 40); + + // Calculate delay + $delay_micoseconds = \abs(\intval(self::BRUTE_FORCE_DELAY_DEFAULT * + (self::BRUTE_FORCE_DELAY_MULTIPLIER_COMMON ** $counter_common) * + (self::BRUTE_FORCE_DELAY_MULTIPLIER_PER_USER ** $counter_by_user) * + (self::BRUTE_FORCE_DELAY_MULTIPLIER_PER_IP ** $counter_by_ip))); + + $max_execution_time = \abs((int)\ini_get("max_execution_time")); + if ($max_execution_time !== 0) { + $max_execution_microseconds = $max_execution_time * 1000000; + $delay_micoseconds = min($delay_micoseconds, \abs($max_execution_microseconds - 1000000)); + } + + if ($max !== 0 && $max * 1000000 < $delay_micoseconds) { + $delay_micoseconds = max($max - 1, 1) * 1000000; + } + + return rand($delay_micoseconds - 100000, $delay_micoseconds); + + } + + /** + * Prevent brute force attacks by delaying the login. + * + * @param string $tool_name Identifier of the login. + * @param string $username Username to look for. + * @param integer $max_execution_time Optional maximum execution time to stay below. * * @return boolean */ - public static function preventBruteForce(string $tool_name, string $username):bool { + public static function preventBruteForce(string $tool_name, string $username, int $max_execution_time = 0):bool { // Unstable but working way to get the user's IP. If the IP is falsified, // this can't be found out anyway and security is established by _common. @@ -112,16 +149,13 @@ final class MD_STD_SEC { $delay_multiplier_per_user = $loginLog['usr'][$hash_user]['count']; $delay_multiplier_per_ip = $loginLog['ip'][$hash_ip]['count']; - // Calculate delay - $delay_micoseconds = \intval(self::BRUTE_FORCE_DELAY_DEFAULT * - (self::BRUTE_FORCE_DELAY_MULTIPLIER_COMMON ** $delay_multiplier_common) * - (self::BRUTE_FORCE_DELAY_MULTIPLIER_PER_USER ** $delay_multiplier_per_user) * - (self::BRUTE_FORCE_DELAY_MULTIPLIER_PER_IP ** $delay_multiplier_per_ip)); - - $max_execution_microseconds = \abs((int)\ini_get("max_execution_time")) * 1000000; + $delay_micoseconds = self::computeAntiBruteForceDelay($delay_multiplier_common, + $delay_multiplier_per_user, + $delay_multiplier_per_ip, + $max_execution_time); // Sleep - \usleep(min($delay_micoseconds, \abs($max_execution_microseconds - 1000000))); + \usleep($delay_micoseconds); if ($delay_micoseconds > \abs($max_execution_microseconds - 1000000)) { return false; diff --git a/tests/MD_STD_SECTest.php b/tests/MD_STD_SECTest.php new file mode 100644 index 0000000..7ae86da --- /dev/null +++ b/tests/MD_STD_SECTest.php @@ -0,0 +1,37 @@ + + */ +declare(strict_types = 1); + +use PHPUnit\Framework\TestCase; + +require __DIR__ . '/../src/MD_STD_SEC.php'; + +/** + * Tests for MD_STD_SEC. + */ +final class MD_STD_SECTest extends TestCase { + /** + * Function for testing if the page can be opened using invalid values for objektnum. + * + * @author Joshua Ramon Enslin + * @group MissingInputs + * @group SafeForProduction + * + * @return void + */ + public function testComputeAntiBruteForceDelayDoesNotGoOverMax():void { + + $delay = MD_STD_SEC::computeAntiBruteForceDelay(100, 100, 100); + self::assertGreaterThan(0, $delay); + # self::assertLessThan(10 * 1000000, $delay); // Smaller than 10 seconds + + $delay_reduced = MD_STD_SEC::computeAntiBruteForceDelay(100, 100, 100, 3); + self::assertGreaterThan(0, $delay_reduced); + self::assertLessThan(3 * 1000000, $delay_reduced); // Smaller than 10 seconds + + } +}