Verified Commit 8da7061e authored by Camil Staps's avatar Camil Staps 🚀

PHP-level DoS protection (resolves #81)

parent b728e2c6
......@@ -82,6 +82,7 @@ fields:
* `151`: invalid request type (should use GET)
* `152`: no input (GET variable `str` should be set to the search string)
* `153`: the Clean backend timed out
* `154`: you have sent too many requests; try again later (DoS protection)
- `msg`
......
......@@ -3,56 +3,81 @@ define('SERVER_HOSTNAME', 'backend');
define('SERVER_PORT', 31215);
define('SERVER_TIMEOUT', 8);
define('E_CLOOGLEDOWN', 150);
define('E_ILLEGALMETHOD', 151);
define('E_ILLEGALREQUEST', 152);
define('E_TIMEOUT', 153);
if (file_exists('conf.php'))
require_once('conf.php');
$start_time = microtime(true);
function log_request($code) {
if (defined('CLOOGLE_KEEP_STATISTICS')) {
$db = new mysqli(
CLOOGLE_DB_HOST, CLOOGLE_DB_USER, CLOOGLE_DB_PASS, CLOOGLE_DB_NAME);
if (mysqli_connect_errno())
return;
if (defined('CLOOGLE_KEEP_STATISTICS')) {
$db = new mysqli(
CLOOGLE_DB_HOST, CLOOGLE_DB_USER, CLOOGLE_DB_PASS, CLOOGLE_DB_NAME);
if (mysqli_connect_errno())
$db = null;
$ua = $_SERVER['HTTP_USER_AGENT'];
$ua_hash = md5($ua);
$ua = $_SERVER['HTTP_USER_AGENT'];
$ua_hash = md5($ua);
$stmt = $db->prepare('SELECT `id` FROM `useragent` WHERE `ua_hash`=?');
$stmt->bind_param('s', $ua_hash);
$stmt->execute();
$stmt->bind_result($ua_id);
if ($stmt->fetch() !== true) {
$stmt->close();
$stmt = $db->prepare(
'INSERT INTO `useragent` (`useragent`,`ua_hash`) VALUES (?,?)');
$stmt->bind_param('ss', $ua, $ua_hash);
$stmt->execute();
$ua_id = $stmt->insert_id;
}
$stmt = $db->prepare('SELECT `id` FROM `useragent` WHERE `ua_hash`=?');
$stmt->bind_param('s', $ua_hash);
$stmt->execute();
$stmt->bind_result($ua_id);
if ($stmt->fetch() !== true) {
$stmt->close();
global $start_time;
$time = (int) ((microtime(true) - $start_time) * 1000);
$ip = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ?
$_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
$stmt = $db->prepare('INSERT INTO `log`
(`ip`,`useragent_id`,`query`,`responsecode`,`responsetime`)
VALUES (?,?,?,?,?)');
$stmt->bind_param('sisii',
$ip,
$ua_id,
$_GET['str'],
$code,
$time);
$stmt = $db->prepare(
'INSERT INTO `useragent` (`useragent`,`ua_hash`) VALUES (?,?)');
$stmt->bind_param('ss', $ua, $ua_hash);
$stmt->execute();
$stmt->close();
$db->close();
$ua_id = $stmt->insert_id;
}
$stmt->close();
$ip = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ?
$_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
}
function has_database() {
global $db;
return defined('CLOOGLE_KEEP_STATISTICS') && !is_null($db);
}
function dos_protect() {
global $db, $ua_id, $ip;
if (!has_database)
return false;
$stmt = $db->prepare('SELECT COUNT(*) FROM `log`
WHERE `useragent_id`=? AND `ip`=? AND `date`>=NOW() - INTERVAL 1 SECOND');
$stmt->bind_param('is', $ua_id, $ip);
$stmt->execute();
$stmt->bind_result($count);
$stmt->fetch();
return $count >= DOS_MAX_REQUESTS_PER_SECOND;
}
function log_request($code) {
global $db, $ua_id, $ip;
if (!has_database())
return;
global $start_time;
$time = (int) ((microtime(true) - $start_time) * 1000);
$stmt = $db->prepare('INSERT INTO `log`
(`ip`,`useragent_id`,`query`,`responsecode`,`responsetime`)
VALUES (?,?,?,?,?)');
$stmt->bind_param('sisii',
$ip,
$ua_id,
$_GET['str'],
$code,
$time);
$stmt->execute();
$stmt->close();
$db->close();
}
function respond($code, $msg, $data=[]) {
......@@ -65,14 +90,12 @@ function respond($code, $msg, $data=[]) {
]);
}
if (file_exists('conf.php')) {
require_once('conf.php');
}
if($_SERVER['REQUEST_METHOD'] !== 'GET'){
if ($_SERVER['REQUEST_METHOD'] !== 'GET'){
respond(E_ILLEGALMETHOD, 'Can only be accessed by GET request');
} else if(!isset($_GET['str'])){
} else if (!isset($_GET['str'])){
respond(E_ILLEGALREQUEST, 'GET variable "str" must be set');
} else if (defined('CLOOGLE_KEEP_STATISTICS') && dos_protect()) {
respond(E_DOSPROTECT, "Yes, cloogle is great, but you don't need it so badly.");
} else {
$str = array_map('trim', explode('::', $_GET['str']));
$name = trim($str[0]);
......
......@@ -5,3 +5,11 @@ define('CLOOGLE_DB_NAME', 'cloogledb');
define('CLOOGLE_DB_PASS', 'cloogle');
define('CLOOGLE_KEEP_STATISTICS', true);
define('E_CLOOGLEDOWN', 150);
define('E_ILLEGALMETHOD', 151);
define('E_ILLEGALREQUEST', 152);
define('E_TIMEOUT', 153);
define('E_DOSPROTECT', 154);
define('DOS_MAX_REQUESTS_PER_SECOND', 3);
......@@ -3,7 +3,9 @@ require_once('./conf.php');
$sql =
"SELECT dayofweek(`date`), hour(`date`), count(*) FROM `log`
WHERE `date` BETWEEN timestamp('$startTime') AND timestamp('$endTime')
WHERE
`responsecode` <> " . E_DOSPROTECT . " AND
`date` BETWEEN timestamp('$startTime') AND timestamp('$endTime')
GROUP BY dayofweek(`date`), hour(`date`)
ORDER BY dayofweek(`date`) asc, hour(`date`) ASC";
......
......@@ -12,6 +12,7 @@ $sql =
FROM `log`
INNER JOIN `useragent` ON `log`.`useragent_id` = `useragent`.`id`
WHERE
`responsecode` <> " . E_DOSPROTECT . " AND
`date` BETWEEN timestamp('$startTime') AND timestamp('$endTime')
AND ";
......
......@@ -38,7 +38,9 @@ $sql =
count(case when `responsecode`>1 and `responsecode`<150 then 1 else null end),
count(distinct `ip`,`useragent_id`)
FROM `log`
WHERE `date` BETWEEN timestamp('$startTime') AND timestamp('$endTime')
WHERE
`responsecode` <> " . E_DOSPROTECT . " AND
`date` BETWEEN timestamp('$startTime') AND timestamp('$endTime')
GROUP BY $group
ORDER BY min(`date`) ASC";
......
......@@ -8,7 +8,9 @@ $sql =
sum(case when `query` LIKE 'type %' then 1 else null end),
sum(case when `query` LIKE 'class %' then 1 else null end)
FROM `log`
WHERE `date` BETWEEN timestamp('$startTime') AND timestamp('$endTime')";
WHERE
`responsecode` <> " . E_DOSPROTECT . " AND
`date` BETWEEN timestamp('$startTime') AND timestamp('$endTime')";
$stmt = $db->stmt_init();
if (!$stmt->prepare($sql))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment