开发 WEB 应用的同学应该不少,不知道大家平时开发中,有没有关注过应用开发的「安全问题」。
所谓安全问题,就是开发中不要留下漏洞,给入侵者破坏者机会。
比如,我们常挂在嘴边的安全问题有 「SQL 注入」,为了防止出现问题,一般都会使用预编译的 SQL,而不是拼接SQL,以此来保证安全。再比如,我们一个允许用户输入的文本框中,会禁用代码的渲染,比如像
<script> alert("hello");</script> 这种会被做为纯文本对待。
类似需要注意的问题有很多,如果我们不留意,就会容易造成安全问题。
不过有些「洞」,是你我开发中不曾留意的。或者说你只有先意识到有这一类问题需要注意,开发的时候才会把它堵上。
比如最近开发的一个WEB 应用,公司做安全的同事帮忙看了看,就给报了几个问题。
这里简单总结和各位一起分享下,欢迎留言讨论。
同事给指出的问题主要有两个:
不安全的 TRACE、OPTIONS 等方法未禁用
用户提交的信息里,可以包含第三方链接,容易出现钓鱼问题。
问题一:
通过TRACE、OPTIONS 方法,可以暴露不少关键信息。
我们知道,HTTP 请求里可以使用不同的 Method 来实现不同的请求功能, 比如常见的 GET/POST/PUT/DELETE 。除此之外还有一些,比如 TRACE/OPTIONS 等,每种请求简单描述如下。详细描述,可以查看RFC2616(https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html)
GET 请求指定的页面信息,并返回实体主体。
HEAD 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
PUT 从客户端向服务器传送的数据取代指定的文档的内容。
DELETE 请求服务器删除指定的页面。
CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
OPTIONS 允许客户端查看服务器的可用内容。
TRACE 回显服务器收到的请求,主要用于测试或诊断
从上面内容可以看出,TRACE 方法,OPTIONS方法,一般都会返回大量和实际业务处理无关的内容,如果开放会暴露许多服务器的关键信息,可能引出安全问题。
处理方式:
对于传统的web项目, 直接在web.xml 里增加安全限制「security-constraint」即可。
对于 Spring Boot应用,如果是使用 Tomcat做为容器,可以通过声明独立的配置,实现类似web.xml的效果:
web.xml的配置
<security-constraint>
<web-resource-collection>
<web-resource-name>restricted methods</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
Spring Boot 应用中的配置
@Configuration
public class TomcatCustomizer implements EmbeddedServletContainerCustomizer {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
TomcatEmbeddedServletContainerFactory tomcat = (TomcatEmbeddedServletContainerFactory) container;
tomcat.setSessionTimeout(8, TimeUnit.HOURS);
tomcat.addContextCustomizers(new ContextSecurityCustomizer());
}
private static class ContextSecurityCustomizer implements TomcatContextCustomizer {
@Override
public void customize(Context context) {
SecurityConstraint constraint = new SecurityConstraint();
SecurityCollection securityCollection = new SecurityCollection();
securityCollection.setName("restricted_methods");
securityCollection.addPattern("/*");
securityCollection.addOmittedMethod(HttpMethod.POST.toString());
securityCollection.addOmittedMethod(HttpMethod.GET.toString());
constraint.addCollection(securityCollection);
constraint.setAuthConstraint(true);
context.addConstraint(constraint);
}
}
}
如果使用 Jetty,配置方式也类似。
当然,如果想要做到不与容器依赖紧耦合,还可以自己定义Filter,然后在请求中判断请求头里的 Method 如果是不允许的方法,可以直接返回错误。
问题二:
具体是这样,我们的应用里允许用户上传图片。上传的图片会保存到对象存储中,然后返回一个URL,最后用户完整的信息里,是直接存储了一个URL。但是,我们看到,整个上传到提交的过程,并不是一步完成的,所以,用户可以自行构造这个提交的请求,并将图片的URL,替换成任意链接。
这个时候,在页面再次渲染时,是会请求图片URL来展示图片的。如果是一个恶意的第三方钓鱼链接,容易造成盗取用户信息等安全问题。
处理方式:
这个问题,一般在后端对提交的内容进行检验,如果有「不符合规定」的域名,就报错。一般是维护一个白名单,判断URL信息在不在白名单中。
判断一个URL的后缀,你一般用什么方式呢?正则表达式?字符串截取?
这里有个方式供参考,可以直接取出我们预期的域名后缀:
String host = "";
URL fullUrl = null;
try {
fullUrl = new URL("http://blog.tomcat8080.com/abc=def");
} catch (MalformedURLException e) {
e.printStackTrace();
}
host = fullUrl.getHost();
InternetDomainName domainName = InternetDomainName.from(host);
String top = domainName.topPrivateDomain().toString();
这些安全技术,仿佛是和WEB开发平行的一个领域,属于另一个工种。这些不属于架构,与设计模式无关,也不在数据库设计、虚拟机优化的范围内,但是,也是我们不得不去认真了解和重视的。可能不需要安全工程师那么深入,但一些常见的安全问题要了解,类似于一个 「CheatSheet」避免自己开发的时候暴露,保证我们开发的应用安全,不被攻击和泄露数据。对于WEB安全,我还是 too young了,欢迎各位留言述说自己的经验。
相关阅读
web.xml是怎么被解析的?
关注『 Tomcat那些事儿 』 ,发现更多精彩文章!了解各种常见问题背后的原理与答案。深入源码,分析细节,内容原创,欢迎关注。
转发是最大的支持,谢谢
更多精彩内容:
一台机器上安装多个Tomcat 的原理(回复001)
监控Tomcat中的各种数据 (回复002)
启动Tomcat的安全机制(回复003)
乱码问题的原理及解决方式(回复007)
Tomcat 日志工作原理及配置(回复011)
web.xml 解析实现(回复 012)
线程池的原理( 回复 014)
Tomcat 的集群搭建原理与实现 (回复 015)
类加载器的原理 (回复 016)
类找不到等问题 (回复 017)
代码的热替换实现(回复 018)
Tomcat 进程自动退出问题 (回复 019)
为什么总是返回404? (回复 020)
...
PS: 对于一些 Tomcat常见问题,在公众号的【常见问题】菜单中,有需要的朋友欢迎关注查看