Skip to content

Commit fcf92fa

Browse files
AdaAibabyadababys
andauthored
fix(envd): use constant-time comparison for signature validation (#3145)
Replace string inequality operator (!=) with crypto/subtle.ConstantTimeCompare for signature validation in auth.go. The previous implementation was vulnerable to timing attacks, where an attacker could potentially determine the correct signature byte-by-byte by measuring response times. This is a standard security best practice for comparing cryptographic values such as HMAC signatures, tokens, and hashes. --------- Co-authored-by: AdaAibaby <shaolila@buaa.edu.cn>
1 parent 2cf489b commit fcf92fa

3 files changed

Lines changed: 98 additions & 10 deletions

File tree

packages/envd/internal/api/auth.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package api
22

33
import (
4+
"crypto/subtle"
45
"errors"
56
"fmt"
67
"net/http"
@@ -113,7 +114,8 @@ func (a *API) validateSigning(r *http.Request, signature *string, signatureExpir
113114
}
114115

115116
// signature validation
116-
if expectedSignature != *signature {
117+
// Use constant-time comparison to prevent timing attacks.
118+
if subtle.ConstantTimeCompare([]byte(expectedSignature), []byte(*signature)) != 1 {
117119
return errors.New("invalid signature")
118120
}
119121

packages/envd/internal/api/auth_test.go

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,33 @@ package api
22

33
import (
44
"fmt"
5+
"net/http"
6+
"net/http/httptest"
57
"strconv"
68
"testing"
79
"time"
810

11+
"github.com/rs/zerolog"
912
"github.com/stretchr/testify/assert"
1013
"github.com/stretchr/testify/require"
1114

1215
"github.com/e2b-dev/infra/packages/shared/pkg/keys"
1316
)
1417

18+
func newAuthTestAPI(t *testing.T, token string) *API {
19+
t.Helper()
20+
secureToken := &SecureToken{}
21+
err := secureToken.Set([]byte(token))
22+
require.NoError(t, err)
23+
logger := zerolog.Nop()
24+
25+
return &API{accessToken: secureToken, logger: &logger}
26+
}
27+
1528
func TestKeyGenerationAlgorithmIsStable(t *testing.T) {
1629
t.Parallel()
1730
apiToken := "secret-access-token"
18-
secureToken := &SecureToken{}
19-
err := secureToken.Set([]byte(apiToken))
20-
require.NoError(t, err)
21-
api := &API{accessToken: secureToken}
31+
api := newAuthTestAPI(t, apiToken)
2232

2333
path := "/path/to/demo.txt"
2434
username := "root"
@@ -40,10 +50,7 @@ func TestKeyGenerationAlgorithmIsStable(t *testing.T) {
4050
func TestKeyGenerationAlgorithmWithoutExpirationIsStable(t *testing.T) {
4151
t.Parallel()
4252
apiToken := "secret-access-token"
43-
secureToken := &SecureToken{}
44-
err := secureToken.Set([]byte(apiToken))
45-
require.NoError(t, err)
46-
api := &API{accessToken: secureToken}
53+
api := newAuthTestAPI(t, apiToken)
4754

4855
path := "/path/to/resource.txt"
4956
username := "user"
@@ -60,3 +67,82 @@ func TestKeyGenerationAlgorithmWithoutExpirationIsStable(t *testing.T) {
6067

6168
assert.Equal(t, localSignature, signature)
6269
}
70+
71+
func TestValidateSigningAcceptsCorrectSignature(t *testing.T) {
72+
t.Parallel()
73+
api := newAuthTestAPI(t, "test-token")
74+
75+
path := "/files"
76+
username := "user1"
77+
operation := SigningWriteOperation
78+
exp := time.Now().Add(time.Hour).Unix()
79+
80+
sig, err := api.generateSignature(path, username, operation, &exp)
81+
require.NoError(t, err)
82+
83+
expInt := int(exp)
84+
r := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/files", nil)
85+
err = api.validateSigning(r, &sig, &expInt, &username, path, operation)
86+
assert.NoError(t, err)
87+
}
88+
89+
func TestValidateSigningRejectsWrongSignature(t *testing.T) {
90+
t.Parallel()
91+
api := newAuthTestAPI(t, "test-token")
92+
93+
wrong := "v1_wrong_signature"
94+
exp := int(time.Now().Add(time.Hour).Unix())
95+
username := "user1"
96+
97+
r := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/files", nil)
98+
err := api.validateSigning(r, &wrong, &exp, &username, "/files", SigningWriteOperation)
99+
assert.EqualError(t, err, "invalid signature")
100+
}
101+
102+
func TestValidateSigningRejectsExpiredSignature(t *testing.T) {
103+
t.Parallel()
104+
api := newAuthTestAPI(t, "test-token")
105+
106+
path := "/files"
107+
username := "user1"
108+
operation := SigningReadOperation
109+
// Expired 1 hour ago
110+
exp := time.Now().Add(-time.Hour).Unix()
111+
112+
sig, err := api.generateSignature(path, username, operation, &exp)
113+
require.NoError(t, err)
114+
115+
expInt := int(exp)
116+
r := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/files", nil)
117+
err = api.validateSigning(r, &sig, &expInt, &username, path, operation)
118+
assert.EqualError(t, err, "signature is already expired")
119+
}
120+
121+
func TestValidateSigningRejectsMissingSignature(t *testing.T) {
122+
t.Parallel()
123+
api := newAuthTestAPI(t, "test-token")
124+
125+
r := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/files", nil)
126+
err := api.validateSigning(r, nil, nil, nil, "/files", SigningReadOperation)
127+
assert.EqualError(t, err, "missing signature query parameter")
128+
}
129+
130+
func TestValidateSigningAcceptsValidAccessTokenHeader(t *testing.T) {
131+
t.Parallel()
132+
api := newAuthTestAPI(t, "test-token")
133+
134+
r := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/files", nil)
135+
r.Header.Set(accessTokenHeader, "test-token")
136+
err := api.validateSigning(r, nil, nil, nil, "/files", SigningReadOperation)
137+
assert.NoError(t, err)
138+
}
139+
140+
func TestValidateSigningRejectsInvalidAccessTokenHeader(t *testing.T) {
141+
t.Parallel()
142+
api := newAuthTestAPI(t, "test-token")
143+
144+
r := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/files", nil)
145+
r.Header.Set(accessTokenHeader, "wrong-token")
146+
err := api.validateSigning(r, nil, nil, nil, "/files", SigningReadOperation)
147+
assert.EqualError(t, err, "access token present in header but does not match")
148+
}

packages/envd/pkg/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
package pkg
22

3-
const Version = "0.6.8"
3+
const Version = "0.6.9"

0 commit comments

Comments
 (0)