chore!: ensure consistent secret token generation and hashing (#20388)

This PR uses the same sha256 hashing technique as we use for APIKeys. So
now all randomly generated secrets will be hashed with sha256 for
consistency.

This is a breaking change for the oauth tokens. Since oauth is only
allowed for dev builds and experimental, this is ok.
This commit is contained in:
Steven Masley
2025-10-23 15:38:49 -05:00
committed by GitHub
parent 906149317d
commit 13ca9ead3a
35 changed files with 169 additions and 179 deletions
+4 -5
View File
@@ -2,7 +2,6 @@ package coderd
import (
"context"
"crypto/sha256"
"database/sql"
"fmt"
"net/http"
@@ -16,6 +15,7 @@ import (
"cdr.dev/slog"
agpl "github.com/coder/coder/v2/coderd"
"github.com/coder/coder/v2/coderd/apikey"
"github.com/coder/coder/v2/coderd/audit"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/db2sdk"
@@ -28,7 +28,6 @@ import (
"github.com/coder/coder/v2/coderd/workspaceapps"
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/cryptorand"
"github.com/coder/coder/v2/enterprise/coderd/proxyhealth"
"github.com/coder/coder/v2/enterprise/replicasync"
"github.com/coder/coder/v2/enterprise/wsproxy/wsproxysdk"
@@ -934,13 +933,13 @@ func (api *API) reconnectingPTYSignedToken(rw http.ResponseWriter, r *http.Reque
}
func generateWorkspaceProxyToken(id uuid.UUID) (token string, hashed []byte, err error) {
secret, err := cryptorand.HexString(64)
secret, hashedSecret, err := apikey.GenerateSecret(64)
if err != nil {
return "", nil, xerrors.Errorf("generate token: %w", err)
}
hashedSecret := sha256.Sum256([]byte(secret))
fullToken := fmt.Sprintf("%s:%s", id, secret)
return fullToken, hashedSecret[:], nil
return fullToken, hashedSecret, nil
}
func convertProxies(p []database.WorkspaceProxy, statuses map[uuid.UUID]proxyhealth.ProxyStatus) []codersdk.WorkspaceProxy {
@@ -2,8 +2,6 @@ package aibridgedserver
import (
"context"
"crypto/sha256"
"crypto/subtle"
"database/sql"
"encoding/json"
"net/url"
@@ -17,6 +15,7 @@ import (
"google.golang.org/protobuf/types/known/structpb"
"cdr.dev/slog"
"github.com/coder/coder/v2/coderd/apikey"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
@@ -358,8 +357,7 @@ func (s *Server) IsAuthorized(ctx context.Context, in *proto.IsAuthorizedRequest
}
// Key secret matches.
hashedSecret := sha256.Sum256([]byte(keySecret))
if subtle.ConstantTimeCompare(key.HashedSecret, hashedSecret[:]) != 1 {
if !apikey.ValidateHash(key.HashedSecret, keySecret) {
return nil, ErrInvalidKey
}
@@ -2,7 +2,6 @@ package aibridgedserver_test
import (
"context"
"crypto/sha256"
"database/sql"
"encoding/json"
"fmt"
@@ -21,6 +20,7 @@ import (
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/coder/coder/v2/coderd/apikey"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbmock"
"github.com/coder/coder/v2/coderd/database/dbtime"
@@ -138,13 +138,12 @@ func TestAuthorization(t *testing.T) {
}
keyID, _ := cryptorand.String(10)
keySecret, _ := cryptorand.String(22)
keySecret, keySecretHashed, _ := apikey.GenerateSecret(22)
token := fmt.Sprintf("%s-%s", keyID, keySecret)
keySecretHashed := sha256.Sum256([]byte(keySecret))
apiKey := database.APIKey{
ID: keyID,
LifetimeSeconds: 86400, // default in db
HashedSecret: keySecretHashed[:],
HashedSecret: keySecretHashed,
IPAddress: pqtype.Inet{
IPNet: net.IPNet{
IP: net.IPv4(127, 0, 0, 1),