fix: enable iframe use on http://localhost (#7152)

* fix: enable iframe use on http://localhost

* docs(iframe): add info about cookies

* improve comments
This commit is contained in:
Livio Spring
2024-01-16 11:28:56 +01:00
committed by GitHub
parent 2ccb7baf85
commit 96d0291848
4 changed files with 47 additions and 20 deletions

View File

@@ -10,10 +10,12 @@ import (
)
const (
prefixSecure = "__Secure-"
prefixHost = "__Host-"
PrefixSecure cookiePrefix = "__Secure-"
PrefixHost cookiePrefix = "__Host-"
)
type cookiePrefix string
type CookieHandler struct {
securecookie *securecookie.SecureCookie
secureOnly bool
@@ -21,6 +23,7 @@ type CookieHandler struct {
sameSite http.SameSite
path string
maxAge int
prefix cookiePrefix
}
func NewCookieHandler(opts ...CookieHandlerOpt) *CookieHandler {
@@ -78,14 +81,17 @@ func WithMaxAge(maxAge int) CookieHandlerOpt {
}
}
func SetCookiePrefix(name, domain, path string, secureOnly bool) string {
func WithPrefix(prefix cookiePrefix) CookieHandlerOpt {
return func(c *CookieHandler) {
c.prefix = prefix
}
}
func SetCookiePrefix(name string, secureOnly bool, prefix cookiePrefix) string {
if !secureOnly {
return name
}
if domain != "" || path != "/" {
return prefixSecure + name
}
return prefixHost + name
return string(prefix) + name
}
func (c *CookieHandler) GetCookieValue(r *http.Request, name string) (string, error) {
@@ -97,7 +103,7 @@ func (c *CookieHandler) GetCookieValue(r *http.Request, name string) (string, er
}
func (c *CookieHandler) GetEncryptedCookieValue(r *http.Request, name string, value interface{}) error {
cookie, err := r.Cookie(SetCookiePrefix(name, r.Host, c.path, c.secureOnly))
cookie, err := r.Cookie(SetCookiePrefix(name, c.secureOnly, c.prefix))
if err != nil {
return err
}
@@ -131,19 +137,24 @@ func (c *CookieHandler) DeleteCookie(w http.ResponseWriter, name string) {
c.httpSet(w, name, "", "", -1)
}
func (c *CookieHandler) httpSet(w http.ResponseWriter, name, domain, value string, maxage int) {
c.httpSetWithSameSite(w, name, domain, value, maxage, c.sameSite)
func (c *CookieHandler) httpSet(w http.ResponseWriter, name, domain, value string, maxAge int) {
c.httpSetWithSameSite(w, name, domain, value, maxAge, c.sameSite)
}
func (c *CookieHandler) httpSetWithSameSite(w http.ResponseWriter, name, domain, value string, maxage int, sameSite http.SameSite) {
func (c *CookieHandler) httpSetWithSameSite(w http.ResponseWriter, name, host, value string, maxAge int, sameSite http.SameSite) {
domain := strings.Split(host, ":")[0]
// same site none requires the secure flag, so we'll set it even if the cookie is set on non-TLS for localhost
secure := c.secureOnly || (sameSite == http.SameSiteNoneMode && domain == "localhost")
// prefix the cookie for secure cookies (TLS only, therefore not for samesite none on http://localhost)
prefixedName := SetCookiePrefix(name, c.secureOnly, c.prefix)
http.SetCookie(w, &http.Cookie{
Name: SetCookiePrefix(name, domain, c.path, c.secureOnly),
Name: prefixedName,
Value: value,
Domain: strings.Split(domain, ":")[0],
Domain: domain,
Path: c.path,
MaxAge: maxage,
MaxAge: maxAge,
HttpOnly: c.httpOnly,
Secure: c.secureOnly,
Secure: secure,
SameSite: sameSite,
})
varyValues := w.Header().Values("vary")

View File

@@ -46,6 +46,7 @@ func NewUserAgentHandler(config *UserAgentCookieConfig, cookieKey []byte, idGene
opts := []http_utils.CookieHandlerOpt{
http_utils.WithEncryption(cookieKey, cookieKey),
http_utils.WithMaxAge(int(config.MaxAge.Seconds())),
http_utils.WithPrefix(http_utils.PrefixSecure),
}
if !externalSecure {
opts = append(opts, http_utils.WithUnsecure())

View File

@@ -8,6 +8,7 @@ import (
"github.com/gorilla/csrf"
"github.com/gorilla/mux"
"github.com/zitadel/zitadel/feature"
"github.com/zitadel/zitadel/internal/api/authz"
http_utils "github.com/zitadel/zitadel/internal/api/http"
@@ -122,13 +123,21 @@ func createCSRFInterceptor(cookieName string, csrfCookieKey []byte, externalSecu
handler.ServeHTTP(w, r)
return
}
// by default we use SameSite Lax and the externalSecure (TLS) for the secure flag
sameSiteMode := csrf.SameSiteLaxMode
if len(authz.GetInstance(r.Context()).SecurityPolicyAllowedOrigins()) > 0 {
secureOnly := externalSecure
instance := authz.GetInstance(r.Context())
// in case of `allow iframe`...
if len(instance.SecurityPolicyAllowedOrigins()) > 0 {
// ... we need to change to SameSite none ...
sameSiteMode = csrf.SameSiteNoneMode
// ... and since SameSite none requires the secure flag, we'll set it for TLS and for localhost
// (regardless of the TLS / externalSecure settings)
secureOnly = externalSecure || instance.RequestedDomain() == "localhost"
}
csrf.Protect(csrfCookieKey,
csrf.Secure(externalSecure),
csrf.CookieName(http_utils.SetCookiePrefix(cookieName, "", path, externalSecure)),
csrf.Secure(secureOnly),
csrf.CookieName(http_utils.SetCookiePrefix(cookieName, externalSecure, http_utils.PrefixHost)),
csrf.Path(path),
csrf.ErrorHandler(errorHandler),
csrf.SameSite(sameSiteMode),