Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd1cf114

Browse files
committed
chore: oauth2 refresh tokens to use sha256 hashed secrets
Salts are not required for random secrets. Salts are only useful toprotect against rainbow table style attacks. Since the input it 40random characters, salts are overkill.
1 parent86f0f39 commitd1cf114

File tree

6 files changed

+59
-28
lines changed

6 files changed

+59
-28
lines changed

‎coderd/apikey/apikey.go‎

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package apikey
22

33
import (
44
"crypto/sha256"
5+
"crypto/subtle"
56
"fmt"
67
"net"
78
"time"
@@ -44,12 +45,17 @@ type CreateParams struct {
4445
// database representation. It is the responsibility of the caller to insert it
4546
// into the database.
4647
funcGenerate(paramsCreateParams) (database.InsertAPIKeyParams,string,error) {
47-
keyID,keySecret,err:=generateKey()
48+
// Length of an API Key ID.
49+
keyID,err:=cryptorand.String(10)
4850
iferr!=nil {
49-
return database.InsertAPIKeyParams{},"",xerrors.Errorf("generate API key: %w",err)
51+
return database.InsertAPIKeyParams{},"",xerrors.Errorf("generate API key ID: %w",err)
5052
}
5153

52-
hashed:=sha256.Sum256([]byte(keySecret))
54+
// Length of an API Key secret.
55+
keySecret,hashedSecret,err:=GenerateSecret(22)
56+
iferr!=nil {
57+
return database.InsertAPIKeyParams{},"",xerrors.Errorf("generate API key secret: %w",err)
58+
}
5359

5460
// Default expires at to now+lifetime, or use the configured value if not
5561
// set.
@@ -120,7 +126,7 @@ func Generate(params CreateParams) (database.InsertAPIKeyParams, string, error)
120126
ExpiresAt:params.ExpiresAt.UTC(),
121127
CreatedAt:dbtime.Now(),
122128
UpdatedAt:dbtime.Now(),
123-
HashedSecret:hashed[:],
129+
HashedSecret:hashedSecret[:],
124130
LoginType:params.LoginType,
125131
Scopes:scopes,
126132
AllowList:params.AllowList,
@@ -142,3 +148,25 @@ func generateKey() (id string, secret string, err error) {
142148
}
143149
returnid,secret,nil
144150
}
151+
152+
funcGenerateSecret(lengthint) (secretstring,hashed []byte,errerror) {
153+
secret,err=cryptorand.String(length)
154+
iferr!=nil {
155+
return"",nil,err
156+
}
157+
hash:=hashSecret(secret)
158+
returnsecret,hash[:],nil
159+
}
160+
161+
// ValidateHash compares a secret against an expected hashed secret.
162+
funcValidateHash(hashedSecret []byte,secretstring)bool {
163+
hash:=hashSecret(secret)
164+
returnsubtle.ConstantTimeCompare(hashedSecret,hash[:])==1
165+
}
166+
167+
// hashSecret is the single function used to hash API key secrets.
168+
// Use this to ensure a consistent hashing algorithm.
169+
funchashSecret(secretstring) []byte {
170+
hash:=sha256.Sum256([]byte(secret))
171+
returnhash[:]
172+
}

‎coderd/apikey/apikey_test.go‎

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

33
import (
4-
"crypto/sha256"
54
"strings"
65
"testing"
76
"time"
@@ -126,8 +125,8 @@ func TestGenerate(t *testing.T) {
126125
require.Equal(t,key.ID,keytokens[0])
127126

128127
// Assert that the hashed secret is correct.
129-
hashed:=sha256.Sum256([]byte(keytokens[1]))
130-
assert.ElementsMatch(t,hashed,key.HashedSecret)
128+
equal:=apikey.ValidateHash(key.HashedSecret,keytokens[1])
129+
require.True(t,equal,"valid secret")
131130

132131
assert.Equal(t,tc.params.UserID,key.UserID)
133132
assert.WithinDuration(t,dbtime.Now(),key.CreatedAt,time.Second*5)
@@ -173,3 +172,17 @@ func TestGenerate(t *testing.T) {
173172
})
174173
}
175174
}
175+
176+
// TestInvalid just ensures the false case is asserted by some tests.
177+
// Otherwise, a function that just `returns true` might pass all tests incorrectly.
178+
funcTestInvalid(t*testing.T) {
179+
t.Parallel()
180+
181+
require.Falsef(t,apikey.ValidateHash([]byte{},"secret"),"empty hash")
182+
183+
secret,hash,err:=apikey.GenerateSecret(10)
184+
require.NoError(t,err)
185+
186+
require.Falsef(t,apikey.ValidateHash(hash,secret+"_"),"different secret")
187+
require.Falsef(t,apikey.ValidateHash(hash[:len(hash)-1],secret),"different hash length")
188+
}

‎coderd/httpmw/apikey.go‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package httpmw
22

33
import (
44
"context"
5-
"crypto/sha256"
6-
"crypto/subtle"
75
"database/sql"
86
"errors"
97
"fmt"
@@ -20,6 +18,7 @@ import (
2018
"golang.org/x/xerrors"
2119

2220
"cdr.dev/slog"
21+
"github.com/coder/coder/v2/coderd/apikey"
2322
"github.com/coder/coder/v2/coderd/database"
2423
"github.com/coder/coder/v2/coderd/database/dbauthz"
2524
"github.com/coder/coder/v2/coderd/database/dbtime"
@@ -188,8 +187,7 @@ func APIKeyFromRequest(ctx context.Context, db database.Store, sessionTokenFunc
188187
}
189188

190189
// Checking to see if the secret is valid.
191-
hashedSecret:=sha256.Sum256([]byte(keySecret))
192-
ifsubtle.ConstantTimeCompare(key.HashedSecret,hashedSecret[:])!=1 {
190+
if!apikey.ValidateHash(key.HashedSecret,keySecret) {
193191
returnnil, codersdk.Response{
194192
Message:SignedOutErrorMessage,
195193
Detail:"API key secret is invalid.",

‎coderd/httpmw/workspaceproxy.go‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package httpmw
22

33
import (
44
"context"
5-
"crypto/sha256"
6-
"crypto/subtle"
75
"database/sql"
86
"net/http"
97
"strings"
@@ -12,6 +10,7 @@ import (
1210
"github.com/google/uuid"
1311
"golang.org/x/xerrors"
1412

13+
"github.com/coder/coder/v2/coderd/apikey"
1514
"github.com/coder/coder/v2/coderd/database"
1615
"github.com/coder/coder/v2/coderd/database/dbauthz"
1716
"github.com/coder/coder/v2/coderd/httpapi"
@@ -125,8 +124,7 @@ func ExtractWorkspaceProxy(opts ExtractWorkspaceProxyConfig) func(http.Handler)
125124
}
126125

127126
// Do a subtle constant time comparison of the hash of the secret.
128-
hashedSecret:=sha256.Sum256([]byte(secret))
129-
ifsubtle.ConstantTimeCompare(proxy.TokenHashedSecret,hashedSecret[:])!=1 {
127+
if!apikey.ValidateHash(proxy.TokenHashedSecret,secret) {
130128
httpapi.Write(ctx,w,http.StatusUnauthorized, codersdk.Response{
131129
Message:"Invalid external proxy token",
132130
Detail:"Invalid proxy token secret.",

‎coderd/oauth2provider/secrets.go‎

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package oauth2provider
33
import (
44
"fmt"
55

6-
"github.com/coder/coder/v2/coderd/userpassword"
6+
"github.com/coder/coder/v2/coderd/apikey"
77
"github.com/coder/coder/v2/cryptorand"
88
)
99

@@ -18,14 +18,15 @@ type AppSecret struct {
1818
Prefixstring
1919
// Hashed is the server stored hash(secret,salt,...). Used for verifying a
2020
// secret.
21-
Hashedstring
21+
Hashed[]byte
2222
}
2323

2424
// GenerateSecret generates a secret to be used as a client secret, refresh
2525
// token, or authorization code.
2626
funcGenerateSecret() (AppSecret,error) {
27+
2728
// 40 characters matches the length of GitHub's client secrets.
28-
secret,err:=cryptorand.String(40)
29+
secret,hashedSecret,err:=apikey.GenerateSecret(40)
2930
iferr!=nil {
3031
returnAppSecret{},err
3132
}
@@ -38,14 +39,9 @@ func GenerateSecret() (AppSecret, error) {
3839
returnAppSecret{},err
3940
}
4041

41-
hashed,err:=userpassword.Hash(secret)
42-
iferr!=nil {
43-
returnAppSecret{},err
44-
}
45-
4642
returnAppSecret{
4743
Formatted:fmt.Sprintf("coder_%s_%s",prefix,secret),
4844
Prefix:prefix,
49-
Hashed:hashed,
45+
Hashed:hashedSecret,
5046
},nil
5147
}

‎enterprise/x/aibridgedserver/aibridgedserver.go‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package aibridgedserver
22

33
import (
44
"context"
5-
"crypto/sha256"
6-
"crypto/subtle"
75
"database/sql"
86
"encoding/json"
97
"net/url"
@@ -17,6 +15,7 @@ import (
1715
"google.golang.org/protobuf/types/known/structpb"
1816

1917
"cdr.dev/slog"
18+
"github.com/coder/coder/v2/coderd/apikey"
2019

2120
"github.com/coder/coder/v2/coderd/database"
2221
"github.com/coder/coder/v2/coderd/database/dbauthz"
@@ -358,8 +357,7 @@ func (s *Server) IsAuthorized(ctx context.Context, in *proto.IsAuthorizedRequest
358357
}
359358

360359
// Key secret matches.
361-
hashedSecret:=sha256.Sum256([]byte(keySecret))
362-
ifsubtle.ConstantTimeCompare(key.HashedSecret,hashedSecret[:])!=1 {
360+
if!apikey.ValidateHash(key.HashedSecret,keySecret) {
363361
returnnil,ErrInvalidKey
364362
}
365363

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp