説明
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に指定してください。