V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
adrianzhang
V2EX  ›  Python

不行了,找遍了国外社区都解决不掉,召唤大神的出现。python requests 在 Debian/Ubuntu 上无法进行 TLS 握手?

  •  
  •   adrianzhang · 2015-09-30 22:37:06 +08:00 · 4047 次点击
    这是一个创建于 3361 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有个用 Requests 的脚本,访问 https://dictation.nuancemobility.net 这个 URL 。但是奇怪的是在 CentOS 可以正常工作,从服务器获得 200 响应,放到 Debian/Ubuntu 上就只能从服务器获得 500 响应。
    这个代码如下:

    <pre><code>

    class Nuance_api():

    def __init__(self, appid, appkey, n_id):
                self.appid = appid
                self.appkey = appkey
                self.id = n_id
    
        def recogenize(self, language, voicefile):
                nuance_url = 'https://dictation.nuancemobility.net/NMDPAsrCmdServlet/dictation'
                n_params = {'appId':self.appid, 'appKey':self.appkey, 'id':self.id}
                voice_file = {'file': open(voicefile, 'rb')}
                n_headers = {'Accept-Language':language, 'Content-Type':'audio/amr;codec=amr;bit=4.75;rate=8000', 'Accept':'text/plain', 'Accept-Topic':'Dictation'}
                r = requests.post(url=nuance_url, params=n_params, headers=n_headers, files=voice_file, verify=False)
                if r.status_code == 200 :
                        return r.text
    

    </code></pre>

    由于涉及到隐私的 key 等信息,单用这段代码测试比较麻烦。可以说明的是,这段代码在 CentOS 没有任何问题,可以返回识别的 text.

    那么,事情就集中到: Debian/Ubuntu 上的 requests 为什么就不能像 CentOS 一样工作?

    经过一些 debug ,这个服务器使用 TLS v1.2 或 v1 来进行握手,那么我甚至将代码改写为强制使用 TLS v1.2 ,在 Debian/Ubuntu 上同样失败。抓包显示,握手后服务端连续发来 4 个 hello ,但是在 CentOS 上的抓包显示为握手后就开始传输数据了。这表明握手并不成功(至少我这么认为)。

    有些文章说 Debian/Ubuntu 上的 openssl 默认不设定任何 CA bundle ,但是 Requests 使用的是自己的 CA bundle ,为了测试,特意在代码中将 verify='我的 cacert.pem',当我的 cacert.pem 里没有任何 root certificate 的时候,确实是 ssl verify failure ,加上该网站证书链里的顶级 VeriSign Class 3 证书,就可以通过验证。所以, CA bundle 不是症结。

    那么,究竟为什么。到底 Debian/Ubuntu 上使用 Requests 的正确姿势是什么呢?

    盼望这里有大神有些许经验。 Stackoverflow, GitHub 在这个问题上都阵亡了。满世界都是 ssl 验证不通过的问题,但是能有效解决的很少。涉及 requests ,那就更少了。

    第 1 条附言  ·  2015-10-01 01:13:23 +08:00
    谢谢大伙!问题被 Requests 作者解决了。因为 Requests 更改了对于对象的识别,这个 voice 对象应该写为 data 而不是 file 。所以,这个奇怪的问题跟系统环境证书什么的都没关系。 TLS v1.2 的握手是正确的。真是好大一个坑啊,足足 debug 了一周。
    21 条回复    2015-10-01 09:31:08 +08:00
    msg7086
        1
    msg7086  
       2015-09-30 22:50:41 +08:00   ❤️ 1
    握手失败的时候竟然能从服务器那边获取到 500 错误?手握在什么东西上了?
    pc10201
        2
    pc10201  
       2015-09-30 22:56:35 +08:00
    zjxubinbin
        3
    zjxubinbin  
       2015-09-30 23:02:29 +08:00
    @msg7086 应该是握在蛋上面了~
    alexapollo
        4
    alexapollo  
       2015-09-30 23:05:52 +08:00
    pip install pyopenssl ndg-httpsclient pyasn1
    可以试试
    187j3x1
        5
    187j3x1  
       2015-09-30 23:09:01 +08:00
    这跟放一段伪代码有什么区别
    mengzhuo
        6
    mengzhuo  
       2015-09-30 23:14:51 +08:00 via iPhone   ❤️ 1
    ca 库地址设置
    python 编译时的 openssl 版本
    ryd994
        7
    ryd994  
       2015-09-30 23:31:06 +08:00 via Android   ❤️ 1
    @pc10201
    @mengzhuo
    能获得 500 就根本不是 SSL 错误
    HTTP500 是 7 层的啊

    不加任何 header ,找个 https 的网站试试,随便找个 https 的登录页面
    Strikeactor
        8
    Strikeactor  
       2015-09-30 23:37:21 +08:00   ❤️ 1
    @msg7086 JB 上
    逃(
    adrianzhang
        9
    adrianzhang  
    OP
       2015-09-30 23:38:00 +08:00
    @msg7086 说的有道理。那请教一下这个握手之后,服务器为什么会发过来 4 个 hello ?一定有什么不同寻常的事情发生了。

    @pc10201 关闭了也不行,代码里可以看到 verify=False 的。这是最初的代码,不通过后才想尽办法加 ca 等,开启验证找问题的。


    @alexapollo 谢谢。我去试试看。


    @187j3x1 你说咋办?把 id 和 key 都放上来?这个代码很特殊在于有一定的 headers 和 parameters ,以及传输文件,如果用别的网站做演示的话,没法复现错误啊。


    @mengzhuo Requests 使用的是自己带的 CA bundle ,在 /usr/local/lib/python2.7/site-packages/requests/cacert.pem 。用 verify=开关设置为自己定的 pem ,这个实验在问题中提到了。 python 和 openssl 版本是这样的:
    1) 从 ISO 安装的 ElementoryOS Luna (基于 Ubuntu 12.02 )
    2) 或 Freya(基于 Ubuntu 14.04 TLS)
    3) 官方 Docker python 2.7.10 镜像(基于 Debian)
    这三个环境都是从 ISO 安装后未作任何特殊设置。因此 python 以及 openssl 版本等信息可以从这些环境里得到。比如直接 run 一个 docker python 2.7.0 。请看下 CentOS, python 2.6.6, OpenSSL 1.0.1e-fips 11 Feb 2013 。

    刚才又做了个测试,在 Debian/Ubuntu 上,使用 curl 是成功的,问题一定出在 python, openssl, requests 这几个中间。
    adrianzhang
        10
    adrianzhang  
    OP
       2015-09-30 23:38:57 +08:00
    @ryd994 如果使用 https://www.google.com ,是没有任何问题的。这个特殊情况只出现在这个特殊场景下。
    adrianzhang
        11
    adrianzhang  
    OP
       2015-09-30 23:58:44 +08:00
    @alexapollo 仍然无法获取正确响应。
    adrianzhang
        12
    adrianzhang  
    OP
       2015-09-30 23:59:39 +08:00
    @zjxubinbin
    @Strikeactor
    大神们我错了行不?给个建设性意见好不好?
    binux
        13
    binux  
       2015-10-01 00:04:57 +08:00
    UA
    binux
        14
    binux  
       2015-10-01 00:11:22 +08:00   ❤️ 1
    requests 版本差异没见提到
    azurefang
        15
    azurefang  
       2015-10-01 00:18:35 +08:00
    楼主你试着装一下 python2.7.9
    alexapollo
        16
    alexapollo  
       2015-10-01 00:22:53 +08:00
    https://dictation.nuancemobility.net/ 怎么普通的点进去都是 500 。。而且你没看 500 的完整回复吗?
    我猜是有什么特殊的配置写在代码里了。
    adrianzhang
        17
    adrianzhang  
    OP
       2015-10-01 00:36:47 +08:00
    @binux UA 应该不是问题,因为同样的代码在 CentOS 上是 work 的。 requests 在 debian 里是 requests==2.7.0
    在 centos 上是 requests==1.2.3

    @azurefang 谢谢,我一会儿测试下 2.7.9

    @alexapollo 是的,代码没有给出必要的 credentials ,因为涉及隐私内容没法放到公共空间里,这个完整 URL 是有 headers, parameters, file, 请看代码。有空我去申请个测试的帐号。

    感谢各位,现在联系到了 Requests 的作者,正在跟他沟通做 debug 。如果各位还有什么需要我提供的也请继续回复,我会持续 post.
    binux
        18
    binux  
       2015-10-01 00:45:43 +08:00   ❤️ 2
    @adrianzhang requests 默认 UA 会带上系统信息的, 而且你这个版本差太多了啊!
    adrianzhang
        19
    adrianzhang  
    OP
       2015-10-01 01:13:50 +08:00
    @binux 谢谢了!还真是版本差异产生的,见题目上我的补充。
    dingyaguang117
        20
    dingyaguang117  
       2015-10-01 06:31:10 +08:00 via iPhone
    没有用 requirements 并且固定版本号的习惯呀。。。

    之前同样的代码, python 版本不一样也会有问题。。。
    adrianzhang
        21
    adrianzhang  
    OP
       2015-10-01 09:31:08 +08:00
    @dingyaguang117 说起来一肚子苦水。

    事情原本是这样的:标的环境是那个 Debian ,固定版本号的各种组件。在这个环境里写的脚本不 work 。为了找原因,用替换法,换个环境,结果就恰巧换到了那个 CentOS 。而脚本在 CentOS 上 work ,把我引入歧途,误以为问题出在 Debian 的某些配置上,既不是 Nuance 方面的问题也不是脚本的问题。所以一直纠结在 Debian 环境的排错上,所以才一周时间都徘徊在死胡同里。

    跟作者沟通,他也直说了,即使是老版本,也需要用 data 对象,至于为什么 CentOS 上能工作,这对他来说也是一个谜。可能会涉及到更底层的一些东西,比如 python 的处理或者 Linux 本身的处理。这个没有时间去具体分析,等项目结束有空的时候,再去深挖一下到底是什么情况。太罕见了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   909 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 20:05 · PVG 04:05 · LAX 12:05 · JFK 15:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.