GitHub CLI (gh auth login) で Git の HTTPS 認証を安全にする

remote URL に PAT を埋め込んでいた状態を、gh auth login + credential helper に置き換える。devcontainer での認証共有もまとめる。

本記事は Ubuntu 24.04 を前提としています。

きっかけ

git remote -v を眺めていたら、Personal Access Token (PAT) が remote URL に丸ごと埋め込まれている状態になっていました。

origin  https://[email protected]/USER/REPO.git (fetch)
origin  https://[email protected]/USER/REPO.git (push)

.git/config はリポジトリに含まれないため「push しても外には出ない」ものの、ローカルで露出する経路は意外と多くあります。

  • シェル履歴 (~/.bash_history ~/.zsh_history)
  • エディタや CI のログ、画面共有・スクリーンショット
  • うっかり配布した dotfiles リポジトリ
  • マルウェアやリモートアクセスで .git/config が読まれる

しかも classic PAT はスコープが粗く、repo 権限があれば private リポジトリ全体の閲覧・書き換え、強制 push による履歴破壊、GitHub Actions secrets の窃取まで可能になります。攻撃者は奪った瞬間には何もせず静かに watch することもできるため、「使われた形跡がない」では安心できません。

これを機に、PAT を URL に埋めず GitHub CLI (gh) の credential helper に任せる構成に切り替えました。

ゴール構成

  • remote URL は素の https://github.com/USER/REPO.git
  • 認証は gh が credential helper として処理 (トークンは OS のセキュアストア管理)
  • devcontainer を作り直しても再ログイン不要

手順

1. 露出したトークンを revoke (最優先)

remote URL を書き換える前に、まず GitHub 側でトークンを無効化します。

該当トークンを Delete した瞬間に無効になります。順序として これを先にやる ことが大切です。後の手順を待っている間に古いトークンが他経路で使われるリスクを断ちます。

2. gh をインストール

devcontainer 環境なら ghcr.io/devcontainers/features/github-cli feature で gh がすでに入っています。

ホスト (Ubuntu 24.04) に入れる場合は、以下の記事を参考にしてください。

3. gh auth login で認証

gh auth login

対話で以下のように選びます。

? Where do you use GitHub?                          GitHub.com
? What is your preferred protocol for Git operations? HTTPS
? Authenticate Git with your GitHub credentials?    Yes
? How would you like to authenticate GitHub CLI?    Login with a web browser

Authenticate Git with your GitHub credentials? → Yes が肝で、これが git config --global credential.helpergh 経由に書き換えてくれます。以後 git push のたびに gh が裏でトークンを取り出して認証してくれます。

devcontainer のようなヘッドレス環境ではブラウザは自動で開きません。表示される 8 桁のワンタイムコードをコピーし、ホスト側のブラウザで https://github.com/login/device に貼って承認します。

✓ Authentication complete.
✓ Logged in as USER

4. remote URL からトークンを除去

git remote set-url origin https://github.com/USER/REPO.git
git remote -v

トークン部分が消えていれば OK です。git fetch origin がパスワードを聞かれずに通れば認証は機能しています。

5. シェル履歴の残骸を確認

revoke 済みでも、ローカル履歴に残ったトークン文字列は気持ちが悪いものです。

grep -rn 'ghp_' ~/.bash_history ~/.zsh_history 2>/dev/null

ヒットしたら該当行を削除します (history -d <行番号> でメモリ上を消した後、ファイルも編集しておきます)。

devcontainer での認証共有

gh の認証情報は ~/.config/gh/hosts.yml (と OS のセキュアストア) に保存されます。devcontainer をリビルドすると ~/.config/gh が消えて再ログインが必要になるため、ホストの設定を bind mount で共有します。

.devcontainer/devcontainer.json:

"mounts": [
  "source=${localEnv:HOME}/.claude,target=/home/vscode/.claude,type=bind,consistency=cached",
  "source=${localEnv:HOME}/.claude.json,target=/home/vscode/.claude.json,type=bind,consistency=cached",
  "source=${localEnv:HOME}/.config/gh,target=/home/vscode/.config/gh,type=bind,consistency=cached"
]

前提として ホスト側に ~/.config/gh ディレクトリが存在している必要があります (なければ bind mount に失敗します)。ホストにも gh を入れて gh auth login 済みにしておくのが一番シンプルです。事情があってホスト側で gh を使わない場合でも、空ディレクトリだけ作っておけばマウント自体は通ります。

mkdir -p ~/.config/gh   # ホスト側で実行

設定変更後は VSCode の Dev Containers: Rebuild Container でリビルドすると反映されます。

補足: SSH 鍵にしないのか

SSH 鍵認証もアリですが、

  • 鍵ファイル自体の管理 (~/.ssh のパーミッション、複数マシン間の同期) が発生します
  • GitHub Actions など他用途で gh をどのみち使うことが多いです

そのため、個人開発の HTTPS なら gh auth login ひとつに寄せた方がメンテが楽だと感じています。複数アカウントの切替も gh auth switch で済むのが良いところです。

まとめ

  • PAT を URL に埋めるのは「公開リポジトリに含まれない」だけでローカル経路の露出リスクは大きいです。露出に気付いた時点で revoke が最優先となります。
  • gh auth login で credential helper を任せれば URL からトークンを排除でき、トークンは OS のセキュアストアに隔離されます。
  • devcontainer ではホストの ~/.config/gh を bind mount しておくと、リビルドのたびに再ログインしなくて済みます。
Hugo で構築されています。
テーマ StackJimmy によって設計されています。