使用openssl子进程将Python STDOUT输出到文件

3 投票
3 回答
4567 浏览
提问于 2025-04-16 21:37

我正在尝试写一个Python脚本,目的是自动检查SSL重新协商的过程,并把结果输出到一个文件里。不过,我遇到了两个问题。

第一个问题是,初始握手的输出结果被写入了文件,但实际的重新协商部分却没有。它反而是在控制台上显示出来。

subprocess.call("echo \"R\" | openssl s_client -connect example.com:443", 
        shell=True, stdout=FILE)

我的另一个问题(虽然这可能不是合适的地方)是,我无法让openSSL命令正常工作,以发送GET命令。

subprocess.call("echo -e \"GET / HTTP/1.1\r\n\r\n\" | openssl s_client -connect
        example.com:443", shell=True)   

再次强调,初始连接是建立起来了,但之后openSSL就退出了,没有处理GET请求。

如果能得到任何帮助,我将非常感激。谢谢。

3 个回答

1

@phihag 我对你的脚本做了一些小改动,现在运行得很好。

import subprocess

f = open('http_answer', 'w')
_,log = subprocess.Popen(
    ['openssl', 's_client', '-quiet', '-connect', 'twitter.com:443','-sess_out', 'session.txt'],
    stdout=f, stderr=subprocess.PIPE, stdin=subprocess.PIPE ).communicate('GET / HTTP/1.0\r\nHOST: twitter.com\r\n\r\n') print('Output of SSL:\n' + log)

这里列出了一些改动:
1. 添加了'sess_out'和'session.txt'这两个参数,这样可以保存所有的SSL会话参数,可以用下面的openssl命令查看这些参数。

$openssl sess_id -in test.txt -text -cert -noout
  1. 在GET命令中添加了主机信息,因为新的部分只有在使用HOST选项时才能正常响应。

    'GET / HTTP/1.0\r\nHOST: twitter.com\r\n\r\n'

@Drew,虽然有点晚了,但我希望这些信息能对你有点帮助。谢谢。

1

请记住,openssl s_client 在输出时也会使用错误输出(stderr)。你需要检查一下重新协商的信息是否会输出到错误输出,我觉得应该是会的,不过我可能记错了。

我用不同的方法实现了这个功能,不过不是用 Python。我创建了一个进程,并把标准输入、标准输出和错误输出的文件描述符连接到我可以读写的地方,这样我就能控制输入并读取输出。虽然这样做稍微麻烦一点,但你可以完全掌控进程的运行情况。我是在 PHP 中实现的,测试结果可以在网上找到,链接是 http://netsekure.org/2009/11/tls-renegotiation-test/

另外,你也可以尝试用 Python 来直接编程 openssl,而不是使用 s_client,但这样工作量会更大,我之前用的是第一种方法。

你可以检查两件事情,但你没有说明你对哪一件感兴趣:

  • 检查远程服务器是否支持客户端发起的重新协商
  • 检查远程服务器是否支持安全的重新协商扩展

这两者都可以通过简单地使用 s_client 并用 grep 查找相关的关键词来推断出来。具体取决于你需要多少控制和复杂性。

6

对于输入来说,没必要使用 shell=True。可以直接用 stdin=subprocess.PIPE。另外,你的请求不太对,因为HTTP 1.1要求必须有 Host 这个头信息。此外,我想不出有什么理由要用命令行的openssl,而不用ssl模块

话虽如此,这里有一个可以运行的例子:

import subprocess

f = open('http_answer', 'w')
_,log = subprocess.Popen(
    ['openssl', 's_client', '-quiet', '-connect', 'twitter.com:443'],
    stdout=f, stderr=subprocess.PIPE, stdin=subprocess.PIPE
).communicate('GET / HTTP/1.0\r\n\r\n')
print('Output of SSL:\n' + log)

撰写回答