fix(federation): verify host header of requsets

This commit is contained in:
famfo 2026-05-12 03:38:02 +02:00
parent 5b6c702f41
commit 355b31a6a9
No known key found for this signature in database
2 changed files with 32 additions and 4 deletions

View file

@ -5,6 +5,7 @@ package activitypub
import (
"net/http"
"net/url"
"forgejo.org/modules/log"
"forgejo.org/modules/setting"
@ -15,6 +16,22 @@ import (
)
func verifyHTTPSignature(ctx app_context.APIContext) (authenticated bool, err error) {
// Verify that the canonical domain for federation is accessed regardless of
// if signatures are actually checked or not.
// This check does not cover the instance actor but that does not contain any
// potentially private information which should only be accessed via the
// canonical instance domain.
appURL, err := url.Parse(setting.AppURL)
if err != nil {
return false, err
}
if ctx.Req.Host != appURL.Host {
log.Error("%s", ctx.Req.Host)
return false, nil
}
if !setting.Federation.SignatureEnforced {
return true, nil
}
@ -48,9 +65,9 @@ func ReqHTTPSignature() func(ctx *app_context.APIContext) {
return func(ctx *app_context.APIContext) {
if authenticated, err := verifyHTTPSignature(*ctx); err != nil {
log.Warn("verifyHttpSignature failed: %v", err)
ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed")
ctx.Error(http.StatusBadRequest, "reqSignature", "request verification failed")
} else if !authenticated {
ctx.Error(http.StatusForbidden, "reqSignature", "request signature verification failed")
ctx.Error(http.StatusForbidden, "reqSignature", "request verification failed")
}
}
}

View file

@ -32,7 +32,7 @@ func TestFederationHttpSigValidation(t *testing.T) {
onApplicationRun(t, func(t *testing.T, u *url.URL) {
userID := 2
userURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", u, userID)
userURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", setting.AppURL, userID)
user1 := unittest.AssertExistsAndLoadBean(t, &user.User{ID: 1})
@ -94,6 +94,17 @@ func TestFederationHttpSigValidation(t *testing.T) {
req := NewRequest(t, "GET", userURL)
MakeRequest(t, req, http.StatusOK)
})
// Request with wrong host header, this should always be checked, even if
// signature validation is disabled.
// The URL passed from the testing function does point to the correct
// instance but with the wrong host (localhost vs 127.0.0.1).
wrongHostUserURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", u, userID)
t.Run("WrongHostHeader", func(t *testing.T) {
req := NewRequest(t, "GET", wrongHostUserURL)
MakeRequest(t, req, http.StatusForbidden)
})
})
}
@ -141,7 +152,7 @@ func TestFederationAllRoutesCovered(t *testing.T) {
}
resp := MakeRequest(t, req, http.StatusBadRequest)
assert.Contains(t, resp.Body.String(), "request signature verification failed")
assert.Contains(t, resp.Body.String(), "request verification failed")
}
}