You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何验证GitHub Action请求来源?GITHUB_TOKEN验证失败求助

解决GitHub Action请求自有服务器的来源验证问题

为什么你的GITHUB_TOKEN方案失败

  • secrets.GITHUB_TOKEN是GitHub生成的临时仓库级Token,仅设计用于Action运行环境内部操作(如拉取代码、调用当前仓库API),其受众(audience)绑定GitHub自身,外部服务器直接使用该Token调用GitHub API时,会因身份验证上下文不匹配被拒绝,返回「Bad credentials」。
  • 即使给Token配置了足够权限,外部调用的安全限制依然会阻断请求,这是GitHub的安全设计,而非单纯的权限不足问题。

推荐解决方案:OIDC身份验证(适配多仓库场景)

这种方式无需在每个仓库配置固定密钥,通过GitHub的OIDC服务生成可验证的身份令牌,服务器直接验证令牌合法性即可确认请求来自合法的Action。

步骤1:配置组织级OIDC

  1. 进入GitHub组织设置 → Security → GitHub Actions → OIDC,启用OIDC。
  2. 在「Allowed audiences」中添加你的服务器域名(如https://your-server-domain.com)。

步骤2:配置Workflow权限

在Workflow中显式声明OIDC令牌的读取权限,也可在组织设置中统一配置:

jobs:
  fetch-server-data:
    runs-on: ubuntu-latest
    permissions:
      id-token: write  # 必须开启才能获取ID Token
      contents: read   # 根据需求添加其他权限
    steps:
      # 获取GitHub OIDC ID Token
      - name: Generate ID Token
        id: get-token
        uses: actions/github-script@v6
        with:
          script: |
            const token = await core.getIDToken('https://your-server-domain.com');
            core.setOutput('token', token);
      # 向自有服务器发送请求,携带ID Token
      - name: Request protected data
        run: |
          curl -X POST https://your-server-domain.com/api/your-endpoint \
            -H "Authorization: Bearer ${{ steps.get-token.outputs.token }}"

步骤3:服务器端验证Token(Gin示例)

使用JWT库验证Token签名、有效期及声明信息,确认请求来自你的组织仓库:

package main

import (
	"context"
	"fmt"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"github.com/lestrrat-go/jwx/jwk"
)

var jwksSet jwk.Set

// 初始化时从GitHub获取公钥集
func init() {
	var err error
	jwksSet, err = jwk.Fetch(context.Background(), "https://token.actions.githubusercontent.com/.well-known/jwks.json")
	if err != nil {
		panic(fmt.Sprintf("failed to fetch JWKS: %v", err))
	}
}

// 验证GitHub ID Token合法性
func verifyToken(tokenStr string) (bool, error) {
	token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
		// 验证签名算法为RSA
		if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
			return nil, fmt.Errorf("invalid signing method: %v", token.Header["alg"])
		}
		// 通过密钥ID从JWKS中获取公钥
		kid, ok := token.Header["kid"].(string)
		if !ok {
			return nil, fmt.Errorf("kid not found in token header")
		}
		key, ok := jwksSet.LookupKeyID(kid)
		if !ok {
			return nil, fmt.Errorf("public key not found for kid: %s", kid)
		}
		var pubkey interface{}
		if err := key.Raw(&pubkey); err != nil {
			return nil, fmt.Errorf("failed to parse public key: %v", err)
		}
		return pubkey, nil
	})

	if err != nil {
		return false, err
	}

	// 验证Token有效性及声明
	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		// 验证受众匹配服务器域名
		if aud := claims["aud"].(string); aud != "https://your-server-domain.com" {
			return false, fmt.Errorf("invalid audience")
		}
		// 验证仓库属于指定组织
		if repo := claims["repository"].(string); !strings.HasPrefix(repo, "my_org/") {
			return false, fmt.Errorf("repository not in allowed organization")
		}
		// 可额外验证Workflow名称、运行环境等字段
		return true, nil
	}

	return false, fmt.Errorf("invalid token")
}

// 处理请求的Handler
func dataHandler(c *gin.Context) {
	authHeader := c.GetHeader("Authorization")
	if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
		c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing valid authorization header"})
		return
	}

	tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
	valid, err := verifyToken(tokenStr)
	if !valid || err != nil {
		c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid request", "details": err.Error()})
		return
	}

	// 验证通过,返回数据
	c.JSON(http.StatusOK, gin.H{"data": "your protected data"})
}

func main() {
	r := gin.Default()
	r.POST("/api/your-endpoint", dataHandler)
	if err := r.Run(":8080"); err != nil {
		panic(fmt.Sprintf("server failed to start: %v", err))
	}
}

备选方案:修正GITHUB_TOKEN使用(不推荐)

如果一定要用GITHUB_TOKEN,需注意以下两点,但仍存在外部调用的安全限制风险:

  1. 显式配置Token权限:在Workflow中添加permissions字段,确保Token有访问目标API的权限:
    permissions:
      actions: read  # 访问actions/runs端点需要此权限
      contents: read
    
  2. 服务器调用API时使用正确的请求头:
    curl -H "Authorization: token $GITHUB_TOKEN" \
         -H "Accept: application/vnd.github.v3+json" \
         https://api.github.com/repos/my_org/repo_name/actions/runs/run_id
    

内容的提问来源于stack exchange,提问作者afraid.jpg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 15:12:11