mirror of
https://github.com/vichan-devel/vichan.git
synced 2024-11-27 17:00:52 +01:00
ip cloaking
This commit is contained in:
parent
7a42158177
commit
bce71c1f98
21
inc/bans.php
21
inc/bans.php
@ -142,6 +142,7 @@ class Bans {
|
||||
if ($ban['post'])
|
||||
$ban['post'] = json_decode($ban['post'], true);
|
||||
$ban['mask'] = self::range_to_string(array($ban['ipstart'], $ban['ipend']));
|
||||
$ban['cmask'] = cloak_mask($ban['mask']);
|
||||
$ban_list[] = $ban;
|
||||
}
|
||||
}
|
||||
@ -161,8 +162,9 @@ class Bans {
|
||||
|
||||
$end = end($bans);
|
||||
|
||||
foreach ($bans as &$ban) {
|
||||
$ban['mask'] = self::range_to_string(array($ban['ipstart'], $ban['ipend']));
|
||||
foreach ($bans as &$ban) {
|
||||
$uncloaked_mask = self::range_to_string(array($ban['ipstart'], $ban['ipend']));
|
||||
$ban['mask'] = cloak_mask($uncloaked_mask);
|
||||
|
||||
if ($ban['post']) {
|
||||
$post = json_decode($ban['post']);
|
||||
@ -174,7 +176,7 @@ class Bans {
|
||||
$ban['access'] = true;
|
||||
}
|
||||
|
||||
if (filter_var($ban['mask'], FILTER_VALIDATE_IP) !== false) {
|
||||
if (filter_var($uncloaked_mask, FILTER_VALIDATE_IP) !== false) {
|
||||
$ban['single_addr'] = true;
|
||||
}
|
||||
if ($filter_staff || ($board_access !== false && !in_array($ban['board'], $board_access))) {
|
||||
@ -200,7 +202,7 @@ class Bans {
|
||||
}
|
||||
}
|
||||
|
||||
$out ? fputs($out, "]") : print("]");
|
||||
$out ? fputs($out, "]") : print("]");
|
||||
|
||||
}
|
||||
|
||||
@ -230,9 +232,10 @@ class Bans {
|
||||
error($config['error']['noaccess']);
|
||||
|
||||
$mask = self::range_to_string(array($ban['ipstart'], $ban['ipend']));
|
||||
$cloaked_mask = cloak_mask($mask);
|
||||
|
||||
modLog("Removed ban #{$ban_id} for " .
|
||||
(filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$mask\">$mask</a>" : $mask));
|
||||
(filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$cloaked_mask\">$cloaked_mask</a>" : $cloaked_mask));
|
||||
}
|
||||
|
||||
query("DELETE FROM ``bans`` WHERE `id` = " . (int)$ban_id) or error(db_error());
|
||||
@ -242,7 +245,9 @@ class Bans {
|
||||
return true;
|
||||
}
|
||||
|
||||
static public function new_ban($mask, $reason, $length = false, $ban_board = false, $mod_id = false, $post = false) {
|
||||
static public function new_ban($cloaked_mask, $reason, $length = false, $ban_board = false, $mod_id = false, $post = false) {
|
||||
$mask = uncloak_mask($cloaked_mask);
|
||||
|
||||
global $mod, $pdo, $board;
|
||||
|
||||
if ($mod_id === false) {
|
||||
@ -251,6 +256,7 @@ class Bans {
|
||||
|
||||
$range = self::parse_range($mask);
|
||||
$mask = self::range_to_string($range);
|
||||
$cloaked_mask = cloak_mask($mask);
|
||||
|
||||
$query = prepare("INSERT INTO ``bans`` VALUES (NULL, :ipstart, :ipend, :time, :expires, :board, :mod, :reason, 0, :post)");
|
||||
|
||||
@ -293,14 +299,13 @@ class Bans {
|
||||
$query->bindValue(':post', null, PDO::PARAM_NULL);
|
||||
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
if (isset($mod['id']) && $mod['id'] == $mod_id) {
|
||||
modLog('Created a new ' .
|
||||
($length > 0 ? preg_replace('/^(\d+) (\w+?)s?$/', '$1-$2', until($length)) : 'permanent') .
|
||||
' ban on ' .
|
||||
($ban_board ? '/' . $ban_board . '/' : 'all boards') .
|
||||
' for ' .
|
||||
(filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$mask\">$mask</a>" : $mask) .
|
||||
(filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$cloaked_mask\">$cloaked_mask</a>" : $cloaked_mask) .
|
||||
' (<small>#' . $pdo->lastInsertId() . '</small>)' .
|
||||
' with ' . ($reason ? 'reason: ' . utf8tohtml($reason) . '' : 'no reason'));
|
||||
}
|
||||
|
@ -1874,3 +1874,13 @@
|
||||
|
||||
// Allowed HTML tags in ?/edit_pages.
|
||||
$config['allowed_html'] = 'a[href|title],p,br,li,ol,ul,strong,em,u,h2,b,i,tt,div,img[src|alt|title],hr';
|
||||
|
||||
// Secret passphrase for IP cloaking
|
||||
// Disabled if empty.
|
||||
$config['ipcrypt_key'] = '';
|
||||
|
||||
// IP cloak prefix
|
||||
$config['ipcrypt_prefix'] = 'Cloak';
|
||||
|
||||
// Whether to append domain names to IP cloaks
|
||||
$config['ipcrypt_dns'] = false;
|
||||
|
@ -2860,3 +2860,118 @@ function strategy_first($fun, $array) {
|
||||
return array('defer');
|
||||
}
|
||||
}
|
||||
|
||||
function base32_decode($d) {
|
||||
$charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||
$d = str_split($d);
|
||||
$l = array_pop($d);
|
||||
$b = '';
|
||||
foreach ($d as $c) {
|
||||
$b .= sprintf("%05b", strpos($charset, $c));
|
||||
}
|
||||
$padding = 8 - strlen($b) % 8;
|
||||
$b .= str_pad(decbin(strpos($charset, $l)), $padding, '0', STR_PAD_LEFT);
|
||||
|
||||
return implode('', array_map(function($c) { return chr(bindec($c)); }, str_split($b, 8)));
|
||||
}
|
||||
|
||||
function base32_encode($d) {
|
||||
$charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||
$b = implode('', array_map(function($c) { return sprintf("%08b", ord($c)); }, str_split($d)));
|
||||
return implode('', array_map(function($c) use ($charset) { return $charset[bindec($c)]; }, str_split($b, 5)));
|
||||
}
|
||||
|
||||
function cloak_ip($ip) {
|
||||
global $config;
|
||||
$ipcrypt_key = $config['ipcrypt_key'] ?: null;
|
||||
|
||||
if (empty($ipcrypt_key))
|
||||
return $ip;
|
||||
|
||||
$ip_dec = inet_pton($ip);
|
||||
|
||||
if ($config['ipcrypt_dns']) {
|
||||
$host = gethostbyaddr($ip);
|
||||
|
||||
if ($host !== $ip) {
|
||||
$segments = explode('.', $host);
|
||||
|
||||
$tld = [];
|
||||
$tld[] = array_pop($segments);
|
||||
if (count($segments) >= 2) {
|
||||
$tld[] = array_pop($segments);
|
||||
}
|
||||
|
||||
$tld = implode('.', array_reverse($tld));
|
||||
}
|
||||
}
|
||||
|
||||
if (is_numeric($ip))
|
||||
$ipbytes = pack('N', $ip);
|
||||
else if ($ip_dec !== false)
|
||||
$ipbytes = $ip_dec;
|
||||
else
|
||||
return "#ERROR";
|
||||
|
||||
if (strlen($ipbytes) >= 16)
|
||||
$ipbytes = substr($ipbytes, 0, 16);
|
||||
|
||||
$cyphertext = openssl_encrypt($ipbytes, 'rc4-40', $ipcrypt_key, OPENSSL_RAW_DATA);
|
||||
|
||||
$ret = $config['ipcrypt_prefix'].':' . base32_encode($cyphertext);
|
||||
if (isset($tld) && !empty($tld)) {
|
||||
$ret .= '.'.$tld;
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function uncloak_ip($ip) {
|
||||
global $config;
|
||||
$ipcrypt_key = ($config['ipcrypt_key']);
|
||||
|
||||
if (empty($ipcrypt_key))
|
||||
return $ip;
|
||||
|
||||
$juice = substr($ip, strlen($config['ipcrypt_prefix']) + 1);
|
||||
if ($delimiter = strpos($juice, '.')) {
|
||||
$juice = substr($juice, 0, $delimiter);
|
||||
}
|
||||
|
||||
if (substr($ip, 0, strlen($config['ipcrypt_prefix']) + 1) === $config['ipcrypt_prefix'].':') {
|
||||
$plaintext = openssl_decrypt(base32_decode($juice), 'rc4-40', $ipcrypt_key, OPENSSL_RAW_DATA);
|
||||
|
||||
if ($plaintext === false || strlen($plaintext) == 0)
|
||||
return '#ERROR';
|
||||
|
||||
if (strlen($ip) >= 16)
|
||||
return inet_ntop($plaintext);
|
||||
else
|
||||
return long2ip(unpack('N', $plaintext)[1]);
|
||||
}
|
||||
|
||||
return '#ERROR';
|
||||
}
|
||||
|
||||
function cloak_mask($mask) {
|
||||
list($net, $block) = explode('/', $mask, 2);
|
||||
$mask = cloak_ip($net);
|
||||
if ($block) {
|
||||
$mask .= '/'.$block;
|
||||
}
|
||||
|
||||
return $mask;
|
||||
}
|
||||
|
||||
function uncloak_mask($mask) {
|
||||
list($addr, $block) = explode('/', $mask, 2);
|
||||
$mask = uncloak_ip($addr);
|
||||
if ($mask === '#ERROR') {
|
||||
$mask = $addr;
|
||||
}
|
||||
if ($block) {
|
||||
$mask .= '/'.$block;
|
||||
}
|
||||
|
||||
return $mask;
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension
|
||||
new Twig_SimpleFilter('push', 'twig_push_filter'),
|
||||
new Twig_SimpleFilter('bidi_cleanup', 'bidi_cleanup'),
|
||||
new Twig_SimpleFilter('addslashes', 'addslashes'),
|
||||
new Twig_SimpleFilter('cloak_ip', 'cloak_ip'),
|
||||
new Twig_SimpleFilter('cloak_mask', 'cloak_mask'),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -773,7 +773,8 @@ function mod_view_thread50($boardName, $thread) {
|
||||
echo $page;
|
||||
}
|
||||
|
||||
function mod_ip_remove_note($ip, $id) {
|
||||
function mod_ip_remove_note($cloaked_ip, $id) {
|
||||
$ip = uncloak_ip($cloaked_ip);
|
||||
global $config, $mod;
|
||||
|
||||
if (!hasPermission($config['mod']['remove_notes']))
|
||||
@ -787,12 +788,13 @@ function mod_ip_remove_note($ip, $id) {
|
||||
$query->bindValue(':id', $id);
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
modLog("Removed a note for <a href=\"?/IP/{$ip}\">{$ip}</a>");
|
||||
modLog("Removed a note for <a href=\"?/IP/{$cloaked_ip}\">{$cloaked_ip}</a>");
|
||||
|
||||
header('Location: ?/IP/' . $ip . '#notes', true, $config['redirect_http']);
|
||||
header('Location: ?/IP/' . $cloaked_ip . '#notes', true, $config['redirect_http']);
|
||||
}
|
||||
|
||||
function mod_page_ip($ip) {
|
||||
function mod_page_ip($cip) {
|
||||
$ip = uncloak_ip($cip);
|
||||
global $config, $mod;
|
||||
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP) === false)
|
||||
@ -804,7 +806,7 @@ function mod_page_ip($ip) {
|
||||
|
||||
Bans::delete($_POST['ban_id'], true, $mod['boards']);
|
||||
|
||||
header('Location: ?/IP/' . $ip . '#bans', true, $config['redirect_http']);
|
||||
header('Location: ?/IP/' . $cip . '#bans', true, $config['redirect_http']);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -821,9 +823,9 @@ function mod_page_ip($ip) {
|
||||
$query->bindValue(':body', $_POST['note']);
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
modLog("Added a note for <a href=\"?/IP/{$ip}\">{$ip}</a>");
|
||||
modLog("Added a note for <a href=\"?/IP/{$cip}\">{$cip}</a>");
|
||||
|
||||
header('Location: ?/IP/' . $ip . '#notes', true, $config['redirect_http']);
|
||||
header('Location: ?/IP/' . $cip . '#notes', true, $config['redirect_http']);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -831,7 +833,7 @@ function mod_page_ip($ip) {
|
||||
$args['ip'] = $ip;
|
||||
$args['posts'] = array();
|
||||
|
||||
if ($config['mod']['dns_lookup'])
|
||||
if ($config['mod']['dns_lookup'] && empty($config['ipcrypt_key']))
|
||||
$args['hostname'] = rDNS($ip);
|
||||
|
||||
$boards = listBoards();
|
||||
@ -873,16 +875,16 @@ function mod_page_ip($ip) {
|
||||
|
||||
if (hasPermission($config['mod']['modlog_ip'])) {
|
||||
$query = prepare("SELECT `username`, `mod`, `ip`, `board`, `time`, `text` FROM ``modlogs`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `text` LIKE :search ORDER BY `time` DESC LIMIT 50");
|
||||
$query->bindValue(':search', '%' . $ip . '%');
|
||||
$query->bindValue(':search', '%' . $cip . '%');
|
||||
$query->execute() or error(db_error($query));
|
||||
$args['logs'] = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$args['logs'] = array();
|
||||
}
|
||||
|
||||
$args['security_token'] = make_secure_link_token('IP/' . $ip);
|
||||
$args['security_token'] = make_secure_link_token('IP/' . $cip);
|
||||
|
||||
mod_page(sprintf('%s: %s', _('IP'), htmlspecialchars($ip)), 'mod/view_ip.html', $args, $args['hostname']);
|
||||
mod_page(sprintf('%s: %s', _('IP'), htmlspecialchars($cip)), 'mod/view_ip.html', $args, $args['hostname'] ?? null);
|
||||
}
|
||||
|
||||
function mod_ban() {
|
||||
@ -974,7 +976,7 @@ function mod_ban_appeals() {
|
||||
error(_('Ban appeal not found!'));
|
||||
}
|
||||
|
||||
$ban['mask'] = Bans::range_to_string(array($ban['ipstart'], $ban['ipend']));
|
||||
$ban['mask'] = cloak_mask(Bans::range_to_string(array($ban['ipstart'], $ban['ipend'])));
|
||||
|
||||
if (isset($_POST['unban'])) {
|
||||
modLog('Accepted ban appeal #' . $ban['id'] . ' for ' . $ban['mask']);
|
||||
@ -1751,7 +1753,8 @@ function mod_deletebyip($boardName, $post, $global = false) {
|
||||
}
|
||||
|
||||
// Record the action
|
||||
modLog("Deleted all posts by IP address: <a href=\"?/IP/$ip\">$ip</a>");
|
||||
$cip = cloak_ip($ip);
|
||||
modLog("Deleted all posts by IP address: <a href=\"?/IP/$cip\">$cip</a>");
|
||||
|
||||
// Redirect
|
||||
header('Location: ?/' . sprintf($config['board_path'], $boardName) . $config['file_index'], true, $config['redirect_http']);
|
||||
@ -2210,7 +2213,7 @@ function mod_reports() {
|
||||
$reports = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$report_queries = array();
|
||||
foreach ($reports as $report) {
|
||||
foreach ($reports as &$report) {
|
||||
if (!isset($report_queries[$report['board']]))
|
||||
$report_queries[$report['board']] = array();
|
||||
$report_queries[$report['board']][] = $report['post'];
|
||||
@ -2308,9 +2311,9 @@ function mod_report_dismiss($id, $all = false) {
|
||||
}
|
||||
$query->execute() or error(db_error($query));
|
||||
|
||||
|
||||
$cip = cloak_ip($ip);
|
||||
if ($all)
|
||||
modLog("Dismissed all reports by <a href=\"?/IP/$ip\">$ip</a>");
|
||||
modLog("Dismissed all reports by <a href=\"?/IP/$cip\">$cip</a>");
|
||||
else
|
||||
modLog("Dismissed a report for post #{$id}", $board);
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
{% if mod|hasPermission(config.mod.show_ip, board.uri) %}
|
||||
<tr>
|
||||
<th>{% trans 'IP' %}</th>
|
||||
<td>{{ ban.mask }}</td>
|
||||
<td>{{ ban.mask|cloak_mask }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
|
@ -21,7 +21,7 @@
|
||||
</th>
|
||||
<td>
|
||||
{% if not hide_ip %}
|
||||
<input type="text" name="ip" id="ip" size="30" maxlength="40" value="{{ ip|e }}">
|
||||
<input type="text" name="ip" id="ip" size="30" maxlength="40" value="{{ ip|cloak_ip|e }}">
|
||||
{% else %}
|
||||
<em>{% trans 'hidden' %}</em>
|
||||
{% endif %}
|
||||
|
@ -27,7 +27,7 @@
|
||||
</td>
|
||||
<td class="minimal">
|
||||
{% if mod|hasPermission(config.mod.show_ip_modlog) %}
|
||||
<a href="?/IP/{{ log.ip }}">{{ log.ip }}</a>
|
||||
<a href="?/IP/{{ log.ip|cloak_ip }}">{{ log.ip|cloak_ip }}</a>
|
||||
{% else %}
|
||||
<em>hidden</em>
|
||||
{% endif %}
|
||||
|
@ -7,7 +7,7 @@
|
||||
{% trans 'Report date' %}: {{ report.time|date(config.post_date) }}
|
||||
<br>
|
||||
{% if mod|hasPermission(config.mod.show_ip, report.board) %}
|
||||
{% trans 'Reported by' %}: <a href="?/IP/{{ report.ip }}">{{ report.ip }}</a>
|
||||
{% trans 'Reported by' %}: <a href="?/IP/{{ report.ip|cloak_ip }}">{{ report.ip|cloak_ip }}</a>
|
||||
<br>
|
||||
{% endif %}
|
||||
{% if mod|hasPermission(config.mod.report_dismiss, report.board) or mod|hasPermission(config.mod.report_dismiss_ip, report.board) %}
|
||||
|
@ -21,7 +21,7 @@
|
||||
{% for note in results %}
|
||||
<tr>
|
||||
<td class="minimal">
|
||||
<a href="?/IP/{{ note.ip }}#notes">{{ note.ip }}</a>
|
||||
<a href="?/IP/{{ note.ip|cloak_ip }}#notes">{{ note.ip|cloak_ip }}</a>
|
||||
</td>
|
||||
<td class="minimal">
|
||||
{% if note.username %}
|
||||
@ -57,9 +57,9 @@
|
||||
<tr{% if ban.expires != 0 and ban.expires < time() %} style="text-decoration:line-through"{% endif %}>
|
||||
<td style="white-space: nowrap">
|
||||
{% if ban.single_addr %}
|
||||
<a href="?/IP/{{ ban.mask }}#bans">{{ ban.mask }}</a>
|
||||
<a href="?/IP/{{ ban.mask|cloak_mask }}#bans">{{ ban.mask|cloak_mask }}</a>
|
||||
{% else %}
|
||||
{{ ban.mask|e }}
|
||||
{{ ban.mask|cloak_mask|e }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
@ -148,7 +148,7 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="minimal">
|
||||
<a href="?/IP/{{ log.ip }}">{{ log.ip }}</a>
|
||||
<a href="?/IP/{{ log.ip|cloak_ip }}">{{ log.ip|cloak_ip }}</a>
|
||||
</td>
|
||||
<td class="minimal">
|
||||
<span title="{{ log.time|date(config.post_date) }}">{{ log.time|ago }}</span>
|
||||
@ -210,8 +210,8 @@
|
||||
</td>
|
||||
<td class="minimal">
|
||||
{% if mod|hasPermission(config.mod.show_ip, post.board) %}
|
||||
<a href="?/IP/{{ post.ip }}">
|
||||
{{ post.ip }}
|
||||
<a href="?/IP/{{ post.ip|cloak_ip }}">
|
||||
{{ post.ip|cloak_ip }}
|
||||
</a>
|
||||
{% else %}
|
||||
<em>hidden</em>
|
||||
|
@ -98,7 +98,7 @@
|
||||
{% for log in logs %}
|
||||
<tr>
|
||||
<td class="minimal">
|
||||
<a href="?/IP/{{ log.ip }}">{{ log.ip }}</a>
|
||||
<a href="?/IP/{{ log.ip|cloak_ip }}">{{ log.ip|cloak_ip }}</a>
|
||||
</td>
|
||||
<td class="minimal">
|
||||
<span title="{{ log.time|date(config.post_date) }}">{{ log.time|ago }}</span>
|
||||
|
@ -45,7 +45,7 @@
|
||||
</td>
|
||||
{% if mod|hasPermission(config.mod.remove_notes) %}
|
||||
<td class="minimal">
|
||||
<a href="?/IP/{{ ip|url_encode(true) }}/remove_note/{{ note.id }}">
|
||||
<a href="?/IP/{{ ip|cloak_ip|url_encode(true) }}/remove_note/{{ note.id }}">
|
||||
<small>[{% trans 'remove' %}]</small>
|
||||
</a>
|
||||
</td>
|
||||
@ -102,7 +102,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans 'IP' %}</th>
|
||||
<td>{{ ban.mask }}</td>
|
||||
<td>{{ ban.cmask }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans 'Reason' %}</th>
|
||||
@ -169,7 +169,7 @@
|
||||
{% if mod|hasPermission(config.mod.ban) %}
|
||||
<fieldset>
|
||||
<legend>{% trans 'New ban' %}</legend>
|
||||
{% set redirect = '?/IP/' ~ ip ~ '#bans' %}
|
||||
{% set redirect = '?/IP/' ~ ip|cloak_ip ~ '#bans' %}
|
||||
{% include 'mod/ban_form.html' %}
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
|
@ -1,3 +1,3 @@
|
||||
{% if post.mod and post.mod|hasPermission(config.mod.show_ip, board.uri) %}
|
||||
[<a class="ip-link" style="margin:0;" href="?/IP/{{ post.ip }}">{{ post.ip }}</a>]
|
||||
[<a class="ip-link" style="margin:0;" href="?/IP/{{ post.ip|cloak_ip }}">{{ post.ip|cloak_ip }}</a>]
|
||||
{% endif %}
|
||||
|
Loading…
Reference in New Issue
Block a user