使用openssl子进程将Python的标准输出重定向到文件

3
我正在尝试编写一个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请求。

非常感谢您的帮助。谢谢。


不使用 Python SSL 封装的原因是什么?也许你可以使用它来获得更好的调试信息。这只是一个想法。 - krs1
3个回答

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)

我尝试了使用http 1.0和1.1,但似乎仍然得到了之前相同的结果。GET请求仍未被处理。编辑:我没有使用ssl模块,因为我们在这里使用openSSL进行了很多不同的测试,并且我想保持一致性。 - Drew
@phihag 如果正在处理GET请求,它不会将响应写入http_answer文件吗?http_answer文件具有握手,然后说完成,其中没有响应。 - Drew
@Drew 抱歉,我的错;我忘记了加上 -quiet 选项。你能用更新后的代码再试一次吗? - phihag
@phihag,太棒了,完美运作。我知道这不在原始问题的范围内,但你有什么想法可以将重新协商请求链接到GET请求上吗?我尝试使用GET / HTTP/1.0 \r\nR\r\n,但它没有起作用。 - Drew
根据文档,当使用-quiet参数时,这是不可能的。看起来你正在尝试做高级SSL操作。为此,我建议直接使用libssl。你可以通过ctypes库访问Python中的本地代码。 - phihag
显示剩余4条评论

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 输出一些内容。你需要检查是否重新协商已转到 stderr,我认为是这样,虽然我的记忆可能正在逐渐消退。
我已经以不同的方式完成了这个过程,但不是用 python。我创建了一个进程并将 stdin、stdout 和 stderr 文件描述符连接到可以读写的文件描述符上,并实际驱动输入和读取输出。这需要更多的工作,但您完全控制正在发生的事情并与进程交互。我在 php 中做到了这一点,并且测试可以在线使用 http://netsekure.org/2009/11/tls-renegotiation-test/
或者,你可以尝试使用 python 编程 openssl 本身,而不是使用 s_client,但这需要更多的工作,而我使用了之前的方法。
有两件事情你可以检查,你没有清楚地表明你感兴趣的是哪一个:
- 检查远程服务器是否支持客户端启动的重新协商。 - 检查远程服务器是否支持安全重新协商扩展。

这两者都可以通过使用 s_client 并使用适用于两种情况的关键字进行全文搜索来简单推断出来。这完全取决于您需要多少控制/复杂性。


我正在尝试测试服务器是否允许客户端启动重新协商。你关于stderr的说法是正确的。谢谢! - Drew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接