Compare commits

...
Sign in to create a new pull request.

12 commits

3 changed files with 170 additions and 0 deletions

View file

@ -236,6 +236,30 @@
// To prevent bump atacks; returns the thread to last position after the last post is deleted.
$config['anti_bump_flood'] = false;
// Use 3rd part APIs to check if an IP should be blocked.
$config['ip_api'] = [
'iphub' => [
// Enable or disable the IPHub API backend.
'enabled' => false,
// IPHub API key for checking for bad proxies (https://iphub.info/api).
'key' => '',
],
// If true, add a note to the IP explaining why it was banned.
'add_note' => false,
// If you find that a lot of spammers are coming from a specific ISP, or ASN,
// you can put the ISP in isp_blacklist and/or the ASN in asn_blacklist
// USE THIS WITH CAUTION, IT MAY PREVENT NON-SPAMMERS FROM POSTING
'isp_blacklist' => [],
'asn_blacklist' => [],
// IPs in this array won't be checked.
// This can be used if known legitimate posters are getting blocked by a backend.
'ip_whitelist' => [],
// Block according to the API suggestion.
// Use 0 to ignore this field, 1 to block those which are strongly suggested to be blocked,
// 2 to block all which aren't known good actors.
'ip_block' => 0
];
/*
* Introduction to Tinyboard's spam filter:
*
@ -1198,6 +1222,7 @@
// Error messages
$config['error']['bot'] = _('You look like a bot.');
$config['error']['proxy'] = _('You seem to be using an unathorized proxy.');
$config['error']['referer'] = _('Your browser sent an invalid or no HTTP referer.');
$config['error']['toolong'] = _('The %s field was too long.');
$config['error']['toolong_body'] = _('The body was too long.');

View file

@ -1823,6 +1823,139 @@ function buildJavascript() {
file_write($config['file_script'], $script);
}
/**
* @param string $ip The IP to check.
* @return bool True if the IP should be blocked.
*/
function checkIPAPI(string $ip): bool {
function add_note(string $ip, string $note) {
$query = prepare('INSERT INTO ``ip_notes`` VALUES (NULL, :ip, :mod, :time, :body)');
$query->bindValue(':ip', $ip);
$query->bindValue(':mod', -1, PDO::PARAM_INT);
$query->bindValue(':time', time(), PDO::PARAM_INT);
$query->bindValue(':body', "IP API automatic note: $note");
$query->execute() or error(db_error($query));
}
global $config;
if (!$config['ip_api']['iphub']['enabled']) {
return false;
}
$iphub_key = $config['ip_api']['iphub']['key'];
if (empty($iphub_key)) {
error_log('IP api backend is enabled but IPHub API is empty!');
return false;
}
// Query IPHub's database with the poster's IP.
if (array_search($ip, $config['ip_api']['ip_whitelist']) !== false) {
// IP is whitelisted, don't bother querying.
return false;
}
$ret = false;
if ($config['cache']['enabled']) {
$ret = cache::get("ip_api_block_$ip");
}
if ($ret === false) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://v2.api.iphub.info/ip/$ip");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, [ "X-Key: $iphub_key" ]);
$reply = curl_exec($ch);
if ($reply === false) {
$curl_err = curl_error($ch);
curl_close($ch);
error_log("IPHub query failed: api call failed with curl error $curl_err");
if ($config['cache']['enabled']) {
// Store the result for 4 hours.
cache::set("ip_api_block_$ip", 'api_error', 14400);
}
return false;
}
curl_close($ch);
if (empty($reply)) {
error_log('IPHub query failed: api returned an empty response');
if ($config['cache']['enabled']) {
// Store the result for 4 hours.
cache::set("ip_api_block_$ip", 'api_error', 14400);
}
return false;
}
$json = json_decode($reply);
if ($json === null) {
error_log('IPHub query failed: api returned incorrect json');
if ($config['cache']['enabled']) {
// Store the result for 4 hours.
cache::set("ip_api_block_$ip", 'api_error', 14400);
}
return false;
}
if (isset($json->error)) {
error_log("IPHub query failed: $json->error");
if ($config['cache']['enabled']) {
// Store the result for 4 hours.
cache::set("ip_api_block_$ip", 'api_error', 14400);
}
return false;
}
$ret = [
'block' => $json['block'],
'isp' => $json['isp'],
'asn' => $json['asn']
];
if ($config['cache']['enabled']) {
// Store the result for 2 days.
cache::set("ip_api_block_$ip", $ret, 172800);
}
} elseif (!is_array($ret)) {
// Cache previously reported an error, just return.
return false;
}
$add_note_mode = (int)$config['ip_api']['add_note'];
if (($config['ip_api']['ip_block'] == 1 && $ret['block'] == 1) || ($config['ip_api']['ip_block'] == 2 && $ret['block'] != 0)) {
if ($add_note_mode !== 0) {
add_note($ip, _('IP was rejected for being blacklisted by the API'));
}
error($config['error']['proxy']);
return true;
}
if (array_search($ret['isp'], $config['ip_api']['isp_blacklist']) !== false) {
if ($add_note_mode !== 0) {
add_note($ip, _("IP was rejected because the \"{$ret['isp']}\" ISP is blacklisted."));
}
error($config['error']['proxy']);
return true;
}
if (array_search($ret['asn'], $config['ip_api']['asn_blacklist']) !== false) {
if ($add_note_mode !== 0) {
add_note($ip, _("IP was rejected because the \"{$ret['asn']}\" ASN is blacklisted."));
}
error($config['error']['proxy']);
return true;
}
return false;
}
function checkDNSBL() {
global $config;

View file

@ -469,6 +469,10 @@ function handle_delete()
checkDNSBL();
if (checkIPAPI($_SERVER['REMOTE_ADDR'])) {
error($config['error']['proxy']);
}
// Check if board exists
if (!openBoard($_POST['board'])) {
error($config['error']['noboard']);
@ -577,6 +581,10 @@ function handle_report()
checkDNSBL();
if (checkIPAPI($_SERVER['REMOTE_ADDR'])) {
error($config['error']['proxy']);
}
// Check if board exists.
if (!openBoard($_POST['board'])) {
error($config['error']['noboard']);
@ -855,6 +863,10 @@ function handle_post()
checkDNSBL();
if (checkIPAPI($_SERVER['REMOTE_ADDR'])) {
error($config['error']['proxy']);
}
// Check if banned
checkBan($board['uri']);