なぜこれが重要なのか:私たちは Geonode というプロキシ販売業者であり、認証情報を含むコマンドを数多く目にするからです。多くの場合、同じ行にプロキシのパスワードとターゲットのパスワードの両方が記載されています。 まず最初に述べておくべき実用的な警告として、curl コマンド内の認証情報は、シェル履歴やプロセス一覧、さらにはサポートチケットに貼り付ける内容など、あらゆる場所に記録されてしまうということです。 実際のパスワードが記載されたスクリーンショットを、私たちは何度も受け取っています。以下の「.netrc」という手法を使えば、約1分でこの問題を無料で解決できます。この方法はプロキシの認証情報にも同様に適用でき、その点については記事の最後の方で説明します。
基本的な構文
curl -u username:password https://api.example.com/private
curl マニュアル には、-u, --user <user:password>
について次のように記載されています。「サーバー認証に使用するユーザー名とパスワードを指定します。」
Basicはデフォルトの認証方式であるため、--basic
は通常、冗長です。マニュアルにも次のように記載されています。「リモートホストに対してHTTP Basic認証を使用します。この方式はデフォルトであり、以前に設定された異なる認証方式を上書きする場合を除き、このオプションは通常無意味です。」
実際にネットワーク上で送信されるのは、次のようなヘッダーです:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
これは username:password
を Base64 エンコードしたものです — 暗号化ではなく、エンコードされています。 リクエストを閲覧できる者なら誰でも、1 ステップで復号できます。これが、プレーン HTTP 上の Basic 認証がパスワードを平文で送信することと同等であり、HTTPS 上でしか使用すべきでない理由です。
マニュアルに記載されている構文上の制約として、「ユーザー名とパスワードは最初のコロンで区切られるため、このオプションではユーザー名にコロンを使用することはできません。ただし、パスワードにコロンを含めることは可能です。」とあります。つまり、パスワード内のコロンは問題ありませんが、ユーザー名内のコロンは使用できません。
なぜコマンドラインは不適切な場所なのか
マニュアルはこの点について明確に述べています:
動作するシステムにおいて、curl は指定されたオプション引数をプロセス一覧から非表示にします。しかし、これだけでは、同じシステム上の他のユーザーに認証情報が閲覧されるのを防ぐには不十分です。なぜなら、消去されるまでの間、一瞬だけ表示されてしまうからです。 このような機密データは、代わりにファイルなどから取得すべきであり、コマンドライン上で平文として使用してはなりません。
以下に、実際に発生した4つの情報漏洩事例を挙げます:
シェル履歴。 ~/.bash_history や zsh の同等の機能により、平文のまま無期限に保存されます。
プロセス一覧。 curl が消去するまでの短い間、そのマシン上の他のユーザーから閲覧可能です。
ログ。 スクリプトが実行したコマンドを記録するあらゆるもの。
貼り付けられた出力。 バグ報告、課題追跡システム、チャットメッセージ、スクリーンショット。
最後の項目は、実際には最も一般的でありながら、最も見過ごされがちなものです。
3つのより安全な方法
1. curlにプロンプトを表示させる。 ユーザー名のみを指定すると、curlが対話形式でパスワードを要求し、エコー表示せずに読み取ります:
curl -u username https://api.example.com/private
何も保存されず、ログにも記録されません。手動で入力するあらゆる操作において、これが適切な方法です。
**2. ``.netrc`
ファイルを使用する。** マニュアルでは、-n, --netrc`
について次のように説明しています。「curl がユーザーのホームディレクトリにある .netrc ファイルをスキャンして、ログイン名とパスワードを検索するようにします……HTTP で使用する場合、curl はユーザー認証を有効にします。」
~/.netrc
を作成します:
machine api.example.com
login myusername
password mypassword
その後、アクセス権を制限してください。curl は自動的にこれを行ってくれないためです。マニュアルには、「そのファイルに適切なアクセス権が設定されていなくても、curl はエラーを報告しません(ファイルは、世界およびグループによる読み取り権限を持たない必要があります)」と記載されています:
chmod 600 ~/.netrc
curl -n https://api.example.com/private
マニュアルから3つの有用な詳細情報です。「netrcファイルは、使用されるプロトコルやポート番号に関係なく、ホスト名に対する認証情報を提供します」。つまり、1つのエントリで1つのホストをカバーできます。--netrc-file
「ファイルの特定を行う他のすべての方法を上書きします」。これは、プロジェクトごとの認証情報ファイルとして便利です。 また、curl 8.16.0 以降では、NETRC
という環境変数でファイル名を指定できます。Windows では、ホームディレクトリ内の .netrc
と _netrc
の両方がチェックされ、前者が優先されます。
--netrc-optional
は、ファイルが存在すればそれを使用し、存在しなくてもエラーを出さない方式です。どちらの状態でも実行される可能性のあるスクリプトに適しています。
3. 環境変数から読み込む。 ファイルの使用が現実的でない場合は、少なくとも実行履歴に残らないようにしましょう:
read -rs API_PASS
curl -u "myuser:${API_PASS}" https://api.example.com/private
read
での -s
に注意してください。これにより、パスワードが画面に表示されなくなります。ただし、これはプロセスの環境変数には依然として表示されるため、理想的な方法というよりは妥協案と言えます。
スクリプトの場合は、``.netrc`
に ``chmod 600
` を組み合わせるのが正解です。これはマニュアルでも推奨されているオプションであり、上記で挙げたすべての情報漏洩リスクを排除できるものです。
Basicとその他の認証方式の比較
curlはいくつかの認証方式をサポートしており、それぞれの違いを理解しておくことで、ある種の混乱を防ぐことができます。
| オプション | 認証方式 | 通信中のパスワード |
|---|---|---|
--basic | HTTP Basic(デフォルト) | Base64エンコード済み、実質的に平文 |
--digest | HTTP Digest | ハッシュ化されたチャレンジ・レスポンス |
--ntlm | NTLM | Windows 環境 |
--negotiate | SPNEGO / Kerberos | チケットベース |
--anyauth | 自動 | 選択内容による |
--oauth2-bearer | ベアラートークン | トークンそのもの |
Digest については、次のように記載されています。「HTTP ダイジェスト認証を有効にします。この認証方式では、パスワードが平文でネットワーク上を送信されるのを防ぎます。ユーザー名とパスワードを設定するには、通常の --user オプションと組み合わせて使用してください。」
--anyauth は利便性を重視したオプションですが、マニュアルにはその代償について次のように明記されています: 「認証方法を自動的に判別し、リモートサイトがサポートすると主張する最も安全な方法を使用します。これは、まずリクエストを行い、レスポンスヘッダーを確認することで行われるため、余分なネットワーク往復が発生する可能性があります。」
リクエストごとに余分な往復が発生すると、処理量が増えるとコストがかさみます。また、マニュアルでは次のような特定の失敗について警告しています:「stdinからのアップロードを行う場合、--anyauthの使用は推奨されません。データが2回送信される可能性があり、その場合、クライアントはデータを巻き戻すことができなければなりません。stdinからのアップロード時にこの必要が生じた場合、アップロード操作は失敗します。」
したがって、サーバーが何を求めているか本当に分からない場合は--anyauthを使用し、判明した時点でスキームを指定してください。
ベアラー・トークンは、最近のAPIの多くで実際に使用されているものであり、これはベーシック認証とは全く異なります:
curl --oauth2-bearer "mF_9.B5f-4.1JqM" https://api.example.com/me
これは、手動で Authorization: Bearer ... を設定するのと同じです。ベアラー・トークン自体が認証情報であり、それを所持している人なら誰でも使用できるため、パスワードと同様に慎重に取り扱う必要があります。
レスポンスの読み取り
認証が失敗した際の簡単なトラブルシューティング手順。
**401 Unauthorized
** は、サーバーが認証情報を要求しているか、送信した認証情報を拒否したことを意味します。レスポンスには、サーバーが期待する認証方式を指定する ``WWW-Authenticate`
` ヘッダーが含まれており、これを確認すれば当て推量を避けることができます:
curl -sS -o /dev/null -D - https://api.example.com/private | grep -i www-authenticate
もし ``Digest`
と記載されており、あなたがBasic` を送信していた場合、それが答えです。
**403 Forbidden
** はこれとは異なり、よく誤解されます。認証は成功しましたが、この操作を行う権限がありません。パスワードを変更しても解決しません。権限の設定を変更すれば解決する可能性があります。
**407 Proxy Authentication Required
** は、ターゲットではなく プロキシ が認証情報を要求していることを意味します。ヘッダーもオプションも異なり、これについては次に説明します。
**ログインページが表示される 200
** は、エンドポイントが HTTP 認証を一切使用していないことを意味します。フォームとセッション Cookie を使用しており、-u
では何も処理されません。Content-Type
を確認してください。JSON を期待していたのに text/html
が返ってきた場合、おそらくこれが原因です。
実際に送信した内容を確認するには:
curl -v -u user:pass https://api.example.com/private 2>&1 | grep -i '^> authorization'
マニュアルにある「詳細な出力には、ユーザー名、認証情報、または機密データなどの機密情報が含まれている可能性がある」という警告を忘れないでください。共有する前に、機密情報を伏せてください。
ヘッダーを手動で作成する
場合によっては、-u
では期待通りの結果が得られないことがあります。この関数が実際にどのような出力を生成するかを理解しておけば、その制限を回避することができます。
ユーザー名にコロンが含まれている場合。 -u
は最初のコロンで分割するため、service:reader
のようなユーザー名は表現できません。ヘッダーを直接作成してください:
CRED=$(printf '%s' 'service:reader:mypassword' | base64 -w0)
curl -H "Authorization: Basic ${CRED}" https://api.example.com/private
echo
ではなく、printf
を使用してください。 では改行が追加され、それがエンコードされた認証情報の中に含まれてしまうため、不可解な 401 エラーが発生します。また、base64 -w0
を使用することで行の折り返しを防ぎます。GNU base64
はデフォルトで 76 文字で行を折り返しますが、改行が埋め込まれたヘッダーは不正なリクエストとみなされます。macOS では、base64
を使用すれば行が折り返されないため、このフラグは不要です。
シークレットマネージャーから認証情報が必要な場合。 ほとんどのシークレット管理ツールは標準出力(stdout)に出力するため、値をファイルではなく変数に保持することで、その有効期間を制限できます:
TOKEN=$(vault kv get -field=token secret/api)
curl -H "Authorization: Bearer ${TOKEN}" https://api.example.com/me
コマンドではなく設定ファイルにヘッダーを記述したい場合。 curlは、~/.curlrc
または -K
で指定されたファイルからオプションを読み取ります:
# api-auth.conf
--user "myuser:mypassword"
--header "Accept: application/json"
curl -K api-auth.conf https://api.example.com/private
chmod 600
でファイルを制限します。これは、.netrc
が適さない場合(例えば、ユーザー名とパスワードではなくトークンが必要な場合など)の妥当な妥協案となります。
特に ~/.curlrc
に関する注意点。 これは、そのユーザーによる すべての curl 実行に適用されます。あなたが記述していないものも含みます。そこに認証情報を記述すると、スクリプトがリクエストするあらゆるホストにその情報が送信されることになります。機密性の高い情報については、-K
を使用して名前付きファイルを利用し、~/.curlrc
は --show-error
や --location
のような無害なデフォルト設定に留めておいてください。
プロキシ認証は別途行われる
当社のサポート窓口において、最も混乱を招いている点です。
2つの独立した認証情報セットが関与している可能性があります。1つはプロキシ用、もう1つはターゲット用です。これらは異なるヘッダー、異なるステータスコード、異なるcurlオプションを使用します。
curl -x http://proxy.example.com:9000 \
--proxy-user proxyuser:proxypass \
-u apiuser:apipass \
https://api.example.com/private
--proxy-user
はプロキシに対して認証を行い、-u
はターゲットに対して認証を行います。これらを混同すると、401を期待していたのに407が返されたり、その逆の現象が発生したりします。
プロキシ側にも、ターゲット側の設定と対応する並行したオプションがあります — --proxy-basic
、--proxy-digest
、--proxy-anyauth
、--proxy-negotiate
。
実用上の注意点として2点あります。
プロキシURL内の認証情報には、前述の露出問題に加え、さらに1つの問題があります。 -x http://user:pass@proxy:9000
を使用すると、パスワードがコマンドライン上 および プロキシURLを格納する環境変数内に含まれてしまいます。http_proxy
は通常、この環境変数に格納されます。 パスワード内の @
、:
、または /
はパーセントエンコードしてください。そうしないと、URL パーサーが誤った場所で分割してしまいます。
推測するのではなく、どのホップで拒否されたかを区別してください:
curl -sS -o /dev/null -x "$PROXY" \
-w 'connect=%{http_connect} status=%{response_code}\n' \
https://api.example.com/private
connect=407
は、プロキシが接続を拒否し、ターゲットに到達しなかったことを意味します。connect=200 status=401
は、プロキシは正常に機能したが、ターゲット側が認証情報を要求していることを意味します。対処法はそれぞれ異なります。
また、プロキシ認証に関する注意点として、多くのプロバイダでは、ユーザー名とパスワードの代わりにIPアドレスの許可リストを提供しています。送信元アドレスが固定されている場合、これによりコマンドから認証情報を完全に排除できます。これは、ファイルも環境変数も不要で、情報漏洩のリスクもない、最もクリーンな解決策です。
サイトがHTTP認証をまったく使用していない場合
「curlの基本認証が機能しない」という報告の多くは、そもそもそのサイトがHTTP認証を一切使用していないケースです。
見分ける方法。 認証情報なしで保護されたURLにリクエストを送信し、レスポンスを確認してください:
curl -sS -o /dev/null -D - https://example.com/dashboard
WWW-Authenticate
ヘッダーを含む401
はHTTP認証を意味し、-u
が適切なツールです。ログインページを返す200
や、/login
にリダイレクトする302
は、そのサイトがフォームとセッションクッキーを使用していることを意味します。この場合、-u
を何度試しても効果はありません。なぜなら、そのヘッダーを読み取る仕組みがないからです。
代わりに「フォームログイン」パターンを採用しましょう。 認証情報をログインエンドポイントにPOSTし、クッキーを保持して再利用します:
curl -c jar.txt -d "username=ada&password=secret" \
https://example.com/login
curl -b jar.txt https://example.com/dashboard
-c
はクッキーを書き込み、-b
はクッキーを読み取ります。多くのサーバーがそうであるように、サーバーがセッションクッキーをローテーションする場合、その後のリクエスト(-b jar.txt -c jar.txt
)では両方のクッキーを使用してください。
直面するであろう問題:CSRFトークン。 ほとんどのログインフォームには、認証情報とともに送信しなければならない非表示のトークンが含まれており、これはセッションごとに生成されます。つまり、2段階の処理が必要になります。フォームを取得し、トークンを抽出し、ステップ1で取得したクッキーとともに送信します:
TOKEN=$(curl -sS -c jar.txt https://example.com/login \
| grep -o 'name="csrf_token" value="[^"]*"' \
| cut -d'"' -f4)
curl -b jar.txt -c jar.txt \
-d "csrf_token=${TOKEN}" -d "username=ada" -d "password=secret" \
https://example.com/login
HTMLに対するgrepとcutによる処理は不安定であり、一度きりの診断には適していますが、継続的な処理には不向きです。継続的な処理を行う場合は、まずそのサービスがトークン認証に対応したAPIを提供しているかどうかを確認してください。ほとんどの場合、提供されており、独自のログインフォーム用スクレイパーを維持管理するよりも、はるかに手間が省けます。
また、ログインに JavaScript が必要な場合、curl ではまったく処理できません。これは回避すべき curl の制限ではなく、ページ自体が呼び出している API エンドポイントを探す合図です。そのエンドポイントはブラウザのネットワークタブで確認でき、直接再現することができます。
よくある質問
curl で基本認証を使用するにはどうすればよいですか?
curl -u username:password URL。基本認証は curl のデフォルトの認証方式であるため、以前に設定されたメソッドを上書きする場合を除き、--basic は不要です。基本認証では認証情報が暗号化されるのではなく Base64 エンコードされるため、常に HTTPS を使用してください。
curl でパスワードの入力を促すにはどうすればよいですか?
ユーザー名のみを指定します:curl -u username URL。curl は対話形式でパスワードを要求し、それをエコーしないため、シェル履歴やプロセス一覧に記録されることはありません。手動で入力する場合は、これが適切な方法です。
curl の基本認証は安全ですか?
HTTPS経由の場合のみ安全です。認証情報はBase64エンコードされますが、これは簡単に復号できるため、プレーンHTTP経由では事実上平文と同じ状態になります。TLS経由であれば、トランスポート層によって保護されますが、残りのリスクは自身のマシン上で認証情報をどこに保存するかという点にあります。
curlの認証情報をファイルに保存するにはどうすればよいですか?
~/.netrc を使用し、machine、login、password の各行を追加します。その後、chmod 600 を実行し、curl を -n で呼び出します。curl は権限設定の誤りについて警告しないため、権限の設定はユーザー自身の責任となります。--netrc-file は代替の保存先を指定し、--netrc-optional はファイルが存在しない場合にエラーが発生しないようにします。
パスワードが正しいのに、なぜ curl は 401 を返すのですか?
いくつかの可能性があります。サーバーが異なるスキームを期待している場合(WWW-Authenticateヘッダーを確認してください)、エンドポイントがHTTP認証ではなくクッキーを使用したフォームログインを採用している場合、あるいはユーザー名にコロンが含まれている場合です。-uは最初のコロンで分割するため、コロンを含むユーザー名を表現できません。
401と403の違いは何ですか?
401は、認証されていないことを意味します。つまり、認証情報が存在しないか、間違っているということです。403は、認証は成功したものの、その操作が許可されていないことを意味します。異なる認証情報で再試行すると、前者の場合は有効ですが、後者の場合は無効です。
curlでプロキシへの認証を行うにはどうすればよいですか?
--proxy-user user:password を使用します。これは、ターゲットへのアクセス用である -u とは別のものです。 ステータスコード 407 はプロキシが認証情報を要求していることを示し、401 はターゲットが要求していることを示します。送信元アドレスが固定されている場合は、代わりにプロバイダに IP の許可リスト登録について相談してください。これにより、コマンドから認証情報を完全に排除できます。
--anyauth オプションは何をしますか?
このオプションを指定すると、curlはリクエストを送信してレスポンスヘッダーを読み取り、サーバーが推奨するスキームを検出し、提供されている中で最も安全な方法で認証を行います。その代償として、リクエストごとに追加の往復通信が発生します。また、マニュアルでは、stdinからのアップロード時にデータが2回送信される可能性があるため、失敗する恐れがあると警告されています。
まとめ
構文はすぐに理解できます:-u username:password と入力すれば、curl のデフォルト方式である Basic 認証で認証されます。注意すべき点は、パスワードの扱い方です。
curlのマニュアルには、この点について「認証情報はファイルなどから取得すべきであり、コマンドライン上で平文で使用してはならない」と明記されており、そこで警告されている情報漏洩のリスクはすべて現実のものなのです。 シェルの履歴には認証情報が無期限に保存され、プロセス一覧表示ではマシン上の誰にでも一時的に認証情報がさらされ、ターミナル出力をコピー&ペーストすると、意図しない場所に到達してしまうという不気味な傾向があります。
これを防ぐには、2つの習慣を身につければ十分です。対話型で使用する場合は、ユーザー名のみを入力し、curlにプロンプトを表示させます。スクリプトの場合は、認証情報を~/.netrcにchmod 600で記述し、-nを使用します。どちらも、パスワードを手入力するよりも時間はかかりません。
また、2つの認証レイヤーを明確に区別してください。401エラーはターゲット側から返され、-uを要求します。407エラーはプロキシ側から返され、--proxy-userを要求します。アドレスが固定されている場合は、許可リスト(allowlist)を使用することで、コマンドから2つ目の認証情報を完全に排除できます。これが、機密情報を保管する唯一の真に安全な場所です。
