ソースを参照

verify secret for webhook

Ivan Galakhov 4 年 前
コミット
a4d7ce3b2b

+ 4 - 3
internal/config/config.go

@@ -41,9 +41,10 @@ type ServerConf struct {
 	GithubClientSecret string `env:"GITHUB_CLIENT_SECRET"`
 	GithubLoginEnabled bool   `env:"GITHUB_LOGIN_ENABLED,default=true"`
 
-	GithubAppClientID     string `env:"GITHUB_APP_CLIENT_ID"`
-	GithubAppClientSecret string `env:"GITHUB_APP_CLIENT_SECRET"`
-	GithubAppName         string `env:"GITHUB_APP_NAME"`
+	GithubAppClientID      string `env:"GITHUB_APP_CLIENT_ID"`
+	GithubAppClientSecret  string `env:"GITHUB_APP_CLIENT_SECRET"`
+	GithubAppName          string `env:"GITHUB_APP_NAME"`
+	GithubAppWebhookSecret string `env:"GITHUB_APP_WEBHOOK_SECRET"`
 
 	GoogleClientID         string `env:"GOOGLE_CLIENT_ID"`
 	GoogleClientSecret     string `env:"GOOGLE_CLIENT_SECRET"`

+ 6 - 4
internal/oauth/config.go

@@ -18,9 +18,10 @@ type Config struct {
 	BaseURL      string
 }
 
-// GithubAppConf is standard oauth2 config but it need to keeps track of the app name
+// GithubAppConf is standard oauth2 config but it need to keeps track of the app name and webhook secret
 type GithubAppConf struct {
-	AppName string
+	AppName       string
+	WebhookSecret string
 	oauth2.Config
 }
 
@@ -37,9 +38,10 @@ func NewGithubClient(cfg *Config) *oauth2.Config {
 	}
 }
 
-func NewGithubAppClient(cfg *Config, name string) *GithubAppConf {
+func NewGithubAppClient(cfg *Config, name string, secret string) *GithubAppConf {
 	return &GithubAppConf{
-		AppName: name,
+		AppName:       name,
+		WebhookSecret: secret,
 		Config: oauth2.Config{
 			ClientID:     cfg.ClientID,
 			ClientSecret: cfg.ClientSecret,

+ 2 - 2
server/api/api.go

@@ -170,13 +170,13 @@ func New(conf *AppConfig) (*App, error) {
 		app.Capabilities.GithubLogin = sc.GithubLoginEnabled
 	}
 
-	if sc.GithubAppClientID != "" && sc.GithubAppClientSecret != "" && sc.GithubAppName != "" {
+	if sc.GithubAppClientID != "" && sc.GithubAppClientSecret != "" && sc.GithubAppName != "" && sc.GithubAppWebhookSecret != "" {
 		app.GithubAppConf = oauth.NewGithubAppClient(&oauth.Config{
 			ClientID:     sc.GithubAppClientID,
 			ClientSecret: sc.GithubAppClientSecret,
 			Scopes:       []string{"read:user"},
 			BaseURL:      sc.ServerURL,
-		}, sc.GithubAppName)
+		}, sc.GithubAppName, sc.GithubAppWebhookSecret)
 	}
 
 	if sc.GoogleClientID != "" && sc.GoogleClientSecret != "" {

+ 28 - 0
server/api/integration_handler.go

@@ -2,6 +2,9 @@ package api
 
 import (
 	"context"
+	"crypto/hmac"
+	"crypto/sha256"
+	"encoding/hex"
 	"encoding/json"
 	"fmt"
 	"github.com/google/go-github/github"
@@ -13,6 +16,7 @@ import (
 	"net/url"
 	"sort"
 	"strconv"
+	"strings"
 
 	"github.com/go-chi/chi"
 	"github.com/porter-dev/porter/internal/forms"
@@ -386,6 +390,22 @@ func (app *App) HandleListProjectOAuthIntegrations(w http.ResponseWriter, r *htt
 	}
 }
 
+// verifySignature verifies a signature based on hmac protocal
+// https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks
+func verifySignature(secret []byte, signature string, body []byte) bool {
+	if len(signature) != 71 || !strings.HasPrefix(signature, "sha256=") {
+		return false
+	}
+
+	actual := make([]byte, 32)
+	hex.Decode(actual, []byte(signature[7:]))
+
+	computed := hmac.New(sha256.New, secret)
+	computed.Write(body)
+
+	return hmac.Equal(computed.Sum(nil), actual)
+}
+
 func (app *App) HandleGithubAppEvent(w http.ResponseWriter, r *http.Request) {
 	payload, err := ioutil.ReadAll(r.Body)
 	if err != nil {
@@ -393,6 +413,14 @@ func (app *App) HandleGithubAppEvent(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	// verify webhook secret
+	signature := r.Header.Get("X-Hub-Signature-256")
+
+	if !verifySignature([]byte(app.GithubAppConf.WebhookSecret), signature, payload) {
+		http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
+		return
+	}
+
 	event, err := github.ParseWebHook(github.WebHookType(r), payload)
 
 	if err != nil {