如何使用PHP中的cURL连接Tor隐藏服务?

How can I connect to a Tor hidden service using cURL in PHP?

我尝试使用以下PHP代码连接到Tor隐藏服务:

1
2
3
4
5
6
7
8
9
10
11
12
$url = 'http://jhiwjjlqpyawmpjx.onion/'
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PROXY,"http://127.0.0.1:9050/");
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
$output = curl_exec($ch);
$curl_error = curl_error($ch);
curl_close($ch);

print_r($output);
print_r($curl_error);

当我运行它时,我得到以下错误:

Couldn't resolve host name

但是,当我从Ubuntu中的命令行运行以下命令时:

1
curl -v --socks5-hostname localhost:9050 http://jhiwjjlqpyawmpjx.onion

我得到了预期的答复

php curl文档显示:

1
2
--socks5-hostname
Use  the  specified  SOCKS5 proxy (and let the proxy resolve the host name).

我相信它在命令行中工作的原因是Tor(代理)正在解析它识别的.洋葱主机名。当运行上面的PHP代码时,我猜想curl或php正在尝试解析.onion主机名,但无法识别它。我已经找到了一种方法来告诉curl/php让代理解析主机名,但是我找不到一种方法。

有一个非常类似的堆栈溢出问题:使用socks5代理的curl请求在使用php时失败,但它通过命令行工作。


看起来CURLPROXY_SOCKS5_HOSTNAME没有在php中定义,但是可以显式使用它的值,它等于7:

1
curl_setopt($ch, CURLOPT_PROXYTYPE, 7);

我用privoxy和curl来刮纸:

1
2
3
4
5
6
7
8
9
<?php
    $ch = curl_init('http://jhiwjjlqpyawmpjx.onion'); // Tormail URL
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
    curl_setopt($ch, CURLOPT_PROXY,"localhost:8118"); // Default privoxy port
    curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
    curl_exec($ch);
    curl_close($ch);
?>

安装privoxy后,需要将此行添加到配置文件(/etc/privoxy/config中)。注意空格和"."a是行尾。

1
forward-socks4a / localhost:9050 .

然后重启privoxy。

1
/etc/init.d/privoxy restart


尝试添加:

1
2
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);

tl;dr:设置CURLOPT_PROXYTYPE使用CURLPROXY_SOCKS5_HOSTNAME,如果您有现代php,则值7,否则,和/或更正CURLOPT_PROXY值。

正如您正确推断的那样,您不能通过普通的DNS系统解析.onion域,因为这是一个专门供Tor使用的保留顶级域,并且这些域设计时没有要映射到的IP地址。

使用CURLPROXY_SOCKS5将指示curl命令将其流量发送到代理,但不会对域名解析执行相同的操作。在curl尝试与洋葱站点建立实际连接之前发出的DNS请求仍将发送到系统的正常DNS解析程序。这些DNS请求肯定会失败,因为系统的正常DNS解析程序不知道如何处理.onion地址,除非它也专门将这些查询转发给tor。

您必须使用CURLPROXY_SOCKS5_HOSTNAME,而不是CURLPROXY_SOCKS5。或者,您也可以使用CURLPROXY_SOCKS4A,但是socks5是首选。这两种代理类型中的任何一种都通知curl执行其DNS查找和通过代理进行的实际数据传输。这是成功解析任何.onion域所必需的。

在最初的问题中,代码中还有两个额外的错误尚未被以前的注释者纠正。这些是:

  • 第1行末尾缺少分号。
  • 代理地址值设置为HTTP URL,但其类型为SOCKS;这些是不兼容的。对于SOCKS代理,该值必须是没有方案/协议/前缀的IP或域名和端口号组合。

这里是完整的正确代码,带有注释以指示更改。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$url = 'http://jhiwjjlqpyawmpjx.onion/'; // Note the addition of a semicolon.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PROXY,"127.0.0.1:9050"); // Note the address here is just `IP:port`, not an HTTP URL.
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME); // Note use of `CURLPROXY_SOCKS5_HOSTNAME`.
$output = curl_exec($ch);
$curl_error = curl_error($ch);
curl_close($ch);

print_r($output);
print_r($curl_error);

您也可以通过将CURLOPT_PROXY值更改为包含socks5h://前缀来完全忽略设置CURLOPT_PROXYTYPE

1
2
// Note no trailing slash, as this is a SOCKS address, not an HTTP URL.
curl_setopt(CURLOPT_PROXY, 'socks5h://127.0.0.1:9050');