stylesheet

2022-09-18

Python SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED エラーの回避策

URLError: <urlopen error [SSL:
UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation
disabled (_ssl.c:1131)>

上記は、セキュリティリスクのあるhttpsアドレスを開いたときに発生するエラーとなる。
SSLプロトコルの不具合で、中間者攻撃に使用される脆弱性のためopenssl側で無効化されている。OpenSSL 3.0.0からデフォルトで無効化された。

本来はサーバー側をアップデートするのが筋だが、なかなかそういうわけにもいかないことも多い。リスクを承知で、クライアント側の無効状態を解除する方法をメモ。

環境は以下の通り。

$ python -c 'import ssl; print(ssl.OPENSSL_VERSION)'
OpenSSL 3.0.2 15 Mar 2022

$ python --version
Python 3.8.10

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
Codename:       jammy

回避策A: opensslの設定変更

opensslの設定を変更する方法。 /etc/ssl/openssl.confを編集して、system_default_sectセクションにOptions = UnsafeLegacyRenegotiation設定を追加する。

# /etc/ssl/openssl.conf

...
[ssl_sect]
system_default = system_default_sect

[system_default_sect]
CipherString = DEFAULT:@SECLEVEL=2
Options = UnsafeLegacyRenegotiation

ただし、/etc/ssl/openssl.confを直接編集すると、システム全体に適用されてしまうので流石にそれはまずい。
実際は別の設定ファイルを用意して、環境変数でpythonプログラムへ渡してやると良い。

$ cp /etc/ssl/openssl.conf openssl_unsecure.conf
$ echo -e "Options = UnsafeLegacyRenegotiation\n" >> openssl_unsecure.conf
$ OPENSSL_CONF=/path/to/openssl_unsecure.conf python cherry.py

回避策B: プログラム改修

sslのオプションに0x4フラグを追加してやると回避できる。
開発版ではssl.OP_LEGACY_SERVER_CONNECT定数が追加されているが、現行バージョン3.10でも使用できないので直接、値を指定する。

import urllib.request
import ssl

ctx = ssl.create_default_context()
ctx.options |= 0x4  # ssl.OP_LEGACY_SERVER_CONNECT
resposne = urllib.request.urlopen('https://...', context=ctx)
...

build_openerを使う場合は、HTTPSHandlerを作成してやる。

opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))
...

開発版でのOP_LEGACY_SERVER_CONNECTについての記載。

.. data:: OP_LEGACY_SERVER_CONNECT

   Allow legacy insecure renegotiation between OpenSSL and unpatched servers
   only.

   This option is only available with OpenSSL 0.9.8m and later, and is disabled
   in default context since OpenSSL 3.0.0.

   .. versionadded:: 3.11

以上。
古いpython2向けのスクリプトをpython3向けへ移植している時に発生した。

参照