enhancement: same site strict cookies (#8716)

To enhance the security of our application and prevent Cross-Site Request Forgery (CSRF) attacks, we have updated the
SameSite attribute of the build in Identity Provider (IDP) cookies to Strict.
This commit is contained in:
Florian Schade
2024-03-25 10:16:10 +01:00
committed by GitHub
parent 65f77c2aa0
commit 6840de574a
18 changed files with 136 additions and 56 deletions

View File

@@ -0,0 +1,12 @@
Enhancement: Make IDP cookies same site strict
To enhance the security of our application and prevent Cross-Site Request Forgery (CSRF) attacks, we have updated the
SameSite attribute of the build in Identity Provider (IDP) cookies to Strict.
This change restricts the browser from sending these cookies with any cross-site requests,
thereby limiting the exposure of the user's session to potential threats.
This update does not impact the existing functionality of the application but provides an additional layer of security
where needed.
https://github.com/owncloud/ocis/pull/8716

2
go.mod
View File

@@ -57,7 +57,7 @@ require (
github.com/justinas/alice v1.2.0
github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1
github.com/libregraph/idm v0.4.1-0.20231213140724-56a222fb4215
github.com/libregraph/lico v0.61.2
github.com/libregraph/lico v0.61.3-0.20240322112242-72cf9221d3a7
github.com/mitchellh/mapstructure v1.5.0
github.com/mna/pigeon v1.2.1
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826

4
go.sum
View File

@@ -1618,8 +1618,8 @@ github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1 h1:k56sFO
github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1/go.mod h1:AT4NpQrOmyj1L/+hLja6aR0lk81yYYL4ePnj2kp7d6M=
github.com/libregraph/idm v0.4.1-0.20231213140724-56a222fb4215 h1:Yw/I6l/0S/zDq2Hnibvwy8cVLpMaBwDe0aUSv/FNU6U=
github.com/libregraph/idm v0.4.1-0.20231213140724-56a222fb4215/go.mod h1:h/B7mB5OqrsrobydErMGewHxonYDKjGOaJsFabXyRo8=
github.com/libregraph/lico v0.61.2 h1:sU8eQ2E9Uq5wnTkD33YX5+gRj59MkPLgDVoB72L1q8w=
github.com/libregraph/lico v0.61.2/go.mod h1:TgZGBAYzVRQSRdBC8PgGQKjYhtXuTr6UCM3ZZyGTleQ=
github.com/libregraph/lico v0.61.3-0.20240322112242-72cf9221d3a7 h1:fcPgiBu7DGyGeokE0Qk+S+GW/3n+QWu1dIjw0TqadhI=
github.com/libregraph/lico v0.61.3-0.20240322112242-72cf9221d3a7/go.mod h1:TgZGBAYzVRQSRdBC8PgGQKjYhtXuTr6UCM3ZZyGTleQ=
github.com/libregraph/oidc-go v1.0.0 h1:l2tE/EwLyLXVy0B5BuVKgIFX9pNpz/5J3x5IBw0KEhc=
github.com/libregraph/oidc-go v1.0.0/go.mod h1:7TRHrY/H1Vg6JqFjV0oAe1+kN+mGFBqXYvclSyvhRyc=
github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE=

View File

@@ -25,6 +25,7 @@ import (
"github.com/libregraph/lico/identifier"
"github.com/libregraph/lico/identity"
"github.com/libregraph/lico/identity/managers"
cs3 "github.com/owncloud/ocis/v2/services/idp/pkg/backends/cs3/identifier"
)
@@ -88,12 +89,18 @@ func NewIdentityManager(bs bootstrap.Bootstrap) (identity.Manager, error) {
activeIdentifier, err := identifier.NewIdentifier(&identifier.Config{
Config: config.Config,
BaseURI: config.IssuerIdentifierURI,
PathPrefix: bs.MakeURIPath(bootstrap.APITypeSignin, ""),
StaticFolder: config.IdentifierClientPath,
LogonCookieName: "__Secure-KKT", // Kopano-Konnect-Token
ScopesConf: config.IdentifierScopesConf,
WebAppDisabled: config.IdentifierClientDisabled,
BaseURI: config.IssuerIdentifierURI,
PathPrefix: bs.MakeURIPath(bootstrap.APITypeSignin, ""),
StaticFolder: config.IdentifierClientPath,
ScopesConf: config.IdentifierScopesConf,
WebAppDisabled: config.IdentifierClientDisabled,
LogonCookieName: "__Secure-KKT", // Kopano-Konnect-Token
LogonCookieSameSite: config.CookieSameSite,
ConsentCookieSameSite: config.CookieSameSite,
StateCookieSameSite: config.CookieSameSite,
AuthorizationEndpointURI: fullAuthorizationEndpointURL,
SignedOutEndpointURI: fullSignedOutEndpointURL,

View File

@@ -2,6 +2,7 @@ package config
import (
"context"
"net/http"
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
)
@@ -112,6 +113,7 @@ type Settings struct {
CookieBackendURI string
CookieNames []string
CookieSameSite http.SameSite
AccessTokenDurationSeconds uint64 `yaml:"access_token_duration_seconds" env:"IDP_ACCESS_TOKEN_EXPIRATION" desc:"'Access token lifespan in seconds (time before an access token is expired).'" introductionVersion:"pre5.0"`
IDTokenDurationSeconds uint64 `yaml:"id_token_duration_seconds" env:"IDP_ID_TOKEN_EXPIRATION" desc:"ID token lifespan in seconds (time before an ID token is expired)." introductionVersion:"pre5.0"`

View File

@@ -1,6 +1,7 @@
package defaults
import (
"net/http"
"path/filepath"
"strings"
@@ -64,6 +65,7 @@ func DefaultConfig() *config.Config {
ValidationKeysPath: "",
CookieBackendURI: "",
CookieNames: nil,
CookieSameSite: http.SameSiteStrictMode,
AccessTokenDurationSeconds: 60 * 5, // 5 minutes
IDTokenDurationSeconds: 60 * 5, // 5 minutes
RefreshTokenDurationSeconds: 60 * 60 * 24 * 30, // 30 days

View File

@@ -127,12 +127,18 @@ func NewIdentityManager(bs bootstrap.Bootstrap) (identity.Manager, error) {
activeIdentifier, err := identifier.NewIdentifier(&identifier.Config{
Config: config.Config,
BaseURI: config.IssuerIdentifierURI,
PathPrefix: bs.MakeURIPath(bootstrap.APITypeSignin, ""),
StaticFolder: config.IdentifierClientPath,
LogonCookieName: "__Secure-KKT", // Kopano-Konnect-Token
ScopesConf: config.IdentifierScopesConf,
WebAppDisabled: config.IdentifierClientDisabled,
BaseURI: config.IssuerIdentifierURI,
PathPrefix: bs.MakeURIPath(bootstrap.APITypeSignin, ""),
StaticFolder: config.IdentifierClientPath,
ScopesConf: config.IdentifierScopesConf,
WebAppDisabled: config.IdentifierClientDisabled,
LogonCookieName: "__Secure-KKT", // Kopano-Konnect-Token
LogonCookieSameSite: config.CookieSameSite,
ConsentCookieSameSite: config.CookieSameSite,
StateCookieSameSite: config.CookieSameSite,
AuthorizationEndpointURI: fullAuthorizationEndpointURL,
SignedOutEndpointURI: fullSignedOutEndpointURL,

View File

@@ -110,12 +110,18 @@ func NewIdentityManager(bs bootstrap.Bootstrap) (identity.Manager, error) {
activeIdentifier, err := identifier.NewIdentifier(&identifier.Config{
Config: config.Config,
BaseURI: config.IssuerIdentifierURI,
PathPrefix: bs.MakeURIPath(bootstrap.APITypeSignin, ""),
StaticFolder: config.IdentifierClientPath,
LogonCookieName: "__Secure-KKT", // Kopano-Konnect-Token
ScopesConf: config.IdentifierScopesConf,
WebAppDisabled: config.IdentifierClientDisabled,
BaseURI: config.IssuerIdentifierURI,
PathPrefix: bs.MakeURIPath(bootstrap.APITypeSignin, ""),
StaticFolder: config.IdentifierClientPath,
ScopesConf: config.IdentifierScopesConf,
WebAppDisabled: config.IdentifierClientDisabled,
LogonCookieName: "__Secure-KKT", // Kopano-Konnect-Token
LogonCookieSameSite: config.CookieSameSite,
ConsentCookieSameSite: config.CookieSameSite,
StateCookieSameSite: config.CookieSameSite,
AuthorizationEndpointURI: fullAuthorizationEndpointURL,
SignedOutEndpointURI: fullSignedOutEndpointURL,

View File

@@ -26,6 +26,7 @@ import (
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
@@ -58,6 +59,7 @@ const (
DefaultSigningKeyBits = 2048
DefaultGuestIdentityManagerName = "guest"
DefaultCookieSameSite = http.SameSiteNoneMode
)
// Bootstrap is a data structure to hold configuration required to start
@@ -332,6 +334,12 @@ func (bs *bootstrap) initialize(settings *Settings) error {
}
bs.config.DyamicClientSecretDurationSeconds = settings.DyamicClientSecretDurationSeconds
// add setting to allow setting the same site attribute of the cookies
bs.config.CookieSameSite = settings.CookieSameSite
if bs.config.CookieSameSite == 0 {
bs.config.CookieSameSite = DefaultCookieSameSite
}
return nil
}
@@ -472,11 +480,13 @@ func (bs *bootstrap) setupOIDCProvider(ctx context.Context) (*oidcProvider.Provi
CheckSessionIframePath: bs.MakeURIPath(APITypeKonnect, "/session/check-session.html"),
RegistrationPath: registrationPath,
BrowserStateCookiePath: bs.MakeURIPath(APITypeKonnect, "/session/"),
BrowserStateCookieName: "__Secure-KKBS", // Kopano-Konnect-Browser-State
BrowserStateCookiePath: bs.MakeURIPath(APITypeKonnect, "/session/"),
BrowserStateCookieName: "__Secure-KKBS", // Kopano-Konnect-Browser-State
BrowserStateCookieSameSite: bs.config.CookieSameSite,
SessionCookiePath: sessionCookiePath,
SessionCookieName: "__Secure-KKCS", // Kopano-Konnect-Client-Session
SessionCookiePath: sessionCookiePath,
SessionCookieName: "__Secure-KKCS", // Kopano-Konnect-Client-Session
SessionCookieSameSite: bs.config.CookieSameSite,
AccessTokenDuration: time.Duration(bs.config.AccessTokenDurationSeconds) * time.Second,
IDTokenDuration: time.Duration(bs.config.IDTokenDurationSeconds) * time.Second,

View File

@@ -21,6 +21,7 @@ import (
"crypto"
"crypto/tls"
"crypto/x509"
"net/http"
"net/url"
"github.com/golang-jwt/jwt/v4"
@@ -64,4 +65,6 @@ type Config struct {
IDTokenDurationSeconds uint64
RefreshTokenDurationSeconds uint64
DyamicClientSecretDurationSeconds uint64
CookieSameSite http.SameSite
}

View File

@@ -17,6 +17,10 @@
package bootstrap
import (
"net/http"
)
// Settings is a typed application config which represents the user accessible
// boostrap settings params.
type Settings struct {
@@ -48,6 +52,7 @@ type Settings struct {
ValidationKeysPath string
CookieBackendURI string
CookieNames []string
CookieSameSite http.SameSite
AccessTokenDurationSeconds uint64
IDTokenDurationSeconds uint64
RefreshTokenDurationSeconds uint64

View File

@@ -18,6 +18,7 @@
package identifier
import (
"net/http"
"net/url"
"github.com/libregraph/lico/config"
@@ -28,9 +29,15 @@ import (
type Config struct {
Config *config.Config
BaseURI *url.URL
LogonCookieName string
ScopesConf string
BaseURI *url.URL
ScopesConf string
LogonCookieName string
LogonCookieSameSite http.SameSite
ConsentCookieSameSite http.SameSite
StateCookieSameSite http.SameSite
PathPrefix string
StaticFolder string

View File

@@ -37,7 +37,7 @@ func (i *Identifier) setLogonCookie(rw http.ResponseWriter, value string) error
Path: i.pathPrefix + "/identifier/_/",
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
SameSite: i.logonCookieSameSite,
}
http.SetCookie(rw, &cookie)
@@ -55,7 +55,7 @@ func (i *Identifier) removeLogonCookie(rw http.ResponseWriter) error {
Path: i.pathPrefix + "/identifier/_/",
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
SameSite: i.logonCookieSameSite,
Expires: farPastExpiryTime,
}
@@ -78,7 +78,7 @@ func (i *Identifier) setConsentCookie(rw http.ResponseWriter, cr *ConsentRequest
Path: i.pathPrefix + "/identifier/_/",
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
SameSite: i.consentCookieSameSite,
}
http.SetCookie(rw, &cookie)
@@ -106,7 +106,7 @@ func (i *Identifier) removeConsentCookie(rw http.ResponseWriter, req *http.Reque
Path: i.pathPrefix + "/identifier/_/",
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
SameSite: i.consentCookieSameSite,
Expires: farPastExpiryTime,
}
@@ -150,7 +150,7 @@ func (i *Identifier) setStateCookie(rw http.ResponseWriter, scope string, state
Path: i.pathPrefix + "/identifier/" + scope,
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
SameSite: i.stateCookieSameSite,
}
http.SetCookie(rw, &cookie)
@@ -178,7 +178,7 @@ func (i *Identifier) removeStateCookie(rw http.ResponseWriter, req *http.Request
Path: i.pathPrefix + "/identifier/" + scope,
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
SameSite: i.stateCookieSameSite,
Expires: farPastExpiryTime,
}

View File

@@ -32,8 +32,10 @@ import (
"github.com/gorilla/mux"
"github.com/longsleep/rndm"
"github.com/sirupsen/logrus"
jose "gopkg.in/square/go-jose.v2"
jwt "gopkg.in/square/go-jose.v2/jwt"
"gopkg.in/square/go-jose.v2"
"gopkg.in/square/go-jose.v2/jwt"
"github.com/libregraph/oidc-go"
konnect "github.com/libregraph/lico"
"github.com/libregraph/lico/identifier/backends"
@@ -44,7 +46,6 @@ import (
"github.com/libregraph/lico/identity/clients"
"github.com/libregraph/lico/managers"
"github.com/libregraph/lico/utils"
"github.com/libregraph/oidc-go"
)
// audienceMarker defines the value which gets included in logon cookies. Valid
@@ -62,10 +63,16 @@ type Identifier struct {
baseURI *url.URL
pathPrefix string
staticFolder string
logonCookieName string
scopesConf string
webappIndexHTML []byte
logonCookieName string
logonCookieSameSite http.SameSite
consentCookieSameSite http.SameSite
stateCookieSameSite http.SameSite
authorizationEndpointURI *url.URL
signedOutEndpointURI *url.URL
oauth2CbEndpointURI *url.URL
@@ -114,10 +121,16 @@ func NewIdentifier(c *Config) (*Identifier, error) {
baseURI: c.BaseURI,
pathPrefix: c.PathPrefix,
staticFolder: staticFolder,
logonCookieName: c.LogonCookieName,
scopesConf: c.ScopesConf,
webappIndexHTML: webappIndexHTML,
logonCookieName: c.LogonCookieName,
logonCookieSameSite: c.LogonCookieSameSite,
consentCookieSameSite: c.ConsentCookieSameSite,
stateCookieSameSite: c.StateCookieSameSite,
authorizationEndpointURI: c.AuthorizationEndpointURI,
signedOutEndpointURI: c.SignedOutEndpointURI,
oauth2CbEndpointURI: oauth2CbEndpointURI,

View File

@@ -18,6 +18,7 @@
package provider
import (
"net/http"
"time"
"github.com/libregraph/lico/config"
@@ -37,11 +38,13 @@ type Config struct {
CheckSessionIframePath string
RegistrationPath string
BrowserStateCookiePath string
BrowserStateCookieName string
BrowserStateCookiePath string
BrowserStateCookieName string
BrowserStateCookieSameSite http.SameSite
SessionCookiePath string
SessionCookieName string
SessionCookiePath string
SessionCookieName string
SessionCookieSameSite http.SameSite
AccessTokenDuration time.Duration
IDTokenDuration time.Duration

View File

@@ -29,7 +29,7 @@ func (p *Provider) setBrowserStateCookie(rw http.ResponseWriter, value string) e
Path: p.browserStateCookiePath,
Secure: true,
HttpOnly: false, // This Cookie is intended to be read by Javascript.
SameSite: http.SameSiteNoneMode,
SameSite: p.browserStateCookieSameSite,
}
http.SetCookie(rw, &cookie)
@@ -43,7 +43,7 @@ func (p *Provider) removeBrowserStateCookie(rw http.ResponseWriter) error {
Path: p.browserStateCookiePath,
Secure: true,
HttpOnly: false, // This Cookie is intended to be read by Javascript.
SameSite: http.SameSiteNoneMode,
SameSite: p.browserStateCookieSameSite,
Expires: farPastExpiryTime,
}
@@ -60,7 +60,7 @@ func (p *Provider) setSessionCookie(rw http.ResponseWriter, value string) error
Path: p.sessionCookiePath,
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
SameSite: p.sessionCookieSameSite,
}
http.SetCookie(rw, &cookie)
@@ -83,7 +83,7 @@ func (p *Provider) removeSessionCookie(rw http.ResponseWriter) error {
Path: p.sessionCookiePath,
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
SameSite: p.sessionCookieSameSite,
Expires: farPastExpiryTime,
}

View File

@@ -73,11 +73,13 @@ type Provider struct {
validationKeys map[string]crypto.PublicKey
certificates map[string][]*x509.Certificate
browserStateCookiePath string
browserStateCookieName string
browserStateCookiePath string
browserStateCookieName string
browserStateCookieSameSite http.SameSite
sessionCookiePath string
sessionCookieName string
sessionCookiePath string
sessionCookieName string
sessionCookieSameSite http.SameSite
accessTokenDuration time.Duration
idTokenDuration time.Duration
@@ -105,11 +107,13 @@ func NewProvider(c *Config) (*Provider, error) {
validationKeys: make(map[string]crypto.PublicKey),
certificates: make(map[string][]*x509.Certificate),
browserStateCookiePath: c.BrowserStateCookiePath,
browserStateCookieName: c.BrowserStateCookieName,
browserStateCookiePath: c.BrowserStateCookiePath,
browserStateCookieName: c.BrowserStateCookieName,
browserStateCookieSameSite: c.BrowserStateCookieSameSite,
sessionCookiePath: c.SessionCookiePath,
sessionCookieName: c.SessionCookieName,
sessionCookiePath: c.SessionCookiePath,
sessionCookieName: c.SessionCookieName,
sessionCookieSameSite: c.SessionCookieSameSite,
accessTokenDuration: c.AccessTokenDuration,
idTokenDuration: c.IDTokenDuration,

2
vendor/modules.txt vendored
View File

@@ -1265,7 +1265,7 @@ github.com/libregraph/idm/server
github.com/libregraph/idm/server/handler
github.com/libregraph/idm/server/handler/boltdb
github.com/libregraph/idm/server/handler/ldif
# github.com/libregraph/lico v0.61.2
# github.com/libregraph/lico v0.61.3-0.20240322112242-72cf9221d3a7
## explicit; go 1.18
github.com/libregraph/lico
github.com/libregraph/lico/bootstrap