Python リクエストで SSL セキュリティ証明書チェックを無視する

Jay Shaw 2023年10月10日
  1. SSL セキュリティ チェックの背後にある理由と失敗する理由を理解する
  2. Python で SSL セキュリティ チェックを無視する
  3. まとめ
Python リクエストで SSL セキュリティ証明書チェックを無視する

安全な SSL 証明書を使用せずに URL にアクセスすると、HTTP 要求が URL に送信されるときに例外警告が発生します。 多くの場合、これらの URL の SSL 証明書は期限切れになり、あらゆる種類のセキュリティの問題が発生します。

情報が機密でない場合、プログラムが Python で request を使用すると、これらの警告を抑えることができます。 この記事では、requests を使用してセキュリティ証明書のチェックを無効にする複数の方法を紹介します。

SSL セキュリティ チェックの背後にある理由と失敗する理由を理解する

プログラムが Python requests を使用して、SSL 証明書の有効期限が切れている URL からリクエストを取得すると、2つの例外が発生します。 以下のプログラムは、それらの例外が何であるかを表示します。

シナリオ 1

このプログラムは、SSL コミュニティによって提供された、期限切れのセキュリティ証明書をテスト目的で使用する URL を使用します。 SSL セキュリティ例外は、期限切れの SSL 証明書を持つ URL でのみ発生することに注意してください。

Python の リクエスト は、有効な SSL 証明書または取り消された証明書を持つ URL で例外を発生させません。 そのため、この記事では主にセキュリティ証明書の有効期限が切れている URL に焦点を当てます。

以下の例は、最初の行で requests をインポートする簡単なプログラムを示しています。 プログラムの 2 行目は、post リクエストを URL に送信して、出現する 'bar''baz' に変更します。

post リクエストを URL に送信するために行われ、プログラム内でそれ以外の意味を持ちません。

import requests

requests.post(url="https://expired-rsa-dv.ssl.com/", data={"bar": "baz"})

この Python スクリプトを実行すると、SSLError 例外がスローされます。

....
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='expired-rsa-dv.ssl.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:841)'),))

これは、requests を使用してセキュリティ証明書のチェックを無効にする方法を学習する際に考慮すべき最初の例外です。

シナリオ 2

このプログラムは、verify=False を使用して SSL 証明書の検証をオフにし、requests を使用してセキュリティ証明書のチェックを無効にします。

import requests

requests.post(url="https://expired-rsa-dv.ssl.com/", data={"bar": "baz"}, verify=False)

requests ライブラリは、SSL 証明書の検証をオフにできるように構築されていますが、プログラムは期限切れの SSL 証明書を持つリンクで別の例外をスローします。

InsecureRequestWarning: Unverified HTTPS request is being made to host 'expired-rsa-dv.ssl.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
  InsecureRequestWarning,

これらの 2つの例外は、requests を使用してセキュリティ証明書のチェックを無効にするために処理する必要があります。

Python で SSL セキュリティ チェックを無視する

このセクションでは、requests を使用してセキュリティ証明書のチェックを無効にするか、問題を解決するさまざまな方法について説明します。 すべてのメソッドには目的があります。

requests ライブラリのモンキー パッチを作成する

サードパーティのライブラリでセキュリティ チェックを無効にする必要がある場合は、requests ライブラリに モンキー パッチを適用 できます。 コンテキスト マネージャーは、requests を使用したセキュリティ証明書のチェックを無効にするためのパッチ適用に使用されます。

requests がパッチされた後、verify フィールドにはデフォルトで False 値が与えられ、警告が抑制されます。 前のセクションのシナリオ 2 で説明したように、verify=false を使用すると、別の警告が発生します。

このパッチは、例外処理ブロックを追加して、requests を使用したセキュリティ証明書のチェックを無効にし、警告を抑制します。 次の例を使用すると、理解しやすくなります。

以下のプログラムは requests ライブラリにパッチを当てます。 このコードが何をするのかを理解しましょう。

輸入品:

  1. warnings: このライブラリ パッケージは Exceptions ライブラリのサブパッケージです。
  2. contextlib: この Python ライブラリは、requests ライブラリにパッチを適用するために使用されます。
  3. requests: Python の requests ライブラリ パッケージ。
  4. urllib3: Python で HTTP リクエストと URL を処理するモジュールです。 このライブラリは、期限切れの SSL 証明書に対して例外を発生させるサブモジュール InsecureRequestWarning をインポートするために使用されます。

パッチ:

最初に、プログラムは requests ライブラリのデフォルトの環境設定を変数 old_merge_environment_settings に保存します。 この変数は、開いているアダプターが閉じられた後、requests をデフォルトの状態に戻すために使用されます。

old_merge_environment_settings = requests.Session.merge_environment_settings

メソッド no_ssl_verification が作成され、@contextlib.contextmanager で装飾されます。 新しい変数 opened_adapters が作成され、set() がそれに割り当てられます。

セットは、アイテムを順序付けされていない形式で格納するデータ型です。

@contextlib.contextmanager
def no_ssl_verification():
    opened_adapters = set()

no_ssl_verification メソッド内で、別のネストされたメソッド merge_environment_settings が作成されます。 このメソッドには、requests モジュールの merge_environment_settings に似た 6つのパラメーターがあり、元のモジュールのパッチとして機能します。

def merge_environment_settings(self, url, proxies, stream, verify, cert):

メソッド内で、opened_adapters 変数は、パラメーター url からの一致するアダプター ペアで更新されます。 すべての接続は、このステップで返されるいくつかの一致するアダプター ペアで行われます。

検証は接続ごとに 1 回しか行われないため、終了後に開いているすべてのアダプターを閉じる必要があります。 そうでない場合、verify=False の効果は、このコンテキスト マネージャーが終了した後も持続します。

def merge_environment_settings(self, url, proxies, stream, verify, cert):

    opened_adapters.add(self.get_adapter(url))

コードの次の行を理解するには、記事の最初のセクションを覚えておくことが重要です。 期限切れの SSL 証明書を使用して URL で requests.post 関数を使用すると、2つの例外がスローされました。

最初の例外は、True 値で設定された verify によって引き起こされました。 verify フィールドは切り替え可能でしたが、False 値を与えることができました。

2 番目の例外は、変更不可能なエンティティであり、プログラムが接続を確立できなくなりました。

以下のコードは、この問題を解決するために、verify フィールドをデフォルトで False 値を持つように変更します。 このステップを実行するために新しい変数 settings が作成され、変数 old_merge_environment_settings からのデータが割り当てられます。

データが変数 settings 内に保存されると、verify フィールドは False になります。

これにより、verify のデフォルト値が変更されます。 最後に、この変数が返されます。

settings = old_merge_environment_settings(self, url, proxies, stream, verify, cert)
settings["verify"] = False

return settings

更新された merge_environment_settings メソッドからのデータは、関数 requests.Session.merge_environment_settings に割り当てられます。 これにより、フィールド verify がデフォルトで False 値を持つことが保証されます。

requests.Session.merge_environment_settings = merge_environment_settings

最初の例外を処理しました。 変更できない 2 番目の例外は、例外処理ブロックを使用して解決されます。

ここでコードが何をするかを理解しましょう。

最初に、try ブロック内で、発生したすべての警告をキャッチする with ブロックが作成されます。 この with ブロック内では、warnings.simplefilter 関数を使用して ignore 値を urllib3 サブモジュール InsecureRequestWarning に与えます。

インポートのセクションで学んだように、InsecureRequestWarning サブモジュールは期限切れの SSL 証明書に対して例外を発生させます。 ignore 値を割り当てると、2 番目に発生した例外がバイパスされます。

try:
    with warnings.catch_warnings():
        warnings.simplefilter('ignore', InsecureRequestWarning)
        yield

finally ブロック内では、old_merge_environment_settings 内に格納された元の設定が関数 requests.Session.merge_environment_settings に割り当てられます。

プログラムを終了する前に、開いているすべてのアダプターを閉じる必要があるため、プログラム内で開いているアダプターの回数だけ反復する for ループが作成されます。

すべてのアダプターは、関数 adapter.close() を使用して、try-except ブロックを使用して閉じられます。 except ブロック内で、反復が渡されます。

for adapter in opened_adapters:
    try:
        adapter.close()
    except:
        pass

完全なコードを以下に示します。

import warnings
import contextlib

import requests
from urllib3.exceptions import InsecureRequestWarning

old_merge_environment_settings = requests.Session.merge_environment_settings


@contextlib.contextmanager
def no_ssl_verification():
    opened_adapters = set()

    def merge_environment_settings(self, url, proxies, stream, verify, cert):

        opened_adapters.add(self.get_adapter(url))

        settings = old_merge_environment_settings(
            self, url, proxies, stream, verify, cert
        )
        settings["verify"] = False

        return settings

    requests.Session.merge_environment_settings = merge_environment_settings

    try:
        with warnings.catch_warnings():
            warnings.simplefilter("ignore", InsecureRequestWarning)
            yield
    finally:
        requests.Session.merge_environment_settings = old_merge_environment_settings

        for adapter in opened_adapters:
            try:
                adapter.close()
            except:
                pass

Python でモンキー パッチを使用する

モンキー パッチは、no_ssl_verification() メソッドを使用して呼び出すことができます。 このメソッドで送信されるリクエストはすべて、モンキー パッチの指示に従い、requests を使用してセキュリティ証明書のチェックを無効にします。

例を見てみましょう。

メソッド no_ssl_verificationwith ブロックの下で呼び出されます。 これにより、ブロック内でメソッドが実行され、コンパイラがブロックから出ると閉じます。

ブロック内で、期限切れの SSL 証明書を使用して、requests.get 呼び出しが URL に送信されます。 デフォルトの設定では、これにより例外がスローされますが、今回は requests.get 呼び出しが正常に送信されます。

requests で別のリクエストが送信され、verify フィールドが True に設定されます。 デフォルトのシナリオとは異なり、今回はエラー例外はスローされません。

with no_ssl_verification():
    requests.get("https://expired-rsa-dv.ssl.com/")
    print("Modifications working Properly")

    requests.get("https://expired-rsa-dv.ssl.com/", verify=True)
    print("`Verify=true` has its meaning changed")

出力:

Modifications working Properly

`Verify=true` has its meaning changed

Verify=False には、パッチをデフォルト設定にリセットするディレクティブが含まれていることがわかりました。 このフィールドが requests.get() 内で設定された場合、モンキー パッチはデフォルト設定にリセットされ、エラー例外をスローできるようになります。

requests.get("https://expired-rsa-dv.ssl.com/", verify=False)
print("It resets back")

出力:

connectionpool.py:1052: InsecureRequestWarning: Unverified HTTPS request is being made to host 'expired-rsa-dv.ssl.com'.
  InsecureRequestWarning,
It resets back

requests サブモジュール session は、monkey パッチでも使用できます。

session という名前の変数では、関数 requests.Session() がロードされます。 session.verify 値は True に設定されています。

with ブロック内では、session.get() 関数を使用して get リクエストを URL に送信し、verify フィールドを True に設定します。 print ステートメントは、接続が確立された場合にメッセージを出力します。

session = requests.Session()
session.verify = True

with no_ssl_verification():
    session.get("https://expired-rsa-dv.ssl.com/", verify=True)
    print("Works in Session")

出力:

Works in Session

コンテキスト マネージャーが終了すると、このコードは、パッチが適用された要求を処理する開いているアダプターをすべて閉じます。 requests はセッションごとに接続プールを維持し、証明書の検証は接続ごとに 1 回だけ行われるため、次のような予期しないイベントが発生します。

try:
    requests.get("https://expired-rsa-dv.ssl.com/", verify=False)
except requests.exceptions.SSLError:
    print("It breaks")

try:
    session.get("https://expired-rsa-dv.ssl.com/")
except requests.exceptions.SSLError:
    print("It breaks here again")

出力:

C:\Users\Win 10\venv\lib\site-packages\urllib3\connectionpool.py:1052: InsecureRequestWarning: Unverified HTTPS request is being made to host 'expired-rsa-dv.ssl.com'.
  InsecureRequestWarning,
It breaks here again

urllib3 を使用して Python で警告を無効にする

requests やモンキー パッチを使用せずにセキュリティ証明書のチェックを無効にする必要がある場合は、urllib3 を使用すると簡単な解決策が得られます。

requestsurllib3 をインポートし、urllib3 からサブモジュール InsecureRequestWarning をインポートします。 urllib3.disable_warnings 関数を使用して警告を無効にします。

requests.post() ステートメントは try ブロックの下に配置され、verify フィールドは False に設定されます。 これにより、期限切れのセキュリティ証明書のセキュリティ チェックが無効になります。

except ブロック内では、エラー メッセージとともに SSLError が発生します。

import requests
from urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

try:
    requests.post(
        url="https://expired-rsa-dv.ssl.com/", data={"bar": "baz"}, verify=False
    )
    print("Connection made successfully")

except requests.exceptions.SSLError:
    print("Expired Certificate")

出力:

Connection made successfully

まとめ

この記事では、Python で requests を使用してセキュリティ証明書のチェックを無効にするさまざまな方法について説明します。 読者は、この記事を通じて、セキュリティ チェックを簡単に無効にすることができます。

関連記事 - Python Requests