2022年 11月 10日

python-stem Tutorial(4) 隐藏服务

隐藏服务(Hidden Services): ——乘船渡彼岸

官方教程网址:https://stem.torproject.org/tutorials.html

隐藏服务顾名思义,就是可以为你提供一种不会暴露本人地址的服务。这些服务只能通过Tor或者Tor2web进行访问,足以让你大开眼界。
1,托管一个匿名网站。这大概是我们想到的第一件事,我将会很快将他演示。
2,在不退出Tor网络的前提下,提供一个Tor使用者能够访问的终端。我们将排除不可靠的或者恶意退出等的风险。本文以FacebookDuckDuckGo为例。
3,私人服务。比如你可以托管的自己的SSH服务作为一个隐藏服务,用以防止自己地址信息的泄露。
Tor2web提供了一个快速而简单的方式来兼职你的隐藏服务是否在工作。只需要用.tor2web.org来代替你的.onion地址
这里写图片描述

一,运行一个隐藏服务
隐藏服务可以通过torrc进行配置,但是Stem提供了一些更为简易的方法。

 1   create_hidden_service()
 2   remove_hidden_service()
 3   get_hidden_service_conf()
 4   set_hidden_service_conf()
  • 1
  • 2
  • 3
  • 4

运行隐藏服务时匿名的最主要的威胁来源于服务本身。例如,调试信息可能会泄露你真正的地址,破坏Tor所提供的便利。下面是一个示例,别指望它不泄露。
尽管如此,让我们看一下下面这个示例,基于Jordan Wright:

import os
import shutil

from stem.control import Controller
from flask import Flask

app = Flask(__name__)


@app.route('/')
def index():
  return "<h1>Hi Grandma!</h1>"


print(' * Connecting to tor')

with Controller.from_port() as controller:
  controller.authenticate()

  # All hidden services have a directory on disk. Lets put ours in tor's data
  # directory.

  hidden_service_dir = os.path.join(controller.get_conf('DataDirectory', '/tmp'), 'hello_world')

  # Create a hidden service where visitors of port 80 get redirected to local
  # port 5000 (this is where Flask runs by default).

  print(" * Creating our hidden service in %s" % hidden_service_dir)
  result = controller.create_hidden_service(hidden_service_dir, 80, target_port = 5000)

  # The hostname is only available when we can read the hidden service
  # directory. This requires us to be running with the same user as tor.

  if result.hostname:
    print(" * Our service is available at %s, press ctrl+c to quit" % result.hostname)
  else:
    print(" * Unable to determine our service's hostname, probably due to being unable to read the hidden service directory")

  try:
    app.run()
  finally:
    # Shut down the hidden service and clean it off disk. Note that you *don't*
    # want to delete the hidden service directory if you'd like to have this
    # same *.onion address in the future.

    print(" * Shutting down our hidden service")
    controller.remove_hidden_service(hidden_service_dir)
    shutil.rmtree(hidden_service_dir)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

下面开始运行:

% python example.py
 * Connecting to tor
 * Creating our hidden service in /home/atagar/.tor/hello_world
 * Our service is available at uxiuaxejc3sxrb6i.onion, press ctrl+c to quit
 * Running on http://127.0.0.1:5000/
127.0.0.1 - - [15/Dec/2014 13:05:43] "GET / HTTP/1.1" 200 -
 * Shutting down our hidden service
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如此,我们创建了一个可以通过Tor浏览器访问的服务。
这里写图片描述

二,短暂隐藏服务
在上面的示例中,你可能注意到了注释部分:

# The hostname is only available when we can read the hidden service
# directory. This requires us to be running with the same user as tor.
  • 1
  • 2

这是隐藏服务多年以来的局限性,然而,从0.2.7.1版本后,tor提供了一个名为短暂隐藏服务的服务。
短暂隐藏服务只能通过控制器创建,而且仅仅存在于你的控制器连接的时候,除非你提供分离标志。控制器仅仅能够监视他们自己的短暂服务以及分离的短暂服务。换句话说,连接的短暂服务仅仅能够被他们自己的控制器管理。
Stem提供了三种方法运行短暂隐藏服务:

    list_ephemeral_hidden_services()
    create_ephemeral_hidden_service()
    remove_ephemeral_hidden_service()
  • 1
  • 2
  • 3

下面是一个很简单的例子:

from stem.control import Controller
from flask import Flask

app = Flask(__name__)


@app.route('/')
def index():
  return "<h1>Hi Grandma!</h1>"


print(' * Connecting to tor')

with Controller.from_port() as controller:
  controller.authenticate()

  # Create a hidden service where visitors of port 80 get redirected to local
  # port 5000 (this is where Flask runs by default).

  response = controller.create_ephemeral_hidden_service({80: 5000}, await_publication = True)
  print(" * Our service is available at %s.onion, press ctrl+c to quit" % response.service_id)

  try:
    app.run()
  finally:
    print(" * Shutting down our hidden service")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

短暂隐藏服务不接触磁盘,因此更容易使用。如果你想要重复使用‘.onion’作为自己的地址,你需要牢记自己的服务密匙。

import os
from stem.control import Controller

key_path = os.path.expanduser('~/my_service_key')

with Controller.from_port() as controller:
  controller.authenticate()

  if not os.path.exists(key_path):
    service = controller.create_ephemeral_hidden_service({80: 5000}, await_publication = True)
    print("Started a new hidden service with the address of %s.onion" % service.service_id)

    with open(key_path, 'w') as key_file:
      key_file.write('%s:%s' % (service.private_key_type, service.private_key))
  else:
    with open(key_path) as key_file:
      key_type, key_content = key_file.read().split(':', 1)

    service = controller.create_ephemeral_hidden_service({80: 5000}, key_type = key_type, key_content = key_content, await_publication = True)
    print("Resumed %s.onion" % service.service_id)

  raw_input('press any key to shut the service down...')
  controller.remove_ephemeral_hidden_service(service.service_id)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

三,隐藏服务描述符
就像是继电器(relays?),隐藏服务发布关于他们自己的文档,叫做隐藏服务描述符。这包括建立连接的底层服务的细节。隐藏服务描述符可以通过方法get_hidden_service_descriptor()
从tor获得。

from stem.control import Controller

with Controller.from_port(port = 9051) as controller:
  controller.authenticate()

  # descriptor of duck-duck-go's hidden service (http://3g2upl4pq6kufc4m.onion)

  print(controller.get_hidden_service_descriptor('3g2upl4pq6kufc4m'))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

下面输出结果:

% python print_duck_duck_go_descriptor.py

rendezvous-service-descriptor e5dkwgp6vt7axoozixrbgjymyof7ab6u
version 2
permanent-key
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAJ/SzzgrXPxTlFrKVhXh3buCWv2QfcNgncUpDpKouLn3AtPH5Ocys0jE
aZSKdvaiQ62md2gOwj4x61cFNdi05tdQjS+2thHKEm/KsB9BGLSLBNJYY356bupg
I5gQozM65ENelfxYlysBjJ52xSDBd8C4f/p9umdzaaaCmzXG/nhzAgMBAAE=
-----END RSA PUBLIC KEY-----
secret-id-part bmsctib2pzirgo7cltlxdm5fxqcitt5e
publication-time 2015-05-11 20:00:00
protocol-versions 2,3
introduction-points
-----BEGIN MESSAGE-----
aW50cm9kdWN0aW9uLXBvaW50IHZzcm4ycGNtdzNvZ21mNGo3dGpxeHptdml1Y2Rr
NGtpCmlwLWFkZHJlc3MgMTc2LjkuNTkuMTcxCm9uaW9uLXBvcnQgOTAwMQpvbmlv
... etc...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

隐藏服务的说明节点是可能加密的base64编码字段。 这些可以使用描述符的introduction_points()方法进行解码(如果必要的话)进行解密。
示例代码如下:

from stem.control import Controller

with Controller.from_port(port = 9051) as controller:
  controller.authenticate()
  desc = controller.get_hidden_service_descriptor('3g2upl4pq6kufc4m')

  print("DuckDuckGo's introduction points are...\n")

  for introduction_point in desc.introduction_points():
    print('  %s:%s => %s' % (introduction_point.address, introduction_point.port, introduction_point.identifier))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

结果如下:

% python print_duck_duck_go_introduction_points.py

DuckDuckGo's introduction points are...

  176.9.59.171:9001 => vsrn2pcmw3ogmf4j7tjqxzmviucdk4ki
  104.131.106.181:9001 => gcl2kpqx5qnkpgxjf6x7ulqncoqj7ghh
  188.166.58.218:443 => jeymnbhs2d6l2oib7jjvweavg45m6gju
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7