Skip to content
This repository was archived by the owner on Jan 16, 2026. It is now read-only.

Commit df28702

Browse files
authored
Merge pull request #161 from cviecco/fix-xss-admin-port
Fix xss admin port
2 parents caad663 + 3eca143 commit df28702

3 files changed

Lines changed: 33 additions & 18 deletions

File tree

cmd/keymasterd/adminDashboard.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func (dashboard *adminDashboardType) ServeHTTP(w http.ResponseWriter,
2727
}
2828
writer := bufio.NewWriter(w)
2929
defer writer.Flush()
30+
setSecurityHeaders(w)
3031
fmt.Fprintln(writer, "<title>keymaster status page</title>")
3132
fmt.Fprintln(writer, "<body>")
3233
fmt.Fprintln(writer, "<center>")

cmd/keymasterd/main.go

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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
493502
func (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

11561176
func (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

22492262
func (state *RuntimeState) defaultPathHandler(w http.ResponseWriter, r *http.Request) {
2263+
setSecurityHeaders(w)
22502264
//redirect to profile
22512265
if r.URL.Path[:] == "/" {
22522266
//landing page

cmd/keymasterd/templateData.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ const loginFormText = `
6969
{{template "login_pre_password" .}}
7070
<form enctype="application/x-www-form-urlencoded" action="/api/v0/login" method="post">
7171
<p>Username: <INPUT TYPE="text" NAME="username" SIZE=18></p>
72-
<p>Password: <INPUT TYPE="password" NAME="password" SIZE=18></p>
72+
<p>Password: <INPUT TYPE="password" NAME="password" SIZE=18 autocomplete="off"></p>
7373
<INPUT TYPE="hidden" NAME="login_destination" VALUE={{.LoginDestination}}>
7474
<p><input type="submit" value="Submit" /></p>
7575
</form>
@@ -117,7 +117,7 @@ const secondFactorAuthFormText = `
117117
<div id="vip_login_destination" style="display: none;">{{.LoginDestination}}</div>
118118
<form enctype="application/x-www-form-urlencoded" action="/api/v0/vipAuth" method="post">
119119
<p>
120-
Enter VIP token value: <INPUT TYPE="text" NAME="OTP" SIZE=18>
120+
Enter VIP token value: <INPUT TYPE="text" NAME="OTP" SIZE=18 autocomplete="off">
121121
<INPUT TYPE="hidden" NAME="login_destination" VALUE={{.LoginDestination}}>
122122
<input type="submit" value="Submit" />
123123
</p>

0 commit comments

Comments
 (0)