Identify Tor Requests On Your Site

Tor is widely known as a great tool for those who respects their privacy. But sometimes hackers use Tor for spamming or DDOS. Luckily, Tor is much easier to block than other open proxies since the list of exit IP addresses is known and published. So if you want to block users from accessing your site you could use TorDNSEl or the Bulk Exit List.

If you use the Bulk Exit List exporting tool you need to get a fresh list often and expire the old blocks since the list of IP addresses change. With the TorDNSEl you did not have such problems, you can check any ip address and be sure that the result is fresh and valid.

TorDNSEl Description

Let's imagine a following scenario to clarify how TorDNSEl works. You host a web-application on a server with the IP address 1.2.3.4 on port 6667. A user visit your web-application with the IP 81.169.137.209. You want to detect if user IP address comes from the Tor network or not. TorDNSEl will return you true if there is a Tor node that can exit through 81.169.137.209 to port 6667 at 1.2.3.4, else it will return false. TorDNSEl is based on DNS Black List technology, so under the hood you need to query DNS for the A record "209.137.169.81.6667.4.3.2.1.ip-port.exitlist.torproject.org". If the result is true you will receive 127.0.0.2, else the TorDNSEl returns NXDOMAIN.

The algorithm is straightforward, I use node.js to implement it. The result listed below:

import dns from 'dns'  
import ipaddr from 'ipaddr.js'

const TOR_EXIT_LIST_DNS = 'ip-port.exitlist.torproject.org'

export const isFromTor = (sourceIp, destIp, destPort) => new Promise((resolve, reject) => {  
    // Code to validate input params omitted for clarity

    sourceIp = ipaddr.process(sourceIp)
    destIp = ipaddr.process(destIp)
    destPort = parseInt(destPort, 10)

    const result = {
        sourceIp: sourceIp.toString(),
        destIp: destIp.toString(),
        destPort: destPort.toString()
    }

    sourceIp = sourceIp.toByteArray().reverse().join('.')
    destIp = destIp.toByteArray().reverse().join('.')

    // quering dns
    dns.lookup(`${sourceIp}.${destPort}.${destIp}.${TOR_EXIT_LIST_DNS}`, (err, address, family) => {
        if (err) {
            if (err.code && err.code == 'ENOTFOUND') {
                resolve({ ...result, found: false })
                return
            }
            reject(err)
            return
        }

        resolve({ ...result, found: true })
    })
})

DeTor

To make things even simpler, we together with colleague created a simple microservice with REST API to identify Tor requests - DeTor. The source code is freely available on GitHub.

You can install it via Docker image

docker run -p 80:80 -e port=80 -d --name=detor rd17/detor  

The requests format is:
curl -X GET 'http://detor.ambar.cloud/?sourceIp=2.3.4.5&destIp=1.2.3.4&destPort=8080'

The response format is:

{
    "sourceIp": "104.200.20.46",
    "destIp": "89.207.89.82",
    "destPort": "8080",
    "found": true
}

Detor details and full documentation can be found on GitHub page.

Hope it helps you to protect your site from Tor attacks. Stay tuned!