GitHub 的登入驗證機制

淺談 Github 的身分驗證機制

基於安全及隱私因素,存取 Private 的 Github repository 不管是進行 clone, push, pull 時都需要驗證身分。 而 Public repository 在 clone 時是不用驗證的,直接 clone 就可以了,但 push 時就要驗證你是否為具有開發者身分了。

不管是早期使用 HTTP/HTTPS 輸入帳號密碼,或者使用 SSH 搭配公私鑰,都是通訊方式及帳戶驗證的手段罷了。 但 SSH 搭配公私鑰讓存取 Github 更安全(不用擔心密碼被破解)且更方便(設定好以後再也不用輸入密碼)。

HTTPS 搭配帳號密碼的方式沒什麼好說的,需要驗證時會要求你輸入帳號密碼。但,這個方式已經被 Github deprecated,我們應該使用更好的方式,以下就來介紹 SSH 搭配公私鑰的方法。

Github 的 SSH key 與 Deploy key

在 Github 或者 Gitlab 中,可以設定 SSH Key 的地方有 2 個,按用途分為 SSH Key 以及 Deploy Key。

  1. 個人 SSH Key: 是屬於整個 Github account 的 global 設定,一台電腦只要設定一次,就能存取所有 Github repositories,適合給該 Github 帳號擁有者設定。
  2. Deploy Key: 是綁定特定 repository 的 SSH 公鑰,用來讓 CI/CD 服務、伺服器或其他環境能夠存取該 repository,但不影響你的 GitHub 上的其他 repositories。
    • 每個 Deploy Key 只能對應一個 repo,不能跨 repo 共用
    • 預設只能讀取(除非勾選 Allow write access)
    • 適用於自動化部署、伺服器存取、CI/CD 系統等場景。

使用 git 指令時 SSH 如何運作

當你使用 git 指令連到 Github 且輸入的是 SSH 格式,則 git 就會嘗試 SSH 安全遠端連線,若需要身分驗證,則會去取用你保存在 local 端的 private key。

Github 不允許使用者直接使用 SSH 操作 Github Host,而必須透過 git 指令,再由 git 指令去進行 SSH 操作。

SSH 的格式為 user@host,對照 git clone 指令格式比如: git clone git@github.com:WSMao/hugo_blog.git 其中

  • SSH user: git
  • SSH host: github.com
  • 至於後面的 :WSMao/hugo_blog.git 則是 Github 仿照 scp 格式冒號後面後面接檔案地址。

SSH 連線時選項的決定優先順序大致如下:

  1. SSH 指令指定

    • 若在 ssh 指令中明確指定連線參數,這些會 優先於 config 設定,如 ssh -i ~/.ssh/custom_key git@github.com,其中 -i 選項指定金鑰。
    • ssh 指令最少可以只輸入 host 即可,如 ssh github.com (前提是 config 設定檔須提供細節)。
  2. ~/.ssh/config

    • 主要用於簡化 ssh 指令,許多設定可以紀錄在這邊,之後使用 ssh 指令時就不用輸入那麼多細部參數。只要 ssh 指令找不到的參數設定都會到 config 裡找。
    • 如果 ~/.ssh/config 中有針對目標主機 (Host github.com) 指定 IdentityFile,則 SSH 會根據這個設定來選擇金鑰。
    • config 可以設定不同 repository 使用不同的金鑰,例如:
      Host github.com-hugo
          HostName github.com
          User git
          IdentityFile ~/.ssh/hugo_key
      
      Host github.com-private
          HostName github.com
          User git
          IdentityFile ~/.ssh/private_key
      
  3. SSH Agent

    • 用於提供 IdentityFile 也就是 private_key 的密碼。
    • 如果 SSH Agent 已經有(ssh-add)載入金鑰(載入時已提供過密碼),則之後使用 private_key 就不用再輸入密碼。
  4. 預設金鑰 (~/.ssh/id_rsa~/.ssh/id_ed25519)

    • 若 SSH 連線需要 ssh-key,但 ssh 指令沒有提供,且 ~/.ssh/config 內也沒有指定 IdentityFile,那 SSH 就會嘗試讀取預設的金鑰,如:
      • ~/.ssh/id_rsa (ssh-keygen 預設)
      • ~/.ssh/id_ed25519 (更推薦)

基本上,除非你有很多個 private_key 或者特殊需求需要管理,不然不必使用 agent 或者 config 來設定,使用預設金鑰名稱即可。

SSH Agent

  • SSH Agent 唯一的作用 就是 暫存私鑰,讓你不用每次輸入密碼。
  • 若有為 private key 設定密碼的話,Agent 可代理管理私鑰密碼,避免每次使用金鑰時都要輸入密碼。只有當 private_key 加入 agent 時需要輸入密碼,只要 agent 持續運行,往後都不用再輸入。

相關 ssh-agent 指令如下:

eval $(ssh-agent): 啟動 ssh-agent

ssh-add <private_key>: 加入 ssh key

ssh-add -l: 列出已加入的 key

ssh-add -D: 移除所有 key

~/.ssh/config 設定檔

這個檔案可以讓你自訂 SSH 連線方式,可用來簡化 SSH 指令、管理多個 SSH 金鑰。

  • 簡化 SSH 連線,不用每次都輸入完整的 user/host ip/port/key 等(比如 ssh -i ~/.ssh/id_rsa chris@192.168.1.100 -p 2222)。
  • 可針對不同的主機設定不同的 SSH Key。
  • 可指定預設的 User
  • 可設定 ProxyJump 來穿透防火牆。

可參考 如何針對不同的-repo-指定不同的-ssh-key

設定 SSH key

以 Github 帳號擁有者來說,他的身分理當允許存取所有 repositories ,只需要為他的電腦,設置一組 SSH key 並將 public key 加入 Github 即可,往後在這台電腦上進行任何對 Github 的操作,都會自動完成身分驗證,無須輸入帳號密碼。

若有另外一台電腦,也是再產生一組 SSH Key 加入 Github 即可,而且我想,或許兩台電腦使用相同的 private key 也是可以的,這樣只需要一組 SSH Key 了。

注意,不管是用在 SSH Key 或者 Deploy Key 都不可以重複,否則加入 Key 時會出現: Key is already in use 的錯誤訊息。

Step1: 產生 key 的指令 ssh-keygen

參數 說明 範例
-t <key_type> 指定 金鑰類型ed25519 是一種較新的橢圓曲線演算法,比 rsa(預設)更安全、更快,且金鑰較短。 -t ed25519
-C <comment> 設定金鑰的 註解 (comment),通常用來標記用途,例如 GitHub 會建議填入 Email 來區分不同設備的金鑰。 -C "your_email@example.com"
-b <bits> 金鑰長度(只適用於 rsaed25519 沒有這個選項) -b 4096(預設 3072)
-f <file> 指定金鑰存放位置(預設存於 ~/.ssh/id_ed25519 -f ~/.ssh/my_github_key
-N <passphrase> 設定金鑰密碼(空字串 "" 代表無密碼) -N "mypassword"
-y 從私鑰產生對應的公鑰 ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub
-q 安靜模式(不顯示輸出) -q

範例,於終端機則一輸入:

  • ssh-keygen,沒給參數則會引導一步步輸入
  • ssh-keygen -t ed25519 -C "my_email@example.com",指定用 ed25519 算法產生晶鑰,並加入 comment 訊息。

輸入後就會產生一對 Keys,帶有尾墜.pub 的是 public_key,沒有尾墜的是 private_key。

Step2: 將 public_key 加入 Github:

  1. 於終端機用 cat id_rsa.pub 讀出 public_key,並複製輸出結果。
  2. 到 Github 點擊頭像 -> Settings -> 左邊欄位選擇 SSH and GPG Keys -> 選擇 New SSH Key -> 貼上 public_key。

Step3: 測試 SSH 連線指令

ssh -T git@github.com: 可用來測試連線,-T 代表禁用虛擬終端分配,因為 GitHub SSH 伺服器不提供 shell 存取,使用 -T 不會開啟遠端終端機,只是測試身份驗證是否成功

如果驗證身分成功,你會看到 Hi WSMao! You've successfully authenticated, but GitHub does not provide shell access.

補充:若你使用 deploy key 存取 github.com 時,則會提到特定 repository 名稱 Hi WSMao/hugo_blog! You've successfully authenticated, but GitHub does not provide shell access.

Step4: 嘗試使用 git 指令

git clone git@github.com:WSMao/hugo_blog.git 是否 clone 成功就知道 SSH Key 設定是否生效。若出現失敗訊息 Please make sure you have the correct access rights and the repository exists. 可能就是 SSH Key 沒有設定好。

其他討論

為何一直說 HTTPS 密碼驗證已經被 deprecated 但 Github 卻還提供 Clone using the web URL 的 HTTPS 選項?

GitHub 禁止的是 Git 操作時使用密碼驗證(此密碼就是登入 Github 之密碼)。在網頁登入 GitHub 時仍然是使用密碼,但透過 HTTPS 的 Git 操作不能再用密碼。

儘管 GitHub 在 2021 年 停用了使用帳號密碼的方式進行 Git 操作,但是仍然可以使用 HTTPS + Personal Access Token(PAT)來驗證並進行 git clone 等操作。 如果你使用 HTTPS 並看到提示要求輸入密碼,那麼密碼應該是 Personal Access Token,而非 GitHub 帳號密碼。

Personal Access Token (PAT) 是 GitHub 提供的一種更安全、更靈活的身份驗證方式,可以用來替代密碼,並可設置具體權限和範圍。

  • 是一串由 GitHub 自動生成的長字元串,比普通密碼更長、更難猜測,通常包含字母和數字的組合。
  • 專門為 API 認證 或 Git 操作(如 git clone, git push 等)而設計的令牌,當你使用 HTTPS clone 或 push 操作時,會被要求提供 Personal Access Token 作為密碼。
  • 使用 範圍(scope)來控制權限,意味著它可以限制你執行的操作範圍,並且可以隨時撤銷或更換。例如,你可以創建一個只有 repo 權限的 PAT,這樣它只能存取你的私人儲存庫,而不能進行組織管理或其他更高層級的操作。

為何 SSH 比 HTTPS 更適合 Git 操作? HTTPS 不也是使用了公私鑰?

SSH 和 HTTPS 都有公私鑰加密,但它們的重點不同,SSH 更強調 身份驗證加密通信,而 HTTPS 則專注於 加密資料傳輸伺服器身份認證

主要差別是:

  • SSH 強調的是對 client 端的身分驗證,所以 client 端要自己製作 SSH Key,上傳 public_key 並在本地端保管 private_key。
  • HTTPS 則相反,強調的是對 server 端的身分驗證,使用 憑證機構(CA)簽署的 SSL/TLS 證書來驗證伺服器的真實性,client 端藉此確認 server 有通過驗證。

對於 Github 應用來說,主要是要進行 client 端的身分驗證,這正是 SSH 本就擅長的部份。HTTPS 雖然也保障資料傳輸是安全加密的,但進行 client 身分認證的部分,還是得透過使用者輸入帳號密碼,即使現在改用 PAT 認證,還是沒有永遠不用輸入密碼的 SSH Key 機制來的安全。

如何針對不同的 repo 指定不同的 SSH Key?

你可以透過 ~/.ssh/config 設定不同 repo 使用不同的 Key。

注意: 這裡主要是要探討若需要管理多把 keys 時, config 檔案可以如何設定。日常 Github 使用上,不太會有這個需求,因為只需要設定好 1 組 SSH key 即可。

(1) 產生多組 SSH Key

ssh-keygen -t ed25519 -C "your-email@example.com" -f ~/.ssh/github_repo1
ssh-keygen -t ed25519 -C "your-email@example.com" -f ~/.ssh/github_repo2

這會產生:

  • ~/.ssh/github_repo1(私鑰)
  • ~/.ssh/github_repo1.pub(公鑰)
  • ~/.ssh/github_repo2(私鑰)
  • ~/.ssh/github_repo2.pub(公鑰)

(2) 分別把 .pub 上傳到 GitHub

  • 進入 GitHub → Settings → SSH and GPG keys
  • 分別把 github_repo1.pubgithub_repo2.pub 加到 SSH Keys

(3) 設定 ~/.ssh/config

Host github-repo1
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_repo1

Host github-repo2
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_repo2

這樣,你可以用 github-repo1github-repo2 來指定不同的 SSH Key。

(4) 使用 git clone

git clone git@github-repo1:WSMao/repo1.git
git clone git@github-repo2:WSMao/repo2.git

這樣就可以讓不同的 repo 使用不同的 SSH Key。