tomcat禁用不安全的HTTP方法
一般情况下在项目开发完成之后上线之前一般都要经过安全组扫描,上周在处理一个微服务迁移过程中安全组提出了两个安全方面的问题,即微服务的api使用了不安全的http方法(DELETE和TRACE),事实上该微服务的http方法只使用了GET和POST,所以根据安全组的建议需要禁用不安全的HTTP方法。
我准备通过一个简单的demo来演示如何进行配置,首先创建一个spring boot项目,根据需要选择需要的依赖,因为主要是做HTTP方法的配置,所以我只选择了web。
一、使用内置tomcat
使用spring boot进行开发的时候一般都会使用内置的tomcat
,这样的话对tomcat
的配置一般会通过application.properties
或者配置类来进行设置。下面通过一个简单的demo来看下如何禁用相关的HTTP方法。
首先创建一个spring boot的项目,定义一个controller
,代码如下:
@RestController
@RequestMapping("/rest")
public class HttpController {
@Value("${spring.application.name:application}")
private String applicationName;
@RequestMapping(value = "/other",method = {RequestMethod.HEAD,RequestMethod.OPTIONS,RequestMethod.TRACE,RequestMethod.PATCH})
public String head (HttpServletRequest request) {
log.info(">>>> request method={} <<<<",request.getMethod());
return request.getMethod() + " method from " + applicationName;
}
@PutMapping("/put")
public String put (HttpServletRequest request) {
return request.getMethod() + " method " + applicationName;
}
@PostMapping("/post")
public String post (HttpServletRequest request) {
return request.getMethod() + " method from " + applicationName;
}
@GetMapping("/get")
public String get (HttpServletRequest request) {
return request.getMethod() + " method from " + applicationName;
}
@DeleteMapping("/delete")
public String delete (HttpServletRequest request) {
return request.getMethod() + " method from " + applicationName;
}
}
然后通过对以上接口调用基本上的都会成功,这里还是推荐使用idea自带的rest-api.http
。这里唯一需要注意的是TRACE
方法,如果直接调用会显示下面的信息:
TRACE http://localhost:8080/rest/other
HTTP/1.1 405
Allow: HEAD, DELETE, POST, GET, OPTIONS, PUT
Content-Type: message/http
Content-Length: 221
Date: Sat, 14 Sep 2019 06:05:06 GMT
TRACE /error HTTP/1.1
accept: */*
cache-control: no-cache
content-type: application/json
host: localhost:8080
connection: Keep-Alive
user-agent: Apache-HttpClient/4.5.9 (Java/11.0.3)
accept-encoding: gzip,deflate
Response code: 405; Time: 14ms; Content length: 221 bytes
可见允许访问的的方法中是没有TRACE
方法的,因此我们需要首先开启TRACE
方法,对于spring boot内置的tomcat
,一般可以考虑通过application.properties
进行配置,但是如果没有提供该项配置的话就需要通过配置类来完成。下面通过一个配置类允许TRACE
方法访问,这个配置类中通过代码创建TomcatServletWebServerFactory
的bean,代码如下:
@Slf4j
@Configuration
public class HttpMethodConfig {
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
log.info(">>>> custom tomcat server factory start <<<<");
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
// 开启TRACE方法
factory.addConnectorCustomizers(connector -> {
connector.setAllowTrace(true);
});
return factory;
}
}
然后再次使用TRACE
方法访问,结果如下:
TRACE http://localhost:8080/rest/other
HTTP/1.1 200
Content-Type: message/http
Content-Length: 226
Date: Sat, 14 Sep 2019 06:17:37 GMT
TRACE /rest/other HTTP/1.1
accept: */*
cache-control: no-cache
content-type: application/json
host: localhost:8080
connection: Keep-Alive
user-agent: Apache-HttpClient/4.5.9 (Java/11.0.3)
accept-encoding: gzip,deflate
Response code: 200; Time: 71ms; Content length: 226 bytes
状态码为200说明请求成功了,而且根据日志输出也可以判定TRACE
方法可以访问了。这时候使用GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
中的任何一个方法我们的接口都是可以正常访问的。
等等....不是说是要禁用不安全的HTTP方法吗???
二、应用禁用HTTP方法
之所以先配置开启TRACE
方法也是因为当时在配置过程中遇到了一个问题(后面会说到),下面通过配置禁用除GET、POST
之外的其他HTTP方法。如果对tomcat
比较熟悉的话应该知道可以通过修改tomcat
的web.xml
配置文件达到禁用相关HTTP方法,但是这里我们是内置的tomcat
所以只能通过配置类来修改,但是原理都是一样的,那就是配置security-constraint
来实现。
接下来修改下自定义配置类,代码如下:
@Slf4j
@Configuration
public class HttpMethodConfig {
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
log.info(">>>> custom tomcat server factory start <<<<");
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
// 开启TRACE
factory.addConnectorCustomizers(connector -> {
connector.setAllowTrace(true);
});
factory.addContextCustomizers(context -> {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection securityCollection = new SecurityCollection();
securityCollection.addPattern("/*");
securityCollection.addMethod("PUT");
securityCollection.addMethod("HEAD");
securityCollection.addMethod("DELETE");
securityCollection.addMethod("OPTIONS");
securityCollection.addMethod("TRACE");
securityCollection.addMethod("PATCH");
securityConstraint.addCollection(securityCollection);
context.addConstraint(securityConstraint);
});
return factory;
}
}
上面的代码中securityConstraint.setUserConstraint("CONFIDENTIAL")
,userConstraint
的值只能是CONFIDENTIAL、NONE、INTEGRAL
其中之一,至于原因我也不知道....感兴趣的话可以google一下。其他的和使用web.xml
基本一样,新建安全集添加需要禁止的请求方法和访问的路径,然后将其添加到新建的安全约束即可。完成之后再调用接口进行测试会发现返回的结果类似下面:
TRACE http://localhost:8080/rest/other
HTTP/1.1 302
Cache-Control: private
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Location: https://localhost/rest/other
Content-Length: 0
Date: Sat, 14 Sep 2019 06:54:39 GMT
<Response body is empty>
Response code: 302; Time: 16ms; Content length: 0 bytes
当然因为我并没有安全扫描的工具,所以只能根据调用接口来判断,这种方法其实并不严谨。下面解释一下为什么需要先启用TRACE
方法,难道不是直接通过在安全约束里面添加TRACE
就可以了吗?事实并不是这样的。在工作中我发现如果不开启TRACE
,而是直接在安全约束禁用TRACE
方法并不会生效,当时我也很奇怪,因为我当时已经禁用了TRACE
方法,但是之后安全组的的安全扫描结果依然显示没用禁用TRACE
方法。后来也是网上查了一些资料才知道必须先允许TRACE
访问才能禁用掉该方法。
三、使用web.xml
如果使用的是传统的方式开发,即使用单独的tomcat
容器,则可以在应用的web.xml
或者tomcat
自己的web.xml
中添加以下配置:
<!-- 禁用不安全的http方法 -->
<security-constraint>
<web-resource-collection>
<web-resource-name>DENY METHOD</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>DELETE</http-method>
<http-method>PATCH</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint></auth-constraint>
</security-constraint>
另外需要修改下server.xml
,即允许TRACE
访问:
<Connector port="8080" protocol="HTTP/1.1" allowTrace="true" connectionTimeout="20000"
redirectPort="8443" />