4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Python requestsモジュール:`auth`引数がない場合、`.netrc`の認証情報を参照する

Last updated at Posted at 2018-09-25

環境

  • Python3.6.5
  • requestsモジュール 2.19.1
  • Windows10

やりたいこと

Pythonのrequestsモジュールを使って、認証が必要なサイトにアクセスしたいです。
アクセスしたいサイト(以下、サイトA)での認証は、以下の手順で行われます。

  1. クライアントはサーバに、ログイン用のリクエストを投げる。ログイン用のリクエストにはユーザIDとパスワードを渡す。
    ⇒ サーバからトークンを受け取る。
  2. クライアントはサーバに、認証が必要なリクエストを投げる。その際Authorizationヘッダにトークンを設定する。
    ⇒ サーバからレスポンスを受け取る。

実施したこと

Authorizationヘッダの設定

手順2の処理は、以下のようなコードを書きました。

import requests
# サイトAのURL
sample_url = "https://example.com"
sample_token = "SAMPLE_TOKEN"
res = requests.get(sample_url, headers={"Authorization": sample_token})

ユーザIDとパスワードを.netrcに記載

サイトAのユーザIDとパスワードは、%USERPROFILE%\.netrcに書きました。

.netrcはfptコマンドのための設定ファイルです。
このファイルにユーザIDとパスワードを書いておくと、fptコマンドを実行するときに自動でログインされます。https://linuxjm.osdn.jp/html/netkit/man5/netrc.5.html
また、fptコマンドだけでなくgitコマンドでも自動ログインされます。
https://qiita.com/azusanakano/items/8dc1d7e384b00239d4d9

Pythonには、標準モジュールに.netrcファイルを扱うnetrcモジュールがあります。
パスワードをファイルに保存する場合、.netrcでなく、自分で指定した設定ファイルに書くという選択肢もありましたが、なんとなく.netrcの方が扱いやすいと思い、.netrcを選択しました。1

.netrcファイルの中身と、netrcを読み込むPythonのコードです。

.netrc
machine example.com
login xxxxxx
password yyyyyy
netrc_hosts = netrc.netrc().hosts
host = netrc_hosts['example.com']
user_id = host[0]
password = host[2]

login(user_id, password)

問題:Authorizationヘッダが設定されない

トークンをAuthorizationヘッダに設定しているのにもかかわらず、認証エラーが発生しました。
リクエストのAuthorizationヘッダを確認したところ、 'Basic eHh4eHh4Onl5eXl5eQ=='という値で、"SAMPLE_TOKEN"ではありませんでした。

import requests
# サイトA
sample_url = "https://example.com"
sample_token = "SAMPLE_TOKEN"

##### `.netrc`にユーザ情報が記載されているとき
res = requests.get(sample_url, headers={"Authorization": sample_token})
print(res.request.headers['Authorization'])
# ⇒ 'Basic eHh4eHh4Onl5eXl5eQ=='

##### `.netrc`にユーザ情報が記載されていないとき
res = requests.get(sample_url, headers={"Authorization": sample_token})
print(res.request.headers['Authorization'])
# ⇒ 'SAMPLE_TOKEN'

AuthorizationヘッダのBasic は、Basic認証を表します。
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Authorization
Basic 以降の文字列をBase64でデコードすると、ユーザIDとパスワードが表示されました。

import base64
print(base64.b64decode("eHh4eHh4Onl5eXl5eQ=="))
# ⇒ b'xxxxxx:yyyyyy'

.netrcに記載されたユーザIDとパスワードで、Basic認証ヘッダが送信されたようです。

原因

requestsモジュールの仕様でした。

If no authentication method is given with the auth argument, Requests will attempt to get the authentication credentials for the URL’s hostname from the user’s netrc file. The netrc file overrides raw HTTP authentication headers set with headers=.
If credentials for the hostname are found, the request is sent with HTTP Basic Auth.

  • 引数authに認証メソッドが渡されない場合、netrcファイルから認証情報を取得する
  • netrcファイルは、認証ヘッダを上書きする
  • ホスト名に対応する認証が見つかったら、HTTP Basic認証でリクエストが送られる

今回のコードでは、引数authを使っていなかったため、netrcファイルの認証情報が優先されました。

解決方法

1. auth引数

認証クラスを作成して、そのクラスのインスタンスをauth引数に渡す

from requests.auth import AuthBase

class SimpleAuth(AuthBase):
    def __init__(self, token):
        self.token = token

    def __call__(self, r):
        r.headers['Authorization'] = self.token
        return r
        
##### `.netrc`にユーザ情報が記載されているとき
res = requests.get(sample_url, auth=SimpleAuth(sample_token))
print(res.request.headers['Authorization'])
# ⇒ 'SAMPLE_TOKEN'

http://docs.python-requests.org/en/master/user/advanced/#custom-authentication 参照

2. Sessionクラスのtrust_env

Sessionクラスのtrust_envFalseにすると、.netrcを無視します。

Trust environment settings for proxy configuration, default authentication and similar.

s = requests.Session()
s.trust_env = False
res = s.get(sample_url, headers={"Authorization": sample_token})

Sessionインスタンスでしか設定できないので、根本的に解決する場合は、解決方法1が良いです。

まとめ

デフォルトで「netrcの設定ファイルを読み込む」というよく分からない仕様にハマりました。
この仕様は、GitHubのイシューでも議論されていました。

However, here's a framework I'd consider for handling auth in the 3.0 branch. I'd welcome a PR to make this the case.

  1. If the user sets an Authorization header themselves, either via the request or on the Session, we don't bother to look at the netrc file.
  2. If they didn't, we look at the netrc file for basic auth.
  3. If we get redirected, we fall back to only looking at the netrc file (which we already currently do).

もしかしたら、バージョン3では「Authorizationヘッダが設定されていれば、netrcを見ない」という仕様になっているかもしれません。

備考

認証情報をnetrc設定ファイルに書くことについて

Twelve-Factor App」というSoftware as a Serviceを作り上げるための方法論には、「設定を環境変数に格納する」という考えがあります。

Twelve-Factor Appは設定を 環境変数 に格納する。 環境変数は、コードを変更することなくデプロイごとに簡単に変更できる。設定ファイルとは異なり、誤ってリポジトリにチェックインされる可能性はほとんどない。また、独自形式の設定ファイルやJava System Propertiesなど他の設定の仕組みとは異なり、環境変数は言語やOSに依存しない標準である。

そもそも、認証情報をnetrc設定ファイルに書かない方がよさそうです。

  1. パスワードを平文で保存しているこ書いていていることには、目をつぶってください…

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?