CVE-2024-23656: Inadequate Encryption Strength (CWE-326)
value: Dex is an identity service that uses OpenID Connect to drive authentication for other apps. Dex 2.37.0 serves HTTPS with insecure TLS 1.0 and TLS 1.1. `cmd/dex/serve.go` line 425 seemingly sets TLS 1.2 as minimum version, but the whole `tlsConfig` is ignored after `TLS cert reloader` was introduced in v2.37.0. Configured cipher suites are not respected either. This issue is fixed in Dex 2.38.0.
CWE description: The product stores or transmits sensitive data using an encryption scheme that is theoretically sound, but is not strong enough for the level of protection required.
CVSS v3 base score: 7.5
Published: 2024-01-25T20:15Z
Repository: https://github.com/dexidp/dex
Language: Go
Files changed: cmd/dex/config.go, cmd/dex/config_test.go, cmd/dex/serve.go, config.yaml.dist
Commit date: 2024-01-25 13:55:55 +0200
Commit message: feat: add TLS versions configuration

Add configuration options for TLSMinVersion and TLSMaxVersion.
This enables setting TLS 1.3 as minimum version for example for both
GRPC and Web, or enforcing TLS 1.2 only for easier debugging of
secure connections.

Signed-off-by: Tuomo Tanskanen <tuomo.tanskanen@est.tech>

Diff: diff --git a/cmd/dex/config.go b/cmd/dex/config.go
index dd91bf1e..9c5c1039 100644
--- a/cmd/dex/config.go
+++ b/cmd/dex/config.go
@@ -62,14 +62,20 @@ func (c Config) Validate() error {
 		{!c.EnablePasswordDB && len(c.StaticPasswords) != 0, "cannot specify static passwords without enabling password db"},
 		{c.Storage.Config == nil, "no storage supplied in config file"},
 		{c.Web.HTTP == "" && c.Web.HTTPS == "", "must supply a HTTP/HTTPS  address to listen on"},
 		{c.Web.HTTPS != "" && c.Web.TLSCert == "", "no cert specified for HTTPS"},
 		{c.Web.HTTPS != "" && c.Web.TLSKey == "", "no private key specified for HTTPS"},
+		{c.Web.TLSMinVersion != "" && c.Web.TLSMinVersion != "1.2" && c.Web.TLSMinVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+		{c.Web.TLSMaxVersion != "" && c.Web.TLSMaxVersion != "1.2" && c.Web.TLSMaxVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+		{c.Web.TLSMaxVersion != "" && c.Web.TLSMinVersion != "" && c.Web.TLSMinVersion > c.Web.TLSMaxVersion, "TLSMinVersion greater than TLSMaxVersion"},
 		{c.GRPC.TLSCert != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
 		{c.GRPC.TLSKey != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
 		{(c.GRPC.TLSCert == "") != (c.GRPC.TLSKey == ""), "must specific both a gRPC TLS cert and key"},
 		{c.GRPC.TLSCert == "" && c.GRPC.TLSClientCA != "", "cannot specify gRPC TLS client CA without a gRPC TLS cert"},
+		{c.GRPC.TLSMinVersion != "" && c.GRPC.TLSMinVersion != "1.2" && c.GRPC.TLSMinVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+		{c.GRPC.TLSMaxVersion != "" && c.GRPC.TLSMaxVersion != "1.2" && c.GRPC.TLSMaxVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+		{c.GRPC.TLSMaxVersion != "" && c.GRPC.TLSMinVersion != "" && c.GRPC.TLSMinVersion > c.GRPC.TLSMaxVersion, "TLSMinVersion greater than TLSMaxVersion"},
 	}
 
 	var checkErrors []string
 
 	for _, check := range checks {
@@ -147,10 +153,12 @@ type OAuth2 struct {
 type Web struct {
 	HTTP           string   `json:"http"`
 	HTTPS          string   `json:"https"`
 	TLSCert        string   `json:"tlsCert"`
 	TLSKey         string   `json:"tlsKey"`
+	TLSMinVersion  string   `json:"tlsMinVersion"`
+	TLSMaxVersion  string   `json:"tlsMaxVersion"`
 	AllowedOrigins []string `json:"allowedOrigins"`
 	AllowedHeaders []string `json:"allowedHeaders"`
 }
 
 // Telemetry is the config format for telemetry including the HTTP server config.
@@ -161,15 +169,17 @@ type Telemetry struct {
 }
 
 // GRPC is the config for the gRPC API.
 type GRPC struct {
 	// The port to listen on.
-	Addr        string `json:"addr"`
-	TLSCert     string `json:"tlsCert"`
-	TLSKey      string `json:"tlsKey"`
-	TLSClientCA string `json:"tlsClientCA"`
-	Reflection  bool   `json:"reflection"`
+	Addr          string `json:"addr"`
+	TLSCert       string `json:"tlsCert"`
+	TLSKey        string `json:"tlsKey"`
+	TLSClientCA   string `json:"tlsClientCA"`
+	TLSMinVersion string `json:"tlsMinVersion"`
+	TLSMaxVersion string `json:"tlsMaxVersion"`
+	Reflection    bool   `json:"reflection"`
 }
 
 // Storage holds app's storage configuration.
 type Storage struct {
 	Type   string        `json:"type"`
diff --git a/cmd/dex/config_test.go b/cmd/dex/config_test.go
index 21037084..16803d6d 100644
--- a/cmd/dex/config_test.go
+++ b/cmd/dex/config_test.go
@@ -69,11 +69,13 @@ storage:
     maxOpenConns: 5
     maxIdleConns: 3
     connMaxLifetime: 30
     connectionTimeout: 3
 web:
-  http: 127.0.0.1:5556
+  https: 127.0.0.1:5556
+  tlsMinVersion: 1.3
+  tlsMaxVersion: 1.2
 
 frontend:
   dir: ./web
   extra:
     foo: bar
@@ -142,11 +144,13 @@ logger:
 					ConnectionTimeout: 3,
 				},
 			},
 		},
 		Web: Web{
-			HTTP: "127.0.0.1:5556",
+			HTTPS:         "127.0.0.1:5556",
+			TLSMinVersion: "1.3",
+			TLSMaxVersion: "1.2",
 		},
 		Frontend: server.WebConfig{
 			Dir: "./web",
 			Extra: map[string]string{
 				"foo": "bar",
diff --git a/cmd/dex/serve.go b/cmd/dex/serve.go
index 54767494..3494443e 100644
--- a/cmd/dex/serve.go
+++ b/cmd/dex/serve.go
@@ -143,13 +143,27 @@ func runServe(options serveOptions) error {
 		tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
 		tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
 		tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
 	}
 
+	allowedTLSVersions := map[string]int{
+		"1.2": tls.VersionTLS12,
+		"1.3": tls.VersionTLS13,
+	}
+
 	if c.GRPC.TLSCert != "" {
+		tlsMinVersion := tls.VersionTLS12
+		if c.GRPC.TLSMinVersion != "" {
+			tlsMinVersion = allowedTLSVersions[c.GRPC.TLSMinVersion]
+		}
+		tlsMaxVersion := 0 // default for max is whatever Go defaults to
+		if c.GRPC.TLSMaxVersion != "" {
+			tlsMaxVersion = allowedTLSVersions[c.GRPC.TLSMaxVersion]
+		}
 		baseTLSConfig := &tls.Config{
-			MinVersion:               tls.VersionTLS12,
+			MinVersion:               uint16(tlsMinVersion),
+			MaxVersion:               uint16(tlsMaxVersion),
 			CipherSuites:             allowedTLSCiphers,
 			PreferServerCipherSuites: true,
 		}
 
 		tlsConfig, err := newTLSReloader(logger, c.GRPC.TLSCert, c.GRPC.TLSKey, c.GRPC.TLSClientCA, baseTLSConfig)
@@ -420,12 +434,22 @@ func runServe(options serveOptions) error {
 		l, err := net.Listen("tcp", c.Web.HTTPS)
 		if err != nil {
 			return fmt.Errorf("listening (%s) on %s: %v", name, c.Web.HTTPS, err)
 		}
 
+		tlsMinVersion := tls.VersionTLS12
+		if c.Web.TLSMinVersion != "" {
+			tlsMinVersion = allowedTLSVersions[c.Web.TLSMinVersion]
+		}
+		tlsMaxVersion := 0 // default for max is whatever Go defaults to
+		if c.Web.TLSMaxVersion != "" {
+			tlsMaxVersion = allowedTLSVersions[c.Web.TLSMaxVersion]
+		}
+
 		baseTLSConfig := &tls.Config{
-			MinVersion:               tls.VersionTLS12,
+			MinVersion:               uint16(tlsMinVersion),
+			MaxVersion:               uint16(tlsMaxVersion),
 			CipherSuites:             allowedTLSCiphers,
 			PreferServerCipherSuites: true,
 		}
 
 		tlsConfig, err := newTLSReloader(logger, c.Web.TLSCert, c.Web.TLSKey, "", baseTLSConfig)
diff --git a/config.yaml.dist b/config.yaml.dist
index 12d9b927..b7e1410f 100644
--- a/config.yaml.dist
+++ b/config.yaml.dist
@@ -53,10 +53,12 @@ web:
 
   # Uncomment to enable HTTPS endpoint.
   # https: 127.0.0.1:5554
   # tlsCert: /etc/dex/tls.crt
   # tlsKey: /etc/dex/tls.key
+  # tlsMinVersion: 1.2
+  # tlsMaxVersion: 1.3
 
 # Dex UI configuration
 # frontend:
 #   issuer: dex
 #   logoURL: theme/logo.png

Vulnerable code: 	Addr        string `json:"addr"`
	TLSCert     string `json:"tlsCert"`
	TLSKey      string `json:"tlsKey"`
	TLSClientCA string `json:"tlsClientCA"`
	Reflection  bool   `json:"reflection"`
  http: 127.0.0.1:5556
			HTTP: "127.0.0.1:5556",
			MinVersion:               tls.VersionTLS12,
			MinVersion:               tls.VersionTLS12,
Fixed code: 		{c.Web.TLSMinVersion != "" && c.Web.TLSMinVersion != "1.2" && c.Web.TLSMinVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
		{c.Web.TLSMaxVersion != "" && c.Web.TLSMaxVersion != "1.2" && c.Web.TLSMaxVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
		{c.Web.TLSMaxVersion != "" && c.Web.TLSMinVersion != "" && c.Web.TLSMinVersion > c.Web.TLSMaxVersion, "TLSMinVersion greater than TLSMaxVersion"},
		{c.GRPC.TLSMinVersion != "" && c.GRPC.TLSMinVersion != "1.2" && c.GRPC.TLSMinVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
		{c.GRPC.TLSMaxVersion != "" && c.GRPC.TLSMaxVersion != "1.2" && c.GRPC.TLSMaxVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
		{c.GRPC.TLSMaxVersion != "" && c.GRPC.TLSMinVersion != "" && c.GRPC.TLSMinVersion > c.GRPC.TLSMaxVersion, "TLSMinVersion greater than TLSMaxVersion"},
	TLSMinVersion  string   `json:"tlsMinVersion"`
	TLSMaxVersion  string   `json:"tlsMaxVersion"`
	Addr          string `json:"addr"`
	TLSCert       string `json:"tlsCert"`
	TLSKey        string `json:"tlsKey"`
	TLSClientCA   string `json:"tlsClientCA"`
	TLSMinVersion string `json:"tlsMinVersion"`
	TLSMaxVersion string `json:"tlsMaxVersion"`
	Reflection    bool   `json:"reflection"`
  https: 127.0.0.1:5556
  tlsMinVersion: 1.3
  tlsMaxVersion: 1.2
			HTTPS:         "127.0.0.1:5556",
			TLSMinVersion: "1.3",
			TLSMaxVersion: "1.2",
	allowedTLSVersions := map[string]int{
		"1.2": tls.VersionTLS12,
		"1.3": tls.VersionTLS13,
	}

		tlsMinVersion := tls.VersionTLS12
		if c.GRPC.TLSMinVersion != "" {
			tlsMinVersion = allowedTLSVersions[c.GRPC.TLSMinVersion]
		}
		tlsMaxVersion := 0 // default for max is whatever Go defaults to
		if c.GRPC.TLSMaxVersion != "" {
			tlsMaxVersion = allowedTLSVersions[c.GRPC.TLSMaxVersion]
		}
			MinVersion:               uint16(tlsMinVersion),
			MaxVersion:               uint16(tlsMaxVersion),
		tlsMinVersion := tls.VersionTLS12
		if c.Web.TLSMinVersion != "" {
			tlsMinVersion = allowedTLSVersions[c.Web.TLSMinVersion]
		}
		tlsMaxVersion := 0 // default for max is whatever Go defaults to
		if c.Web.TLSMaxVersion != "" {
			tlsMaxVersion = allowedTLSVersions[c.Web.TLSMaxVersion]
		}

			MinVersion:               uint16(tlsMinVersion),
			MaxVersion:               uint16(tlsMaxVersion),
  # tlsMinVersion: 1.2
  # tlsMaxVersion: 1.3