WebLogic Server 10.3.6升级至p30463097(JWEB|10.3.6.0.200114)后管理控制台登陆失败问题分析及临时处理办法。
案例背景
01
PART
Oracle 2020年1月发布了新一季的CPU,WebLogic Server 11g最新补丁版本代号JWEB,WebLogic Server版本10.3.6.0.200114。
在管理服务器侦听端口设定为80(http)/443(https)时,会出现以下问题:
1、服务器启动正常;
2、管理控制台登陆失败;
3、测试发现当管理服务器侦听端口设置为其它端口时,控制台可以登陆。
经过分析,最终确认为WebLogic Server 10.3.6.0.200114 Web Security Bug,影响的范围为使用基于JAAS Form (login-config\auth-method = FORM)登陆的应用。
临时解决的办法为通过weblogic.xml设定weblogic-web-app\container-descriptor\referer-validation为 NONE。
Oracle已经发布临时补丁Patch 18509293: SU Patch [EIL8]: 10.3.6.0.200114WLSPSU Overlay: CANNOT LOGIN TO CONSOLE, BUT CAN LOGIN TO EM WITH THE SAME USER来修正这个问题
详细的MOS DocID可参考:2637876.1
分析过程
02
PART
开启管理服务器调试:debug-http及debug-web-app-security为true,登陆失败时,AdminServer log有如下报错:
####<2020-3-7 下午04时48分44秒 CST>
####<2020-3-7 下午04时48分44秒 CST>
####<2020-3-7 下午04时48分44秒 CST>
可以确定在请求经过weblogic. servlet. security. internal.FormSecurityModule后被重定向至/console/login/LoginError.jsp;代码搜索weblogic. servlet. security. internal. FormSecurityModule,我们发现该类主要存在于以下两个路径:
J:\OLMJavaEEToolkit\classscanner>run.cmd
d:\OFM weblogic.servlet.security.internal.FormSecurityModule
J:\OLMJavaEEToolkit\classscanner>echo off
Search target directory:d:\OFM
Target class full name:weblogic.servlet.security.internal.FormSecurityModule
file:/d:/OFM/patch_wls1036/patch_jars/BUG30463097_10360200114.jar
file:/d:/OFM/wlserver_10.3/server/lib/weblogic.jar
其中BUG30463097_10360200114.jar是实施p30463097后的补丁包,反编译weblogic.servlet.security.internal.FormSecurityModule代码,我们可以看到该处有j_security_check的处理过程
WebLogic Server 10.3.6.0.200114 weblogic.servlet.security.internal.FormSecurityModule
/* / boolean checkUserPerm(HttpServletRequest req, HttpServletResponse rsp, SessionInternal session, ResourceConstraint cons, AuthenticatedSubject waFormAuthUser, boolean applyAuthFilters)
/ / throws IOException, ServletException
/ / {
/ 208 / if (req.getRequestURI().endsWith(“j_security_check”))
/ / {
/ 215 / return processJSecurityCheck(req, rsp, session);
/ 216 / }if (waFormAuthUser != null)
/ / {
/ 219 / return processLoggedInUser(req, rsp, waFormAuthUser);
/ / }
/ /
/ 223 / if ((this.webAppSecurity.isFullSecurityDelegationRequired()) && (this.webAppSecurity.hasPermission(req, rsp, null, cons)))
/ 224 / return true;
/ 225 / if ((applyAuthFilters) && (this.webAppSecurity.hasAuthFilters())) {
/ 226 / invokeAuthFilterChain(req, rsp);
/ 227 / return false;
/ / }
/ 229 / if (isForbidden(cons)) sendForbiddenResponse(req, rsp); else
/ 230 / sendLoginPage(req, rsp);
/ 231 / return false;
/ */ }
/* / private boolean processJSecurityCheck(HttpServletRequest req, HttpServletResponse rsp, SessionInternal session)
/ / throws IOException
/ / {
/ 239 / if (!checkRefererHeader(req, rsp)) {
/ 240 / sendError(req, rsp);
/ 241 / return false;
/ */ }
…
}
/* / private boolean checkRefererHeader(HttpServletRequest req, HttpServletResponse rsp) {
/ 354 / if (this.refererValidationType == RefererValidationType.NONE) return true;
/ /
/ 356 / String referer = req.getHeader(“Referer”);
/ 357 / if ((referer == null) && (this.refererValidationType == RefererValidationType.LENIENT)) {
/ 358 / return true;
/ / }
/ 360 / if (referer == null) {
/ 361 / return false;
/ / }
/ /
/ 364 / String[] refererAddr = extractRefererAddr(referer);//问题调用,referer=http://localhost/console/login/LoginForm.jsp,and only return refererAddr[0] = “localhost” ,and return false lead to checkRefererHeader failue.
/ 365 / if ((refererAddr == null) || (refererAddr.length != 2)) return false;//return false
/ /
/ 367 / String serverHost = req.getServerName();
/ 368 / int serverPort = req.getServerPort();
/ 369 / int refererPort = 0;
/ / try {
/ 371 / refererPort = Integer.parseInt(refererAddr[1]);
/ / } catch (NumberFormatException e) {
/ 373 / return false;
/ / }
/ /
/ 376 / if (serverPort != refererPort) return false;
/ 377 / if (serverHost.equals(refererAddr[0])) return true;
/ / try
/ / {
/ 380 / return InetAddress.getByName(serverHost).equals(refererAddr[0]); } catch (UnknownHostException e) {
/ / }
/ 382 / return false;
/ */ }
//问题代码:referer=http://localhost/console/login/LoginForm.jsp时,只会返回长度为1的字符串数组
/* / private String[] extractRefererAddr(String referer)
/ / {
/ 387 / int hostBegin = referer.indexOf("//") + 2;
/ 388 / if (hostBegin <= 1) return null;
/ 389 / int addrEnd = referer.indexOf("/", hostBegin);
/ 390 / if (addrEnd < 0) return null;
/ 391 / String refererAddr = referer.substring(hostBegin, addrEnd);
/ 392 / return refererAddr.split("😊;
/ */ }
通过jdb调试还原问题过程,以下为调试信息:
[ACTIVE] ExecuteThread: ‘0’ for queue: ‘weblogic.kernel.Default (self-tuning)’[1] where
[1]weblogic.servlet.security.internal.FormSecurityModule.processJSecurityCheck (FormSecurityModule.java:240)
[2]weblogic.servlet.security.internal.FormSecurityModule.checkUserPerm (FormSecurityModule.java:215)
[3]weblogic.servlet.security.internal.FormSecurityModule.checkAccess (FormSecurityModule.java:98)
[4]weblogic.servlet.security.internal.ChainedSecurityModule.checkAccess (ChainedSecurityModule.java:79)
[5]weblogic.servlet.security.internal.ServletSecurityManager.checkAccess (ServletSecurityManager.java:82)
[6]weblogic.servlet.internal.WebAppServletContext.securedExecute (WebAppServletContext.java:2,219)
[7]weblogic.servlet.internal.WebAppServletContext.execute (WebAppServletContext.java:2,182)
[8] weblogic.servlet.internal.ServletRequestImpl.run (ServletRequestImpl.java:1,499)
[9] weblogic.work.ExecuteThread.execute (ExecuteThread.java:263)
[10] weblogic.work.ExecuteThread.run (ExecuteThread.java:221)
[ACTIVE] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’[1] dump this.refererValidationType
this.refererValidationType = {
NONE: instance of weblogic.servlet.security.internal.RefererValidationType(id=11841)
LENIENT: instance of weblogic.servlet.security.internal.RefererValidationType(id=11842)
STRICT: instance of weblogic.servlet.security.internal.RefererValidationType(id=11822)
$VALUES: instance of weblogic.servlet.security.internal.RefererValidationType[3] (id=11843)
java.lang.Enum.name: “STRICT”
java.lang.Enum.ordinal: 2}
评析:
进入weblogic. servlet. security. internal. FormSecurityModule. processJSecurityCheck方法处理时,可以看到当前对象(weblogic.servlet.security. internal. FormSecurityModule)成员变量refererValidationType为STRICT;
已完成的步骤:
“线程=[ACTIVE] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’”, weblogic.servlet. security. internal. FormSecurityModule. checkRefererHeader(), 行=357 bci=21
[ACTIVE] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’[1] where
[1]weblogic.servlet.security.internal.FormSecurityModule.checkRefererHeader (FormSecurityModule.java:357)
[2]weblogic.servlet.security.internal.FormSecurityModule.processJSecurityCheck (FormSecurityModule.java:239)
[3]weblogic.servlet.security.internal.FormSecurityModule.checkUserPerm (FormSecurityModule.java:215)
[4]weblogic.servlet.security.internal.FormSecurityModule.checkAccess (FormSecurityModule.java:98)
[5]weblogic.servlet.security.internal.ChainedSecurityModule.checkAccess (ChainedSecurityModule.java:79)
[6]weblogic.servlet.security.internal.ServletSecurityManager.checkAccess (ServletSecurityManager.java:82)
[7]weblogic.servlet.internal.WebAppServletContext.securedExecute (WebAppServletContext.java:2,219)
[8]weblogic.servlet.internal.WebAppServletContext.execute (WebAppServletContext.java:2,182)
[9] weblogic.servlet.internal.ServletRequestImpl.run (ServletRequestImpl.java:1,499)
[10] weblogic.work.ExecuteThread.execute (ExecuteThread.java:263)
[11] weblogic.work.ExecuteThread.run (ExecuteThread.java:221)
[ACTIVE] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’[1] locals
方法参数:
req = instance of weblogic.servlet.internal.ServletRequestImpl(id=11846)
rsp = instance of weblogic.servlet.internal.ServletResponseImpl(id=11847)
本地变量:
referer = “http://localhost/console/login/LoginForm.jsp”
[ACTIVE] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’[1]
评析:
进入weblogic.servlet.security. internal.FormSecurityModule. checkRefererHeader方法处理时,可以看到从报头上获取得到的Referer值为" http://localhost/console/login/LoginForm.jsp "并赋给局部变量referer;
[ACTIVE] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’[1] where
[1]weblogic.servlet.security.internal.FormSecurityModule.checkRefererHeader (FormSecurityModule.java:365)
[2]weblogic.servlet.security.internal.FormSecurityModule.processJSecurityCheck (FormSecurityModule.java:239)
[3]weblogic.servlet.security.internal.FormSecurityModule.checkUserPerm (FormSecurityModule.java:215)
[4]weblogic.servlet.security.internal.FormSecurityModule.checkAccess (FormSecurityModule.java:98)
[5]weblogic.servlet.security.internal.ChainedSecurityModule.checkAccess (ChainedSecurityModule.java:79)
[6]weblogic.servlet.security.internal.ServletSecurityManager.checkAccess (ServletSecurityManager.java:82)
[7]weblogic.servlet.internal.WebAppServletContext.securedExecute (WebAppServletContext.java:2,219)
[8]weblogic.servlet.internal.WebAppServletContext.execute (WebAppServletContext.java:2,182)
[9] weblogic.servlet.internal.ServletRequestImpl.run (ServletRequestImpl.java:1,499)
[10] weblogic.work.ExecuteThread.execute (ExecuteThread.java:263)
[11] weblogic.work.ExecuteThread.run (ExecuteThread.java:221)
[ACTIVE] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’[1] locals
方法参数:
req = instance of weblogic.servlet.internal.ServletRequestImpl(id=11846)
rsp = instance of weblogic.servlet.internal.ServletResponseImpl(id=11847)
本地变量:
referer = “http://localhost/console/login/LoginForm.jsp”
refererAddr = instance of java.lang.String[1] (id=11849)
[ACTIVE] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’[1]
[ACTIVE] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’[1]
[ACTIVE] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’[1] print refererAddr[0]
refererAddr[0] = “localhost”
[ACTIVE] ExecuteThread: ‘5’ for queue: ‘weblogic.kernel.Default (self-tuning)’[1]
评析:
执行至代码weblogic.servlet.security.internal.FormSecurityModule第365行(weblogic.servlet.security.internal.FormSecurityModule.checkRefererHeader),
故障结论
03
PART
可以看到,当referer = "http://localhost/console/login/LoginForm.jsp"时,从extractRefererAddr返回的字符串数组长度仅为1;代码weblogic.servlet.security.internal.FormSecurityModule第365执行完成的结果为return false;导致weblogic.servlet.security.internal.FormSecurityModule.processJSecurityCheck执行第240行:sendError(req,rsp);
问题代码位于weblogic.servlet.security.internal.FormSecurityModule.extractRefererAddr。
可以确认此问题为10.3.6.0.200114的代码Bug。
根据代码weblogic.servlet.security.internal.FormSecurityModule.checkRefererHeader,如果weblogic.servlet.security.internal.FormSecurityModule.refererValidationType为NONE时,将会跳过Referer的检查;
参考weblogic.servlet.internal.WebAppConfigManager,refererValidationType默认值为LENIENT,可通过weblogic-web-app\container-descriptor\referer-validation进行设置。因此对于此问题的临时替代办法为:
设置$WL_HOME/server/lib/consoleapp/webapp/WEB-INF/weblogic.xml#weblogic-web-app\container-descriptor\referer-validation = NONE
根据Oracle的相关信息,Oracle发布了临时补丁Patch 18509293: SU Patch [EIL8]: 10.3.6.0.200114WLSPSU Overlay: CANNOT LOGIN TO CONSOLE, BUT CAN LOGIN TO EM WITH THE SAME USER来修正这个问题。可以在MOS上下载18509293打上此补丁修正此问题。




