From 9e60ab4b900e11a823e0a51bd4ee4dc6d58d73be Mon Sep 17 00:00:00 2001 From: Aaron Harper Date: Fri, 23 Aug 2024 14:59:30 -0400 Subject: [PATCH] Apply JCS before signature validation (#54) --- consts.go | 2 +- handler.go | 8 +++++++- handler_test.go | 6 +++--- signature.go | 20 +++++++++++++++++--- signature_test.go | 12 ++++++------ 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/consts.go b/consts.go index e77fc935..8a51078d 100644 --- a/consts.go +++ b/consts.go @@ -2,5 +2,5 @@ package inngestgo const ( SDKLanguage = "go" - SDKVersion = "0.5.4" + SDKVersion = "0.7.3" ) diff --git a/handler.go b/handler.go index 2d159ff5..61a3d8eb 100644 --- a/handler.go +++ b/handler.go @@ -774,7 +774,13 @@ func (h *handler) trust( return } - w.Header().Add("X-Inngest-Signature", Sign(ctx, time.Now(), []byte(key), byt)) + resSig, err := Sign(ctx, time.Now(), []byte(key), byt) + if err != nil { + _ = publicerr.WriteHTTP(w, err) + return + } + + w.Header().Add("X-Inngest-Signature", resSig) w.WriteHeader(200) _, err = w.Write(byt) if err != nil { diff --git a/handler_test.go b/handler_test.go index a8b7f61d..dbf8e12a 100644 --- a/handler_test.go +++ b/handler_test.go @@ -494,7 +494,7 @@ func TestIntrospection(t *testing.T) { r := require.New(t) reqBody := []byte("") - sig := Sign(context.Background(), time.Now(), []byte(testKey), reqBody) + sig, _ := Sign(context.Background(), time.Now(), []byte(testKey), reqBody) req, err := http.NewRequest(http.MethodGet, server.URL, bytes.NewReader(reqBody)) r.NoError(err) req.Header.Set("X-Inngest-Signature", sig) @@ -531,7 +531,7 @@ func TestIntrospection(t *testing.T) { reqBody := []byte("") invalidKey := "deadbeef" - sig := Sign(context.Background(), time.Now(), []byte(invalidKey), reqBody) + sig, _ := Sign(context.Background(), time.Now(), []byte(invalidKey), reqBody) req, err := http.NewRequest(http.MethodGet, server.URL, bytes.NewReader(reqBody)) r.NoError(err) req.Header.Set("X-Inngest-Signature", sig) @@ -591,7 +591,7 @@ func handlerPost(t *testing.T, url string, r *sdkrequest.Request) *http.Response t.Helper() body := marshalRequest(t, r) - sig := Sign(context.Background(), time.Now(), []byte(testKey), body) + sig, _ := Sign(context.Background(), time.Now(), []byte(testKey), body) req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body)) require.NoError(t, err) diff --git a/signature.go b/signature.go index d73a6c44..2985ac44 100644 --- a/signature.go +++ b/signature.go @@ -10,6 +10,9 @@ import ( "regexp" "strconv" "time" + + "github.com/gowebpki/jcs" + "github.com/inngest/inngest/pkg/logger" ) var ( @@ -22,9 +25,17 @@ var ( ) // Sign signs a request body with the given key at the given timestamp. -func Sign(ctx context.Context, at time.Time, key, body []byte) string { +func Sign(ctx context.Context, at time.Time, key, body []byte) (string, error) { key = normalizeKey(key) + var err error + if len(body) > 0 { + body, err = jcs.Transform(body) + if err != nil { + logger.StdlibLogger(ctx).Warn("failed to canonicalize body", "error", err) + } + } + ts := at.Unix() if at.IsZero() { ts = time.Now().Unix() @@ -36,7 +47,7 @@ func Sign(ctx context.Context, at time.Time, key, body []byte) string { // timing attacks. _, _ = mac.Write([]byte(fmt.Sprintf("%d", ts))) sig := hex.EncodeToString(mac.Sum(nil)) - return fmt.Sprintf("t=%d&s=%s", ts, sig) + return fmt.Sprintf("t=%d&s=%s", ts, sig), nil } // validateSignature ensures that the signature for the given body is signed with @@ -58,7 +69,10 @@ func validateSignature(ctx context.Context, sig string, key, body []byte) (bool, return false, ErrExpiredSignature } - actual := Sign(ctx, ts, key, body) + actual, err := Sign(ctx, ts, key, body) + if err != nil { + return false, err + } if actual != sig { return false, ErrInvalidSignature } diff --git a/signature_test.go b/signature_test.go index cdf216f8..02062900 100644 --- a/signature_test.go +++ b/signature_test.go @@ -10,7 +10,7 @@ import ( ) var ( - testBody = []byte(`hey! if you're reading this come work with us: careers@inngest.com`) + testBody = []byte(`{"msg": "hey! if you're reading this come work with us: careers@inngest.com"}`) testKey = "signkey-test-12345678" testKeyFallback = "signkey-test-00000000" ) @@ -23,9 +23,9 @@ func TestSign(t *testing.T) { keyA := []byte("signkey-test-12345678") keyB := []byte("signkey-prod-12345678") keyC := []byte("12345678") - a := Sign(ctx, at, keyA, testBody) - b := Sign(ctx, at, keyB, testBody) - c := Sign(ctx, at, keyC, testBody) + a, _ := Sign(ctx, at, keyA, testBody) + b, _ := Sign(ctx, at, keyB, testBody) + c, _ := Sign(ctx, at, keyC, testBody) require.Equal(t, a, b) require.Equal(t, a, c) }) @@ -54,7 +54,7 @@ func TestValidateSignature(t *testing.T) { t.Run("with the wrong key it fails", func(t *testing.T) { at := time.Now() - sig := Sign(ctx, at, []byte(testKey), testBody) + sig, _ := Sign(ctx, at, []byte(testKey), testBody) ok, _, err := ValidateSignature(ctx, sig, "signkey-test-lolwtf", "", testBody) require.False(t, ok) @@ -64,7 +64,7 @@ func TestValidateSignature(t *testing.T) { t.Run("with the same key and within a reasonable time it succeeds", func(t *testing.T) { at := time.Now().Add(-5 * time.Second) - sig := Sign(ctx, at, []byte(testKey), testBody) + sig, _ := Sign(ctx, at, []byte(testKey), testBody) ok, _, err := ValidateSignature(ctx, sig, testKey, "", testBody) require.True(t, ok)