/trunk/.gitignore |
---|
1,3 → 1,5 |
vendor/ |
composer.lock |
composer.phar |
Sha3.php |
/trunk/ClientChallenge.class.php |
---|
2,7 → 2,7 |
/* |
* php_clientchallenge |
* Copyright 2021 Daniel Marschall, ViaThinkSoft |
* Copyright 2021-2022 Daniel Marschall, ViaThinkSoft |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); |
* you may not use this file except in compliance with the License. |
21,13 → 21,25 |
class ClientChallenge { |
const OPEN_TRANS_DIR = __DIR__.'/cache'; |
private static function sha3_512($message, $raw_output=false) { |
if (version_compare(PHP_VERSION, '7.1.0') >= 0) { |
return hash('sha3-512', $message, $raw_output); |
} else { |
return \bb\Sha3\Sha3::hash($message, 512, $raw_output); /** @phpstan-ignore-line */ |
// Download file if required (usually composer should do it) |
if (file_exists(__DIR__.'/Sha3.php')) include_once __DIR__.'/Sha3.php'; |
if (!class_exists('\bb\Sha3\Sha3')) { |
$sha3_lib = file_get_contents('https://raw.githubusercontent.com/danielmarschall/php-sha3/master/src/Sha3.php'); |
if (file_put_contents(__DIR__.'/Sha3.php', $sha3_lib)) { |
include_once __DIR__.'/Sha3.php'; |
} else { |
eval('?>'.$sha3_lib); |
} |
} |
return \bb\Sha3\Sha3::hash($message, 512, $raw_output); |
} |
} |
private static function sha3_512_hmac($message, $key, $raw_output=false) { |
// RFC 2104 HMAC |
53,11 → 65,22 |
} |
} |
public static function checkValidation($max_time=10, $server_secret) { |
private static function getOpenTransFileName($ip_target, $random) { |
// Delete challenges which were never completed |
$files = glob(self::OPEN_TRANS_DIR.'/*.tmp'); |
$expire = strtotime('-3 DAYS'); |
foreach ($files as $file) { |
if (!is_file($file)) continue; |
if (filemtime($file) > $expire) continue; |
@unlink($file); |
} |
if (!isset($_REQUEST['vts_validation_result'])) throw new \Exception('No challenge response found'); |
return self::OPEN_TRANS_DIR.'/'.self::sha3_512($ip_target.'/'.$random).'.tmp'; |
} |
list($starttime, $ip_target, $challenge, $answer, $challenge_integrity) = @json_decode($_REQUEST['vts_validation_result'], true); |
public static function checkValidation($client_response, $max_time=10, $server_secret) { |
list($starttime, $ip_target, $challenge, $answer, $challenge_integrity) = $client_response; |
$open_trans_file = self::getOpenTransFileName($ip_target, $answer); |
if ($ip_target != $_SERVER['REMOTE_ADDR']) { |
throw new \Exception('Wrong IP'); |
67,17 → 90,19 |
throw new \Exception('Challenge integrity failed'); |
} else if ($challenge !== self::sha3_512($starttime.'/'.$ip_target.'/'.$answer)) { |
throw new \Exception('Wrong answer'); |
} else if (!file_exists($open_trans_file)) { |
throw new \Exception('Challenge submitted twice or transaction missing'); |
} else { |
unlink($open_trans_file); |
return true; |
} |
} |
// This is only called by ajax_get_challenge.php |
public static function createChallenge($complexity=500000, $server_secret) { |
public static function createChallenge($complexity=50000, $server_secret) { |
$offset = 0; // doesn't matter |
$min = $offset; |
$max = $offset + $complexity; |
$min = 0; |
$max = $complexity; |
$starttime = time(); |
$random = rand($min,$max); // TODO: cryptographic rand |
90,9 → 115,12 |
$send_to_client = array($starttime, $ip_target, $challenge, $min, $max, $challenge_integrity); |
header('Content-Type:application/json'); |
die(json_encode($send_to_client)); |
$open_trans_file = self::getOpenTransFileName($ip_target, $random); |
if (@file_put_contents($open_trans_file, '') === false) { |
throw new \Exception("Cannot write $open_trans_file"); |
} |
return $send_to_client; |
} |
} |
/trunk/ClientChallenge.js |
---|
1,6 → 1,6 |
/* |
* php_clientchallenge |
* Copyright 2021 Daniel Marschall, ViaThinkSoft |
* Copyright 2021-2022 Daniel Marschall, ViaThinkSoft |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); |
* you may not use this file except in compliance with the License. |
22,6 → 22,10 |
data: { |
}, |
success: function(data) { |
if ("error" in data) { |
error_cb(null,null,data["error"]); |
return; |
} |
var starttime = data[0]; |
var ip_target = data[1]; |
var challenge = data[2]; |
/trunk/cache/.gitignore |
---|
--- composer.json (revision 5) |
+++ composer.json (revision 6) |
@@ -1,7 +1,7 @@ |
{ |
"name": "danielmarschall/php_clientchallenge", |
"description": "Server requests using client-challenges in order to mitigate resource starvation", |
- "version": "1.0", |
+ "version": "1.1", |
"type": "package", |
"homepage": "https://www.viathinksoft.com/projects/php_clientchallenge", |
"authors": [ |
/trunk/example/ajax_example.php |
---|
2,7 → 2,7 |
/* |
* php_clientchallenge |
* Copyright 2021 Daniel Marschall, ViaThinkSoft |
* Copyright 2021-2022 Daniel Marschall, ViaThinkSoft |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); |
* you may not use this file except in compliance with the License. |
29,7 → 29,9 |
// Check request field "vts_validation_result" for valid response of the Challenge |
try { |
\ViaThinkSoft\RateLimitingChallenge\ClientChallenge::checkValidation(MAX_TIME, VTS_CS_SERVER_SECRET); |
if (!isset($_REQUEST['vts_validation_result'])) throw new \Exception('No challenge response found'); |
$client_response = @json_decode($_REQUEST['vts_validation_result'], true); |
\ViaThinkSoft\RateLimitingChallenge\ClientChallenge::checkValidation($client_response, MAX_TIME, VTS_CS_SERVER_SECRET); |
} catch (\Exception $e) { |
$res = array("error" => $e->getMessage()); |
header('Content-Type:application/json'); |
/trunk/example/ajax_get_challenge.php |
---|
2,7 → 2,7 |
/* |
* php_clientchallenge |
* Copyright 2021 Daniel Marschall, ViaThinkSoft |
* Copyright 2021-2022 Daniel Marschall, ViaThinkSoft |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); |
* you may not use this file except in compliance with the License. |
25,4 → 25,11 |
require_once __DIR__ . '/config.inc.php'; |
\ViaThinkSoft\RateLimitingChallenge\ClientChallenge::createChallenge(COMPLEXITY, VTS_CS_SERVER_SECRET); |
try { |
$res = \ViaThinkSoft\RateLimitingChallenge\ClientChallenge::createChallenge(COMPLEXITY, VTS_CS_SERVER_SECRET); |
} catch (\Exception $e) { |
$res = array("error" => $e->getMessage()); |
} |
header('Content-Type:application/json'); |
die(json_encode($res)); |
/trunk/example/config.inc.php |
---|
2,7 → 2,7 |
/* |
* php_clientchallenge |
* Copyright 2021 Daniel Marschall, ViaThinkSoft |
* Copyright 2021-2022 Daniel Marschall, ViaThinkSoft |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); |
* you may not use this file except in compliance with the License. |
18,5 → 18,5 |
*/ |
define('VTS_CS_SERVER_SECRET', '1234567890'); // PLEASE CHANGE THIS VALUE TO SOMETHING RANDOM! |
define('MAX_TIME', 10); // seconds |
define('COMPLEXITY', 500000); |
define('MAX_TIME', 15); // seconds |
define('COMPLEXITY', 50000); |
/trunk/example/index.html |
---|
12,6 → 12,7 |
let error_cb = function (request, status, error) { |
$("#out").val("Error!"); |
alert(error); |
} |
let callback = function(params, vts_validation_result) { |
29,8 → 30,7 |
}, |
success: function(data) { |
if ("error" in data) { |
$("#out").val('ERROR'); |
alert(data["error"]); |
error_cb(null,null,data["error"]); |
} else { |
$("#out").val(data["result"]); |
} |
/trunk/phpstan.neon.dist |
---|
6,6 → 6,8 |
paths: |
- . |
excludePaths: |
analyse: |
- Sha3.php |
analyseAndScan: |
- .phpstan.tmp |
tmpDir: .phpstan.tmp |
/trunk/. |
---|
Property changes: |
Modified: svn:ignore |
composer.phar |
.phpstan.tmp |
phpstan.neon |
+Sha3.php |
+ |