Python で REST API を叩く時は requests ライブラリを使うが、最近の REST API は HTTPS をメインに使うようになった。これに伴い SSLError というよくわからんエラーが出るように。
このエラーは何を意味するのか。どう回避すればいいのか。その辺を知るヒントとして、requests 公式ドキュメントを意訳したもの+α をまとめてみた。
requests ドキュメントの意訳
SSL Cert Verification
Advanced Usage — Requests 2.18.4 documentation
SSL 認証に絡む挙動や使い方。
- requests はウェブブラウザみたくSSL認証(SSL証明書を用いた認証)を行う
- デフォでは SSL 認証は有効になってて、認証にしくじったら SSLError 例外を投げる
SSL 認証の指定は verify 引数にて行う。
- verify 引数が False だと認証を行わない
- verify 引数が True だと認証を行う(デフォルト)
- verify 引数が「認証局によって認証された CA_BUNDLE ファイルパス」だとそれを使って認証を行う
- あるいは
REQUESTS_CA_BUNDLE
環境変数に指定してもいい
- あるいは
- verify 引数が「認証局によって認証された CA_BUNDLE ファイル、のあるディレクトリ」だと OpenSSL の c_rehash 関数 と同じ動きで認証を行う
- 要するにディレクトリ中の証明書ファイル達を使う、というやり方もできるということ
Client Side Certificates
Advanced Usage — Requests 2.18.4 documentation
SSL 認証とは話が逸れるが「クライアント認証」についての話。
SSL 認証は「クライアントが、サーバーに対して、"あなたは信頼できる相手なんですよね?"」を知るための仕組みだが、クライアント認証はその逆で「サーバーが、クライアントに対して "あなたは信頼できる利用者なんですよね?"」を知るための仕組み。つまりは相互認証という、より堅固な認証のお話であって、SSL 認証とは関係が無い話題。
CA Certificates
- Advanced Usage — Requests 2.18.4 documentation
-
Python requestsライブラリは認証局の証明書をどう管理する? | Developers.IO
- こっちが詳しい
requests が証明書ファイルをどうやって管理しているかという話。
まず昔の話:
- 昔(v2.16以前) は Mozilla が作ってる証明書ファイルをバンドルしていた
- でもこの方法だと、Mozilla 側の更新を取り込めない(requests にバンドルしてる側の証明書ファイルはどんどん古くなっていく)
更新を取り込むにはどうしたらいい?と考えて、requests が取ったのは以下の仕組み。
- 証明書ファイル更新の仕組みを certifi という別パッケージに分離した
- requests は certifi を使って証明書ファイルをゲット&更新している
- 証明書ファイルを更新したい場合、自分で certifi パッケージをアップデートしてね
- コマンドで言うと
pip install -U certifi
- コマンドで言うと
Q: 結局 SSLError を逃れるためには要するにどうすればええの?
解1: verify=False をセットする
てっとり早い方法。
ただしこれは「SSL 認証はしません」というやり方であるため、通信先が本当に信用できる場合以外は使わないこと。もし信用しちゃいけない相手に対して verify=False しちゃうと(もっと言うとその先の HTTPS 通信でプライベートなデータを渡しちゃうと)最悪、通信先の悪者に悪用されちゃう。
解2: verify='SSL証明書のパス' をセットする
SSL証明書、サーバ証明書、ルート証明書、など呼び方は色々あるが、とにかく「SSL認証で使う証明書」を指定する。そうしたら、指定したその証明書の範囲で SSL 認証をしてくれる。
証明書は CA_BUNDLE という「信頼できる証明書のリスト」的なデータを使うのがてっとり早い。もし会社業務などで別途証明書のインポートを指示されてるなら、その証明書も追加してやる。
証明書ファイルは大体 .pem ファイルか .crt ファイル。中身は公開鍵の羅列。
ああ、あと CA_BUNDLE の入手先はたとえば以下。
おわりに
SSLやら証明書やら自体のお勉強ノートははてなブログに書いた
これでもう迷わないはず……。