1a7b4e1d39c69f33d6d78d1f3ca61b783d2ded40 max Tue Aug 11 08:08:50 2026 -0700 hgLogin: escape reflected values on the older login/account form pages (XSS) The pre-social-login pages printed cart and CGI values into HTML with a plain %s. Since every CGI parameter becomes a cart variable, a crafted URL could reflect script into the page, and the login cookie is written by JavaScript (not HttpOnly), so injected script could read it. Wrap the reflected values in htmlEncode() at the point of output, matching the existing encXxx pattern in the file. Covers displayLoginPage, displayAccHelpPage, changePasswordPage, signupPage, displayMailSuccess and displayMailSuccessPwd. refs #38011 diff --git src/hg/hgLogin/hgLogin.c src/hg/hgLogin/hgLogin.c index c16fd4b7a7f..50993c7fd99 100644 --- src/hg/hgLogin/hgLogin.c +++ src/hg/hgLogin/hgLogin.c @@ -427,52 +427,52 @@ hPrintf( "<h2>%s</h2>", brwName); hPrintf( "<p align=\"left\">" "</p>" "<h3>Error emailing to: %s</h3>" "Click <a href=%s?hgLogin.do.displayAccHelpPage=1>here</a> to return.<br>", hgLoginUrl, email ); exit(0); } } void displayMailSuccess() /* display mail success confirmation box */ { -char *sendMailTo = cartUsualString(cart, "hgLogin_sendMailTo", ""); +char *sendMailTo = htmlEncode(cartUsualString(cart, "hgLogin_sendMailTo", "")); // printed into the page; escape (XSS) hPrintf( "<div id=\"confirmationBox\" class=\"centeredContainer formBox\">" "<h2>%s</h2>", brwName); hPrintf( "<p id=\"confirmationMsg\" class=\"confirmationTxt\">All usernames on file (if any) for <B>%s</B> " "have been sent to that address.<BR><BR>" " If <B>%s</B> is not your registered email address, you will not receive an email." " If you can't find the message we sent you, please contact %s for help.</p>", sendMailTo, sendMailTo, returnAddr); hPrintf("<p><a href=\"%s?hgLogin.do.displayLoginPage=1\">Return to Login</a></p>\n", hgLoginUrl); cartRemove(cart, "hgLogin_helpWith"); cartRemove(cart, "hgLogin_email"); cartRemove(cart, "hgLogin_userName"); cartRemove(cart, "hgLogin_sendMailTo"); cartRemove(cart, "hgLogin_sendMailContain"); } void displayMailSuccessPwd() /* display mail success confirmation box */ { -char *username = cgiUsualString("user",""); +char *username = htmlEncode(cgiUsualString("user","")); // printed into the page; escape (XSS) hPrintf( "<div id=\"confirmationBoxPwd\" class=\"centeredContainer formBox\">" "<h2>%s</h2>", brwName); char *contactAddr = returnAddr; if (sameString(returnAddr, "NOEMAIL")) contactAddr = "the administrator of this Genome Browser Mirror"; hPrintf( "<p id=\"confirmationMsgPwd\" class=\"confirmationTxt\">An email containing password reset information has been sent to the registered email address of <B>%s</B>.<BR><BR>" " If you do not receive an email, please contact %s for help.</p>", username, contactAddr); if (sameString(returnAddr, "NOEMAIL")) hPrintf("<p>If you still have questions, you can contact the Genome Browser team at " "genome-www@soe.ucsc.edu. As this is a mirror website not managed by UCSC, please " "specify the address of the mirror in your email.</p>"); @@ -583,32 +583,33 @@ { char subject[256]; char msg[4096]; char *remoteAddr=getenv("REMOTE_ADDR"); safef(subject, sizeof(subject),"New temporary password for your account at the %s", brwName); safef(msg, sizeof(msg), " Someone (probably you, from IP address %s) requested a new password for the %s (%s). A temporary password for user \"%s\" has been created and was set to \"%s\". If this was your intent, you will need to log in and choose a new password now. Your temporary password will expire in 7 days.\n\n If someone else made this request, or if you have remembered your password, and you no longer wish to change it, you may ignore this message and continue using your old password.\n\n%s\n%s", remoteAddr, brwName, brwAddr, username, password, signature, returnAddr); sendPwdMailOut(email, recovEmail, subject, msg, username); } void displayAccHelpPage(struct sqlConnection *conn) /* draw the account help page */ { -char *email = cartUsualString(cart, "hgLogin_email", ""); -char *username = cartUsualString(cart, "hgLogin_userName", ""); +// these go into value="" attributes further down; escape them (reflected XSS) +char *email = htmlEncode(cartUsualString(cart, "hgLogin_email", "")); +char *username = htmlEncode(cartUsualString(cart, "hgLogin_userName", "")); jsInline( "function toggle(value){\n" "if(value=='showE'){\n" " document.getElementById('usernameBox').style.display='none';\n" " document.getElementById('emailAddrBox').style.display='inline';\n" " } else {\n" " document.getElementById('usernameBox').style.display='inline';\n" " document.getElementById('emailAddrBox').style.display='none';\n" " }\n" "}\n" ); hPrintf("<div id=\"accountHelpBox\" class=\"centeredContainer formBox\">" "\n" "<h2>%s</h2>" @@ -771,31 +772,32 @@ " var slash = document.getElementById(slashId);\n" " if (inp.type === 'password') {\n" " inp.type = 'text';\n" " slash.style.display = 'inline';\n" " } else {\n" " inp.type = 'password';\n" " slash.style.display = 'none';\n" " }\n" "}\n" ); } void displayLoginPage(struct sqlConnection *conn) /* draw the account login page */ { -char *username = cartUsualString(cart, "hgLogin_userName", ""); +// goes into a value="" attribute further down; escape it (reflected XSS) +char *username = htmlEncode(cartUsualString(cart, "hgLogin_userName", "")); hPrintf("<div id=\"loginBox\" class=\"centeredContainer formBox\">" "\n" "<h2>%s</h2>" "\n", brwName); hPrintf( "<h3>Login</h3>" "<p>Do not have an account? <a href=\"%s?hgLogin.do.signupPage=1\">Go to the sign up page</a>.</p>" "\n", hgLoginUrl); if (errMsg && sameString(errMsg, "Your account has been activated.")) hPrintf("<span style='color:green;'>%s</span>\n", errMsg ? errMsg : ""); else hPrintf("<span style='color:red;'>%s</span>\n", errMsg ? errMsg : ""); hPrintf("<form method=post action=\"%s\" name=\"accountLoginForm\" id=\"accountLoginForm\">" "\n" "<div class=\"inputGroup\">" @@ -873,31 +875,31 @@ hPrintf("<div id=\"changePwBox\" class=\"centeredContainer formBox\">" "\n" "<h2>%s</h2>", brwName); hPrintf( "<h3>Change Password</h3>" "\n" "<p> <span style='color:red;'>%s</span> </p>" "\n" "<form method=\"post\" action=\"%s\" name=\"changePasswordForm\" id=\"changePasswordForm\">" "\n" "<div class=\"inputGroup\">" "<label for=\"userName\">Username</label>" "<input type=\"text\" name=\"hgLogin_userName\" size=\"30\" value=\"%s\" id=\"email\">" "</div>" "\n", errMsg ? errMsg : "", hgLoginUrl, - cartUsualString(cart, "hgLogin_userName", "")); + htmlEncode(cartUsualString(cart, "hgLogin_userName", ""))); // value="" attribute; escape (XSS) hPrintf("<div class=\"inputGroup\">" "\n" "<label for=\"currentPw\">Current or Emailed Password</label>" "<span style=\"display:inline-flex; align-items:center;\">" "<input type=\"password\" name=\"hgLogin_password\" value=\"\" size=\"30\" id=\"currentPw\">"); printPwdEyeIcon("curPwEyeIcon", "curPwEyeSlash"); hPrintf( "</span>" "</div>" "\n" "<div class=\"inputGroup\">" "<label for=\"newPw1\">New Password</label>" "<span style=\"display:inline-flex; align-items:center;\">" "<input type=\"password\" name=\"hgLogin_newPassword1\" value=\"\" size=\"30\" id=\"newPw1\">"); printPwdEyeIcon("newPw1EyeIcon", "newPw1EyeSlash"); @@ -1281,55 +1283,56 @@ printUsernameNote(); hPrintf("<div class=\"inputGroup\">" "<label for=\"userName\">Username</label>" "<input type=text name=\"hgLogin_userName\" value=\"%s\" size=\"30\" id=\"userName\">" "</div>" "\n" "<div class=\"inputGroup\">" "<label for=\"emailAddr\">Email address</label>" "<input type=text name=\"hgLogin_email\" value=\"%s\" size=\"30\" id=\"emailAddr\">" "</div>" "\n" "<div class=\"inputGroup\">" "<label for=\"reenterEmail\">Re-enter Email address</label>" "<input type=text name=\"hgLogin_email2\" value=\"%s\" size=\"30\" id=\"emailCheck\">" "</div>\n", - cartUsualString(cart, "hgLogin_userName", ""), cartUsualString(cart, "hgLogin_email", ""), - cartUsualString(cart, "hgLogin_email2", "")); + htmlEncode(cartUsualString(cart, "hgLogin_userName", "")), // all three go into value="" attributes; escape (XSS) + htmlEncode(cartUsualString(cart, "hgLogin_email", "")), + htmlEncode(cartUsualString(cart, "hgLogin_email2", ""))); if (sqlFieldIndex(conn, "gbMembers", "recovEmail") != -1) hPrintf("<div class=\"inputGroup\">" "<label for=\"recovEmail\">Optional Secondary Recovery Email</label>" "<input type=text name=\"hgLogin_recovEmail\" size=\"30\" id=\"recovEmail\">" "</div>" "\n"); hPrintf("<div class=\"inputGroup\">" "<label for=\"password\">Password <small>(must be at least 5 characters)</small></label>" "<span style=\"display:inline-flex; align-items:center;\">" "<input type=password name=\"hgLogin_password\" value=\"%s\" size=\"30\" id=\"password\">", - cartUsualString(cart, "hgLogin_password", "")); + htmlEncode(cartUsualString(cart, "hgLogin_password", ""))); // value="" attribute; escape (XSS) printPwdEyeIcon("signupPwEyeIcon", "signupPwEyeSlash"); hPrintf( "</span>" "</div>" "\n" "<div class=\"inputGroup\">" "<label for=\"passwordCheck\">Re-enter Password</label>" "<span style=\"display:inline-flex; align-items:center;\">" "<input type=password name=\"hgLogin_password2\" value=\"%s\" size=\"30\" id=\"passwordCheck\">", - cartUsualString(cart, "hgLogin_password2", "")); + htmlEncode(cartUsualString(cart, "hgLogin_password2", ""))); // value="" attribute; escape (XSS) printPwdEyeIcon("signupPwCheckEyeIcon", "signupPwCheckEyeSlash"); hPrintf( "</span>" "\n" "</div>" "\n" "<div class=\"formControls\">" " <input type=\"submit\" name=\"hgLogin.do.signup\" value=\"Sign Up using Email\" class=\"largeButton\"> " " <a href=\"%s\" class=\"cancelButton\">Cancel</a>" "</div>" "</form>" "</div><!-- END - signUpBox -->", getReturnToURL()); if (pwdEyeIconEnabled) {