Would it be possible to block Tor users? (https://www.torproject.org/)
Due to the nature of the site I run I should do all I can to stop multiple accounts and block cert
Detecting Tor traffic is rather easy. The main way to do this is to monitor the Tor exit node list and compare the IP against the list.
I had the need to do such a thing recently and built a small Ruby gem to keep the list of exit nodes up to date and provide a simple way to detect exit nodes. I also wrote a small executable you can use to detect exit nodes.
The gem is open source and can be found here: tor-guard
Installing the gem is simple enough:
$ gem install tor-guard
Using the library in your own Ruby code can be done as follows:
require 'tor-guard'
if TorGuard.exit_node?('108.56.199.13')
puts "Yep, it's an exit node!"
end
The executable is also easy to use:
$ tg 108.56.199.13 && echo "Yep, it's an exit node"
(This was written for a PHP specific question that was subsequently deleted and linked here as a duplicate).
Disclaimer: Consider the impact of blocking all Tor users as raised in the best answer here. Consider only blocking functions such as registration, payment, comments etc and not a blanket block on everything.
--
Here are two pure PHP solutions. The first downloads and caches a Tor node list and compares the visitor IP against the list. The second uses the Tor DNS Exit List project to determine if the visitor is using Tor via DNS lookups.
Using the following set of functions we can determine if an IP belongs to the Tor network by checking it against a dynamic exit list that gets downloaded and cached for 10 minutes. Feel free to use this list but please cache for 10 minutes when possible.
Where you want to enforce the Tor check, you can simply use:
$isTorUser = isTorUser($_SERVER['REMOTE_ADDR']);
if ($isTorUser) {
// blocking action
}
Here is the code which you can put in a separate functions file and include when you want to run the check. Note, you may want to adjust some of it to change the path to the cache file.
<?php
function isTorUser($ip)
{
$list = getTorExitList();
if (arrayBinarySearch($ip, $list) !== false) {
return true;
} else {
return false;
}
}
function getTorExitList()
{
$path = __DIR__ . '/tor-list.cache';
if ( file_exists($path) && time() - filemtime($path) < 600 ) {
$list = include $path;
if ($list && is_array($list)) {
return $list;
}
}
$data = file('https://www2.openinternet.io/tor/tor-exit-list.txt');
if (!$data) {
return array();
}
$list = array();
foreach($data as $line) {
$line = trim($line);
if ($line == '' || $line[0] == '#') continue;
list($nick, $ip) = explode("\t", $line);
$list[] = $ip;
}
sort($list);
file_put_contents($path, sprintf("<?php return %s;", var_export($list, true)));
return $list;
}
/**
* Perform binary search of a sorted array.
* Credit: http://php.net/manual/en/function.array-search.php#39115
*
* Tested by VigilanTor for accuracy and efficiency
*
* @param string $needle String to search for
* @param array $haystack Array to search within
* @return boolean|number false if not found, or index if found
*/
function arrayBinarySearch($needle, $haystack)
{
$high = count($haystack);
$low = 0;
while ($high - $low > 1){
$probe = ($high + $low) / 2;
if ($haystack[$probe] < $needle){
$low = $probe;
} else{
$high = $probe;
}
}
if ($high == count($haystack) || $haystack[$high] != $needle) {
return false;
} else {
return $high;
}
}
The DNS exit check is a bit more robust in that it takes into account the relay's exit policy and looks at what IP and port on your server the client is connecting to and if such exit traffic is permitted, it will return a match. The potential downfall is that if the DNS project is down temporarily, DNS requests can hang before timing out slowing things down.
For this example, I will use a class from a library I wrote and maintain called TorUtils.
First, you'll need to install it with Composer using composer require dapphp/torutils
and include the standard vendor/autoloader.php
code in your application.
The code for the check: $isTor = false;
try {
// check for Tor using the remote (client IP)
if (TorDNSEL::isTor($_SERVER['REMOTE_ADDR'])) {
// do something special for Tor users
} else {
// not using Tor, educate them! :-D
}
} catch (\Exception $ex) {
// This would likely be a timeout, or possibly a malformed DNS response
error_log("Tor DNSEL query failed: " . $ex->getMessage());
}
if ($isTor) {
// blocking action
}
If your application uses PHP sessions, I'd highly suggest caching the "isTorUser" response into the session (along with the source IP) and only run the check initially or when the IP changes (e.g. $_SERVER['REMOTE_ADDR'] != $_SESSION['last_remote_addr']
) as not to perform many duplicated lookups. Even though they try to be very efficient, it's a waste to do over and over for the same IP.
Here (see https://github.com/RD17/DeTor) is a simple REST API to determine whether a request was made from TOR network or not.
The request is:
curl -X GET http://detor.ambar.cloud/
.
The response is:
{
"sourceIp": "104.200.20.46",
"destIp": "89.207.89.82",
"destPort": "8080",
"found": true
}
As a bonus you can add a badge to your site to detect whether a user comes from TOR or not:
<img src="http://detor.ambar.cloud/badge" />
It is possible due to the tor project publishing a list of exit proxies.
The list of exit proxies can be downloaded directly from the project at https://check.torproject.org/exit-addresses in space delimited text form.
I have written a python script to add iptables rules for all exit nodes that reject all packets from them. You can find the script on github here: https://github.com/vab/torblock
If the Tor Project ever decides to stop publishing a list of exit nodes it will be possible to block them. Code would just need to be written to connect to the tor network and discover the exit nodes.
The Tor Project actually provides its own list here:
https://check.torproject.org/exit-addresses
Blocking Tor is wrong because (ab)users and IP addresses are not the same. By blocking Tor you will also block legitimate users and harmless restricted Tor exit nodes configured with conservative exit policies.
For example, if you concerned about attacks on SSH (port 22) then blocking only Tor will do little to increase security. What you really might need is dynamic synchronised blacklist like http://denyhosts.sourceforge.net/ that track offenders disregarding of their affiliation with Tor.
Denyhosts will automatically block Tor exit nodes that allow Tor to access port 22 without unnecessary denying access to anonymous users and operators of Tor exit nodes who never let offenders to attack your SSH services.