File: jans-auth-server/server/src/main/java/io/jans/as/server/session/ ws/rs/EndSessionUtils.java:77-101
createFronthannelHtml concatenates the attacker-controlled state
parameter into the body of a <script> block that calls window.location:
if (!Util.isNullOrEmpty(postLogoutUrl)) {
if (!Util.isNullOrEmpty(state)) {
if (postLogoutUrl.contains("?")) {
postLogoutUrl += "&state=" + state;
} else {
postLogoutUrl += "?state=" + state;
}
}
html += "<script>" +
"window.onload=function() {" +
"window.location='" + postLogoutUrl + "'" + // <-- JS context
"}" +
"</script>";
}
The state parameter is attacker-controlled by OAuth/OIDC spec design
(it round-trips through the user-agent). Because the injection point is
a JavaScript string literal (not an HTML attribute), HTML-context
escaping does not help — the attacker breaks out of the JS string with
a single quote and executes arbitrary JavaScript in the AS's origin.
Crafted URL (victim with active session follows it):
/jans-auth/restv1/end_session?id_token_hint=<valid token> &post_logout_redirect_uri=https://target.example/ &state='+alert(document.cookie)+'
Injected JS runs in the AS's origin. Can exfiltrate cookies/session
tokens, read localStorage (where OAuth client tokens may be stashed),
and issue same-origin fetches with the user's credentials.
Default deployments have no browser-side mitigation. The default Apache
template at community-edition-setup/templates/apache/https_gluu.conf has
the Content-Security-Policy header commented out:
# Header always set Content-Security-Policy "default-src 'self'
# 'unsafe-inline' https://%(hostname)s"
Even if uncommented, the proposed value includes 'unsafe-inline' in
script-src and would not block this injection.
Suggested fix:
JS-string-escape state and postLogoutUrl values (the
cleanest approach is to JSON-encode the string, which produces a valid
JS string literal):
String postLogoutUrlJs = JsonUtil.toJson(postLogoutUrl);
html += "<script>" +
"window.onload=function() {" +
"window.location=" + postLogoutUrlJs + ";" +
"}" +
"</script>";
The same applies to the logoutUri values inserted into iframe src
attributes earlier in the method (HTML-attribute context; needs HTML
escape).
Also recommend uncommenting + tightening the CSP in the Apache template,
removing 'unsafe-inline' from script-src.
File:
jans-auth-server/server/src/main/java/io/jans/as/server/session/ ws/rs/EndSessionUtils.java:77-101createFronthannelHtmlconcatenates the attacker-controlled stateparameter into the body of a <script> block that calls window.location:
The state parameter is attacker-controlled by OAuth/OIDC spec design
(it round-trips through the user-agent). Because the injection point is
a JavaScript string literal (not an HTML attribute), HTML-context
escaping does not help — the attacker breaks out of the JS string with
a single quote and executes arbitrary JavaScript in the AS's origin.
Crafted URL (victim with active session follows it):
/jans-auth/restv1/end_session?id_token_hint=<valid token> &post_logout_redirect_uri=https://target.example/ &state='+alert(document.cookie)+'Injected JS runs in the AS's origin. Can exfiltrate cookies/session
tokens, read localStorage (where OAuth client tokens may be stashed),
and issue same-origin fetches with the user's credentials.
Default deployments have no browser-side mitigation. The default Apache
template at community-edition-setup/templates/apache/https_gluu.conf has
the Content-Security-Policy header commented out:
# Header always set Content-Security-Policy "default-src 'self'
# 'unsafe-inline' https://%(hostname)s"
Even if uncommented, the proposed value includes 'unsafe-inline' in
script-src and would not block this injection.
Suggested fix:
JS-string-escape state and postLogoutUrl values (the
cleanest approach is to JSON-encode the string, which produces a valid
JS string literal):
The same applies to the logoutUri values inserted into iframe src
attributes earlier in the method (HTML-attribute context; needs HTML
escape).
Also recommend uncommenting + tightening the CSP in the Apache template,
removing 'unsafe-inline' from script-src.