@@ -437,6 +437,7 @@ func (state *RuntimeState) writeFailureResponse(w http.ResponseWriter, r *http.R
437437 }
438438 w .WriteHeader (code )
439439 publicErrorText := fmt .Sprintf ("%d %s %s\n " , code , http .StatusText (code ), message )
440+ setSecurityHeaders (w )
440441 switch code {
441442
442443 case http .StatusUnauthorized :
@@ -456,7 +457,7 @@ func (state *RuntimeState) writeFailureResponse(w http.ResponseWriter, r *http.R
456457 if r .Method == "POST" {
457458 /// assume it has been parsed... otherwise why are we here?
458459 if r .Form .Get ("login_destination" ) != "" {
459- loginDestnation = r . Form . Get ( "login_destination" )
460+ loginDestnation = getLoginDestination ( r )
460461 }
461462 }
462463 if authCookie == nil {
@@ -489,6 +490,14 @@ func (state *RuntimeState) writeFailureResponse(w http.ResponseWriter, r *http.R
489490 }
490491}
491492
493+ func setSecurityHeaders (w http.ResponseWriter ) {
494+ //all common security headers go here
495+ w .Header ().Set ("Strict-Transport-Security" , "max-age=1209600" )
496+ w .Header ().Set ("X-Frame-Options" , "DENY" )
497+ w .Header ().Set ("X-XSS-Protection" , "1" )
498+ w .Header ().Set ("Content-Security-Policy" , "default-src 'self' ;style-src 'self' fonts.googleapis.com 'unsafe-inline'; font-src fonts.gstatic.com fonts.googleapis.com" )
499+ }
500+
492501// returns true if the system is locked and sends message to the requester
493502func (state * RuntimeState ) sendFailureToClientIfLocked (w http.ResponseWriter , r * http.Request ) bool {
494503 var signerIsNull bool
@@ -497,12 +506,7 @@ func (state *RuntimeState) sendFailureToClientIfLocked(w http.ResponseWriter, r
497506 signerIsNull = (state .Signer == nil )
498507 state .Mutex .Unlock ()
499508
500- //all common security headers go here
501- w .Header ().Set ("Strict-Transport-Security" , "max-age=31536" )
502- w .Header ().Set ("X-Frame-Options" , "DENY" )
503- w .Header ().Set ("X-XSS-Protection" , "1" )
504- //w.Header().Set("Content-Security-Policy", "default-src 'none'; script-src 'self' code.jquery.com; connect-src 'self'; img-src 'self'; style-src 'self';")
505- w .Header ().Set ("Content-Security-Policy" , "default-src 'self' ;style-src 'self' fonts.googleapis.com 'unsafe-inline'; font-src fonts.gstatic.com fonts.googleapis.com" )
509+ setSecurityHeaders (w )
506510
507511 if signerIsNull {
508512 state .writeFailureResponse (w , r , http .StatusInternalServerError , "" )
@@ -1089,6 +1093,7 @@ func (state *RuntimeState) publicPathHandler(w http.ResponseWriter, r *http.Requ
10891093 case "loginForm" :
10901094 w .WriteHeader (200 )
10911095 //fmt.Fprintf(w, "%s", loginFormText)
1096+ setSecurityHeaders (w )
10921097 state .writeHTMLLoginPage (w , r , profilePath , "" )
10931098 return
10941099 case "x509ca" :
@@ -1151,6 +1156,21 @@ func (state *RuntimeState) startVIPPush(cookieVal string, username string) error
11511156 return nil
11521157}
11531158
1159+ // We need to ensure that all login destinations are relative paths
1160+ // Thus the path MUST start with a / but MUST NOT start with a //, because
1161+ // // is interpreted as: use whatever protocol you think is OK
1162+ func getLoginDestination (r * http.Request ) string {
1163+ loginDestination := profilePath
1164+ if r .Form .Get ("login_destination" ) != "" {
1165+ inboundLoginDestination := r .Form .Get ("login_destination" )
1166+ if strings .HasPrefix (inboundLoginDestination , "/" ) &&
1167+ ! strings .HasPrefix (inboundLoginDestination , "//" ) {
1168+ loginDestination = inboundLoginDestination
1169+ }
1170+ }
1171+ return loginDestination
1172+ }
1173+
11541174//const loginPath = "/api/v0/login"
11551175
11561176func (state * RuntimeState ) loginHandler (w http.ResponseWriter , r * http.Request ) {
@@ -1275,11 +1295,7 @@ func (state *RuntimeState) loginHandler(w http.ResponseWriter, r *http.Request)
12751295 CertAuthBackend : certBackends }
12761296 switch returnAcceptType {
12771297 case "text/html" :
1278- loginDestination := profilePath
1279- if r .Form .Get ("login_destination" ) != "" {
1280- loginDestination = r .Form .Get ("login_destination" )
1281- }
1282-
1298+ loginDestination := getLoginDestination (r )
12831299 requiredAuth := state .getRequiredWebUIAuthLevel ()
12841300 if (requiredAuth & AuthTypePassword ) != 0 {
12851301 eventNotifier .PublishWebLoginEvent (username )
@@ -1455,10 +1471,7 @@ func (state *RuntimeState) VIPAuthHandler(w http.ResponseWriter, r *http.Request
14551471 loginResponse := proto.LoginResponse {Message : "success" } //CertAuthBackend: certBackends
14561472 switch returnAcceptType {
14571473 case "text/html" :
1458- loginDestination := profilePath
1459- if r .Form .Get ("login_destination" ) != "" {
1460- loginDestination = r .Form .Get ("login_destination" )
1461- }
1474+ loginDestination := getLoginDestination (r )
14621475 eventNotifier .PublishWebLoginEvent (authUser )
14631476 http .Redirect (w , r , loginDestination , 302 )
14641477 default :
@@ -2247,6 +2260,7 @@ func (state *RuntimeState) serveClientConfHandler(w http.ResponseWriter, r *http
22472260}
22482261
22492262func (state * RuntimeState ) defaultPathHandler (w http.ResponseWriter , r * http.Request ) {
2263+ setSecurityHeaders (w )
22502264 //redirect to profile
22512265 if r .URL .Path [:] == "/" {
22522266 //landing page
0 commit comments