做为一个用户量具大的Servlet容器,Tomcat一直以其源码开放,方便易用,社区活跃,文档丰富等特点,受到众多Java Web开发者的喜欢。
在这篇文章中,我们来了解下Tomcat的一些安全机制,为了保证应用服务器的安全,Tomcat都提供些啥。
为了有一个更直观的印象,我们先来做这样一个尝试,在你Tomcat上部署一个包含jsp页面的web应用。这个jsp页面中至少包含下面一段内容:
<%
System.exit(0);
%>
看到上面的语句,我们大概心想:好家伙,这是要退出系统呀。在你向这个jsp页面发起请求时,果然不出所料,你的Tomcat被停掉了。
这能说明什么呢,啥也说明不了。毕竟这个exit是自己写在页面中的。那如果是下面这样的场景呢?
1.
服务器上的一个Tomcat,同时跑了不止一个web应用。其中某一个应用中包含上面的jsp页面,这样,当这个应用中触发了这个页面请求的时候,导致整个
Tomcat被停了,而其它的Web应用很无辜的受到了影响。
2.
依然是整个Tomcat上部署了多个Web应用,其中某个应用的代码包含这样的逻辑:
定期对父目录下除自己外的目录执行删除操作。
这个时候,另外的Web应用哥几个又受到了猪一样队友的影响不能正常工作了。
3. 部署的多个应用可以会依赖环境的不同进行不同的操作。而这些对系统的判断,我们一定是通过获取JVM提供的一些属性,比如获取当前操作系统的属性是os.name,版本的则是os.version。如果某个应用在运行过程中把这此属性给改了,那其它应用又受到影响了。
Tomcat内部提供的安全机制,则对上述的场景做了各种限制来保证其不会发生。
既然不会发生,那你还扯这些干嘛呢?你心里一定开始这么想了。
对,Tomcat是提供了这些,但默认并没有开启。不信你试试前面说的在jsp页面中执行System.exit!!!
所以,泡杯茶,继续读一读。
Tomcat实现上述种种形式的限制,都是通过JVM 提供的SecurityManager机制。这种机制配置后,陌生的代码只能执行有限的操作,对于可以执行从remote Code的应用,这种机制更是必不可少。
在JVM中,特定的classLoader,SecurityManager及其它一些要素,组成的Java的sandBox,即传说中的沙箱。你也许在使用杀毒软件时听过这个词,让一个刚下载的不知道干啥的软件在沙箱里运行,确保没问题再使用。
为了使用Tomcat的这种安全机制,我们需要在启动的时候先开启之。
方式:
在启动脚本之后,传递-security参数,以windows系统为例,在启动Tomcat的时候,可以这样操作
startup.bat -security
catalina.bat -security
上面两种方式任选一个即可。其实质上是给JVM传入了这样一个参数:
-Djava.security.manager。
此时,你再执行上面包含System.exit代码的jsp请求,会看到这个样子的结果
会明显的提示access denied。
这个东西是怎么生效的呢?
打开%Catalina.home%/conf/catalina.policy文件查看下。(Catalina.home代表Tomcat安装目录)
这里面定义了一系列的包permission,Tomcat启动的时候policy文件默认使用这个文件。你可以通过参数-Djava.security.policy=xxx指定。
Tomcat内部又是如何配置和使用的呢?
看这里,Global类里有这样一个常量的定义:
/**
* Has security been turned on?
*/
public static final boolean IS_SECURITY_ENABLED =
(System.getSecurityManager() != null);
如果指定了上面的java.security.manager参数,则JVM的security Manager开启。此时后续的一系列地方都会根据此标识来判断是否要进行安全限制。
例如我们上面提到的场景,为了限制应用内对自身资源的管理,删除等,会设置些目录的访问权限。看下面的代码:
/**
* Configure associated class loader permissions.
*/
private void setPermissions() {
if (!Globals.IS_SECURITY_ENABLED) // 没有启用则直接返回
return;
if (context == null)
return;
// Tell the class loader the root of the context
ServletContext servletContext = context.getServletContext();
// Assigning permissions for the work directory
File workDir =
(File) servletContext.getAttribute(ServletContext.TEMPDIR);
if (workDir != null) {
try {
String workDirPath = workDir.getCanonicalPath();
classLoader.addPermission
(new FilePermission(workDirPath, "read,write")); //为应用工作目录设置读写权限
classLoader.addPermission
(new FilePermission(workDirPath + File.separator + "-",
"read,write,delete"));
} catch (IOException e) {
// Ignore
}
}
for (URL url : context.getResources().getBaseUrls()) {
classLoader.addPermission(url);
}
}
而在大量的Tomcat源码中,你会发现对于securityManager的判断,例如
if (Globals.IS_SECURITY_ENABLED &&
name.equals(Globals.SENDFILE_FILENAME_ATTR)) {
...
System.getSecurityManager().checkRead(canonicalPath);
}
开启securityManager走权限检测的流程,默认走普通流程。以我们熟悉的Filter处理为例,在处理链中,默认直接执行Filter中的doFilter方法,而开启安全机制则会复杂一些,看这些代码:
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege
("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
正是由于源码内的这一系列安全限制,才保证Tomcat的安全稳定。马上加上
-security参数试一下吧,骚年。
感觉有用就转发到朋友圈,让更多朋友了解吧。
欢迎关注本公众号,了解更多内容。