不適切な証明書検証

不適切な証明書検証

説明

SSL/TLS証明書を正しく検証しないアプリケーションでは、通信を傍受できる攻撃者が接続相手になりすまし、データを読み取ったり改ざんしたりするおそれがあります。

想定される影響

  • 中間者攻撃: クライアントとサーバーの通信が傍受・改ざんされるおそれがあります。
  • 情報漏えい: 機密情報が攻撃者に渡る可能性があります。
  • 完全性の損失: 通信の機密性と完全性が損なわれるおそれがあります。

対処方法

  • SSL/TLS証明書の検証を有効にしてください。
  • Pythonの requests などのライブラリを使い、証明書検証を正しく設定してください。
  • 信頼する認証局(CA)が発行した有効な証明書を使ってください。

例

次の抜粋は証明書の設定に焦点を当てています。CAファイルやクライアント証明書のパスは、環境に合わせて用意してください。サーバーの認証には証明書チェーンとホスト名の検証が必要であり、クライアント証明書の提示では代用できません。

Standard Library - SSL

変更前

python
# 証明書検証を無効にしたsslコード
import ssl

unsafe_ctx_1 = ssl._create_stdlib_context()
unsafe_ctx_2 = ssl._create_unverified_context()

unsafe_ctx_3 = ssl.create_default_context()
unsafe_ctx_3.check_hostname = False
unsafe_ctx_3.verify_mode = ssl.CERT_NONE

変更後

# 証明書検証を有効にしたsslコード
import ssl

safe_ctx_1 = ssl.create_default_context()
safe_ctx_1.verify_mode = ssl.CERT_REQUIRED

safe_ctx_2 = ssl._create_default_https_context()

説明:

  • 変更前: ssl._create_stdlib_context()、ssl._create_unverified_context()、またはverify_modeへのssl.CERT_NONEの指定によって証明書検証を無効にしています。例では CERT_NONE を設定する前にホスト名検証も無効にしています。
  • 変更後: 既定のクライアントコンテキストで証明書チェーンとホスト名を検証します。ソケットをラップする際には、正しい server_hostname も渡してください。

OpenSSL

変更前

python
# 証明書検証を無効にしたOpenSSLコード
from OpenSSL import SSL

unsafe_ctx_1 = SSL.Context(SSL.TLSv1_2_METHOD)

unsafe_ctx_2 = SSL.Context(SSL.TLSv1_2_METHOD)
unsafe_ctx_2.set_verify(SSL.VERIFY_NONE)

変更後

python
# 証明書検証を有効にしたOpenSSLコード
from OpenSSL import SSL

ctx = SSL.Context(SSL.TLSv1_2_METHOD)
ctx.set_default_verify_paths()
ctx.set_verify(SSL.VERIFY_PEER)

説明:

  • 変更前: set_verifyで接続相手の検証を適切に設定していないか、SSL.VERIFY_NONEで無効にしています。
  • 変更後: 既定の信頼ストアを読み込み、SSL.VERIFY_PEER で証明書チェーンを検証します。ホスト名の検証も別途構成してください。独自のコールバックを使う場合は、検証失敗を無視しないでください。VERIFY_FAIL_IF_NO_PEER_CERT などのクライアント認証を要求するオプションは、サーバーの用途に合わせて選択してください。

Requests

変更前

python
# 証明書検証を無効にしたRequestsコード
import requests

requests.get('https://example.com', verify=False)

変更後

python
# 証明書検証を有効にしたRequestsコード
import requests

requests.get('https://example.com')

信頼するプライベートCAの証明書を使う例です。

python
# 信頼するプライベートCAを指定したRequestsコード
import requests

requests.get('https://private.com', verify="/path/to/private_cert")

説明

  • 変更前: verify=False で証明書検証を無効にしているため、中間者攻撃を許すおそれがあります。
  • 変更後:
    • verify=False を削除するか、verify=True を指定して検証を有効にしてください。
    • プライベートCAを使う場合は、信頼するCAバンドルのパスを verify に指定してください。任意の証明書を信頼したり検証を無効にしたりしてエラーを回避しないでください。

HTTPX

変更前

python
# 証明書検証を無効にしたHTTPXコード
import httpx

httpx.get('https://example.com', verify=False)

変更後

python
# 証明書検証を有効にしたHTTPXコード
import httpx

httpx.get('https://example.com')

信頼するプライベートCAの証明書を使う例です。

python
# 信頼するプライベートCAを指定したHTTPXコード
import ssl
import httpx

ctx = ssl.create_default_context(cafile="/path/to/private_cert")
httpx.get('https://private.com', verify=ctx)

サーバーがクライアント認証を要求する場合は、次のようにクライアント証明書を追加します。サーバー証明書を信頼するためのCA設定とは別のものです。

python
import ssl
import httpx

ctx = ssl.create_default_context()
ctx.load_cert_chain(certfile="path/to/client.pem")
client = httpx.Client(verify=ctx)
response = client.get("https://example.org")

説明

  • 変更前: verify=False で証明書検証を無効にしています。
  • 変更後:
    • サーバー証明書の検証を有効に保ってください。
    • プライベートCAを信頼する場合は、そのCAを読み込んだ SSLContext を verify に渡してください。クライアント証明書と秘密鍵は、クライアントの身元をサーバーに示すための別の設定です。

aiohttp

変更前

python
# 証明書検証を無効にしたaiohttpコード
import aiohttp

async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get("https://example.com", ssl=False):
            pass
        async with session.get("https://legacy.example.com", verify_ssl=False):
            pass

変更後

python
# 証明書検証を有効にしたaiohttpコード
import aiohttp

async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get("https://example.com"):
            pass

説明

  • 変更前: ssl=False または verify_ssl=False で証明書検証を無効にしています。
  • 変更後:
    • これらのオプションを削除するか、必要に応じて信頼する ssl.SSLContext を渡してください。非同期コンテキストマネージャーが要求を実行し、応答を管理します。

urllib3

変更前

python
# 証明書検証を無効にしたurllib3コード
import urllib3

http = urllib3.PoolManager(cert_reqs='CERT_NONE')
http.request('GET', 'https://example.com')

変更後

python
# 証明書検証を有効にしたurllib3コード
import urllib3

http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs='/path/to/ca-bundle.crt')
http.request('GET', 'https://example.com')

説明:

  • 変更前: cert_reqs='CERT_NONE' で証明書検証を無効にしています。
  • 変更後: cert_reqs='CERT_REQUIRED' を指定してください。プライベートCAを使う場合は、信頼する証明書バンドルのパスを ca_certs に指定してください。

参考資料