rasp 的侵入式特性和拦截特性导致开发和运维普通不太愿意配合,当生产环境出现问题时往往第一时间先把责任推给 rasp,逐渐的安全部门普遍只能把 rasp 设置为告警模式,而且越是大的集群拦截开的就越少,所以字节的 elkeid 和某外卖大厂内部的 rasp 都是告警模式,没有发挥 rasp 的实际作用。相反的深圳某体制内企业他们的信息系统大部分都是采购的,并采取自研的策略,但是这个企业对外每开放一个端口,都强制要求安装网防 G01 进行管控。
我思考这个问题得出的答案是:
本质上 rasp 是安全部门推动的,对业务性来说代码可控力度较弱,强侵入性和强拦截性导致只有话语权较强的企业才能完整落地。尤其排查问题的成本实在过高,所以导致开发、运维、安全三方技术力量在面对生产环境问题时很容易扯皮,最终 rasp 面临的不是减少拦截性就是减少侵入性。
当然,后面我们再进一步解析安全中间件会发现:本质上这是一个管理问题,还真不是技术问题。
安全中间件的优势是:
运维和开发由于合规因素都是相对隔离的,企业人数越多,运维和开发的隔离性就越明显。在运维人员采购以及管控中间件的这部分工作中,安全中间件的优势就出现了:运维部门采购安全中间件后,往往会开启所有的安全策略,但是安全策略的关闭、调整的权限是留给开发部门的。从管理角度上运维人员已经落实了安全责任,如果开发在使用中间件时为了业务逻辑关闭、调整中间件的安全策略,属于是开发部门的安全问题与运维无关。
这也间接解释了国内很多企业的安全部门尴尬的原因:安全工作要落地,但是各部门又没有相关的能力,只能安全部门自身输出安全能力提供安全产品。而安全预算往往又不足,只能安全部门从业务端开始从业务捋到运维,链路太长又气又累,出了问题还要背锅。但安全部门本质上是要求从业务端就开始层层履行安全责任的监管部门,而目前的现状是运动员又是裁判员,这让人确实很难受。
将以下代码放到 pom.xml 里面然后放到源码的根目录中
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat</artifactId>
<name>tomcat</name>
<version>8.5.75</version>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.5</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jaxrpc_1.1_spec</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
<version>3.26.0</version>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>4.0.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>biz.aQute.bnd</groupId>
<artifactId>biz.aQute.bnd</artifactId>
<version>5.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
3)默认启动是有问题的,我们来做如下修改
3.1)删掉 examples 目录
3.2)找到 java/org/apache/catalina/startup/ContextConfig.java 的 configureStart 方法
在 webConfig (); 这句话下增加下面这句话
context.addServletContainerInitializer(new JasperInitializer(), null);
3.3)修改启动时的 vm 参数,防止乱码
-Dfile.encoding=UTF-8
-Duser.timezone=Asia/Shanghai
-Duser.language=en
由
if (bundle != null) {
str = bundle.getString(key);
}
try {
// Avoid NPE if bundle is null and treat it like an MRE
if (bundle != null) {
//str = bundle.getString(key);
str = new String(bundle.getString(key).getBytes("ISO-8859-1"), "UTF-8");
}
} catch (MissingResourceException | UnsupportedEncodingException mre) {
package org.apache.coyote.sec;
import org.apache.catalina.filters.RequestFilter;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
/* web.xml注解
<filter>
<filter-name>SecCheckFilter</filter-name>
<filter-class>
org.apache.coyote.sec.SecCheckFilter
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>SecCheckFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
*/
public final class SecCheckFilter extends RequestFilter {
private final Log log = LogFactory.getLog(SecCheckFilter.class);
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
HttpServletRequest httpRequest = (HttpServletRequest) request;
//防止getParameter与getInputStream冲突
httpRequest.getParameterMap();
httpRequest = new BufferedServletRequestWrapper(httpRequest);
response.setCharacterEncoding("UTF-8");
String httpMethod = httpRequest.getMethod().toLowerCase(Locale.ROOT);
// 禁用 get post options 之外的其他http请求,防止 put move 等上传攻击
try {
switch (httpMethod) {
case "get":
case "post":
case "options":
break;
default:
((HttpServletResponse) response).sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return;
}
boolean isMultipart = ServletFileUpload.isMultipartContent(httpRequest);
if (isMultipart) {
// 校验Multipart上传时的文件后缀
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> fileItems;
fileItems = upload.parseRequest(new ServletRequestContext(httpRequest));
if (fileItems != null && fileItems.size() > 0) {
//遍历Multipart入参
for (FileItem item : fileItems) {
if (!item.isFormField()) {
String FileName = item.getName().toLowerCase(Locale.ROOT);
// 校验文件名中的特殊字符
if (FileName.contains("/") || FileName.contains("\") || FileName.contains(":") || FileName.contains("*")
|| FileName.contains("?") || FileName.contains(""") || FileName.contains("<") || FileName.contains(">")
|| FileName.contains("|") // windows文件名禁用 / : * ? " < > |
) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
// 校验文件后缀
String Extension = FileName.substring(FileName.lastIndexOf(".") + 1).trim();
if (Extension.startsWith("js") // jsp jspx js
|| Extension.startsWith("asp") // asp aspx
|| Extension.startsWith("jar") // jar
|| Extension.startsWith("war") // war
|| Extension.startsWith("php") // php
|| Extension.startsWith("htm") // htm html
|| Extension.startsWith("shtm") // shtml
|| Extension.startsWith("exe") // exe
|| Extension.startsWith("bat") // bat
) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
}
}
}
}
// 全部使用wrapper进行处理
chain.doFilter(httpRequest, response);
} catch (Exception e) {
}
}
protected Log getLogger() {
return log;
}
}
package org.apache.coyote.sec;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
public class BufferedServletInputStream extends ServletInputStream {
private final ByteArrayInputStream inputStream;
public BufferedServletInputStream(byte[] buffer) {
this.inputStream = new ByteArrayInputStream(buffer);
}
public int available() throws IOException {
return inputStream.available();
}
public int read() throws IOException {
return inputStream.read();
}
public int read(byte[] b, int off, int len) throws IOException {
return inputStream.read(b, off, len);
}
public boolean isFinished() {
return false;
}
public boolean isReady() {
return false;
}
public void setReadListener(ReadListener listener) {
}
}
package org.apache.coyote.sec;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class BufferedServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] buffer;
public BufferedServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
InputStream is = request.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int read;
while ((read = is.read(buff)) > 0) {
baos.write(buff, 0, read);
}
this.buffer = baos.toByteArray();
}
public ServletInputStream getInputStream() throws IOException {
return new BufferedServletInputStream(this.buffer);
}
}
同时在 conf 目录下的 web.xml 中引入 filter
<filter>
<filter-name>SecCheckFilter</filter-name>
<filter-class>
org.apache.coyote.sec.SecCheckFilter
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>SecCheckFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
6)试验一下,成功
ANT_HOME=C:Javaapache-ant-1.9.15
<filter>
<filter-name>SecCheckFilter</filter-name>
<filter-class>
org.apache.coyote.sec.SecCheckFilter
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>SecCheckFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
ant package-zip
可以看到依然是提示 400 错误,并且 tomcat 给出的信息与开发模式不一样了。
request.getMethod (); get 和 post 都可用
request.getContentType (); get 和 post 都可用,示例值:application/json ,multipart/form-data, application/xml 等
request.getParameterNames (); get 和 post 都可用,注:不适用 contentType 为 multipart/form-data
request.getParameter ("test"); get 和 post 都可用,注:不适用 contentType 为 multipart/form-data
request.getParameterMap (); get 和 post 都可用,注:不适用 contentType 为 multipart/form-data
package org.apache.coyote.sec;
import org.apache.catalina.filters.RequestFilter;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
/* web.xml注解
<filter>
<filter-name>SecCheckFilter</filter-name>
<filter-class>
org.apache.coyote.sec.SecCheckFilter
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>SecCheckFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
*/
public final class SecCheckFilter extends RequestFilter {
private final Log log = LogFactory.getLog(SecCheckFilter.class);
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
httpRequest = new BufferedServletRequestWrapper(httpRequest);
response.setCharacterEncoding("UTF-8");
String httpMethod = ((HttpServletRequest) request).getMethod().toLowerCase(Locale.ROOT);
// 禁用 get post options 之外的其他http请求,防止 put move 等上传攻击
try {
switch (httpMethod) {
case "get":
case "post":
case "options":
break;
default:
((HttpServletResponse) response).sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return;
}
// 使用语义waf策略过滤所有非Multipart提交的输入参数
Enumeration<String> enums = httpRequest.getParameterNames();
while (enums.hasMoreElements()) {
String pn = enums.nextElement();
String[] vales = httpRequest.getParameterValues(pn);
// i=1&i=2&i=3 这种情况下,所有的值也都必须过滤一次
for (String vale : vales) {
if (vale.length() > 5 && SqlParse.isSQLi(vale)) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
}
}
boolean isMultipart = ServletFileUpload.isMultipartContent(httpRequest);
if (isMultipart) {
// 校验Multipart上传时的文件后缀
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> fileItems;
fileItems = upload.parseRequest(new ServletRequestContext(httpRequest));
if (fileItems != null && fileItems.size() > 0) {
//遍历Multipart入参
for (FileItem item : fileItems) {
if (!item.isFormField()) {
String FileName = item.getName().toLowerCase(Locale.ROOT);
// 校验文件名中的特殊字符
if (FileName.contains("/") || FileName.contains("\") || FileName.contains(":") || FileName.contains("*")
|| FileName.contains("?") || FileName.contains(""") || FileName.contains("<") || FileName.contains(">")
|| FileName.contains("|") // windows文件名禁用 / : * ? " < > |
) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
// 校验文件后缀
String Extension = FileName.substring(FileName.lastIndexOf(".") + 1).trim();
if (Extension.startsWith("js") // jsp jspx js
|| Extension.startsWith("asp") // asp aspx
|| Extension.startsWith("jar") // jar
|| Extension.startsWith("war") // war
|| Extension.startsWith("php") // php
|| Extension.startsWith("htm") // htm html
|| Extension.startsWith("shtm") // shtml
|| Extension.startsWith("exe") // exe
|| Extension.startsWith("bat") // bat
) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
}
// 校验Multipart入参时的sql攻击
else {
if (item.getString().length() > 5 && SqlParse.isSQLi(item.getString())) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
}
}
}
}
// 全部使用wrapper进行处理
chain.doFilter(httpRequest, response);
} catch (Exception e) {
}
}
protected Log getLogger() {
return log;
}
}
// "mzQ1BAN<'">cjkNLJ" 是sqlmap探测数据库种类的payload
if (Pattern.compile("[`~!@#$%^&*()_+-=,.<>/?;:\[\]{}'"]{3,}").matcher("mzQ1BAN<'">cjkNLJ").find()) {
System.out.println("sqli!");
return;
}
-XX:+UseG1GC -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai -Duser.language=en
if ""%1"" == ""debug"" goto needJavaHome
set "JAVA_OPTS=%JAVA_OPTS% -XX:+UseG1GC -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai -Duser.language=en"
set "JAVA_OPTS=%JAVA_OPTS% -Djdk.serialFilter=maxarray=5000;!java.util.PriorityQueue;!org.apache.commons.collections.functors.ChainedTransformer;!org.apache.commons.collections.functors.InvokerTransformer;!org.apache.commons.collections.functors.InstantiateTransformer;!org.apache.commons.collections4.functors.InvokerTransformer;!org.apache.commons.collections4.functors.InstantiateTransformer;!org.codehaus.groovy.runtime.ConvertedClosure;!org.codehaus.groovy.runtime.MethodClosure;!org.springframework.beans.factory.ObjectFactory;!com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;!org.apache.xalan.xsltc.trax.TemplatesImpl;!com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;"
if [ -z "$JAVA_HOME" ] && [ -z "$JRE_HOME" ]; then
if [[ $EUID -eq 0 ]]; then
echo "Error:tomcat can't be run as root!" 1>&2
exit 1
fi
JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai -Duser.language=en"
JAVA_OPTS="$JAVA_OPTS -Djdk.serialFilter=maxarray=5000;!java.util.PriorityQueue;
!org.apache.commons.collections.functors.ChainedTransformer;
!org.apache.commons.collections.functors.InvokerTransformer;
!org.apache.commons.collections.functors.InstantiateTransformer;
!org.apache.commons.collections4.functors.InvokerTransformer;
!org.apache.commons.collections4.functors.InstantiateTransformer;
!org.codehaus.groovy.runtime.ConvertedClosure;
!org.codehaus.groovy.runtime.MethodClosure;
!org.springframework.beans.factory.ObjectFactory;
!com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
!org.apache.xalan.xsltc.trax.TemplatesImpl;
!com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;"
<Host name="localhost" appBase="webapps" unpackWARs="false" autoDeploy="true">
<Valve className="org.apache.catalina.valves.ErrorReportValve" showReport="false" showServerInfo="false" />
package org.apache.coyote.sec;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
public class BufferedServletResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream buffer;
private ServletOutputStream out;
private PrintWriter writer;
public BufferedServletResponseWrapper(HttpServletResponse resp) throws IOException {
super(resp);
buffer = new ByteArrayOutputStream();//真正存储数据的流
out = new WapperedOutputStream(buffer);
writer = new PrintWriter(new OutputStreamWriter(buffer));
}
// 过滤响应包的head头
public void setHeader(String name, String value) {
if ("allow".equalsIgnoreCase(name)) {
return;
}
if ("server".equalsIgnoreCase(name)) {
return;
}
if ("WWW-Authenticate".equalsIgnoreCase(name)) {
return;
}
super.setHeader(name, value);
}
// 过滤响应包的head头
public void addHeader(String name, String value) {
if ("allow".equalsIgnoreCase(name)) {
return;
}
if ("server".equalsIgnoreCase(name)) {
return;
}
if ("WWW-Authenticate".equalsIgnoreCase(name)) {
return;
}
super.setHeader(name, value);
}
//重载父类获取outputstream的方法
public ServletOutputStream getOutputStream() throws IOException {
return out;
}
//重载父类获取writer的方法
public PrintWriter getWriter() throws UnsupportedEncodingException {
return writer;
}
//重载父类获取flushBuffer的方法
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
if (writer != null) {
writer.flush();
}
}
public void reset() {
buffer.reset();
}
public String getContentString() throws IOException {
//将out和writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据
flushBuffer();
return buffer.toString();
}
public byte[] getContentBytes() throws IOException {
//将out和writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据
flushBuffer();
return buffer.toByteArray();
}
//内部类,对ServletOutputStream进行包装
private class WapperedOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos;
public WapperedOutputStream(ByteArrayOutputStream stream) {
bos = stream;
}
public void write(int b) throws IOException {
bos.write(b);
}
public boolean isReady() {
return false;
}
public void setWriteListener(WriteListener listener) {
}
}
}
package org.apache.coyote.sec;
import org.apache.catalina.filters.RequestFilter;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
/* web.xml注解
<filter>
<filter-name>SecCheckFilter</filter-name>
<filter-class>
org.apache.coyote.sec.SecCheckFilter
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>SecCheckFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
*/
public final class SecCheckFilter extends RequestFilter {
private final Log log = LogFactory.getLog(SecCheckFilter.class);
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
httpRequest = new BufferedServletRequestWrapper(httpRequest);
// 对响应数据使用wrapper进行代理
BufferedServletResponseWrapper httpResponse = new BufferedServletResponseWrapper((HttpServletResponse) response);
response.setCharacterEncoding("UTF-8");
String httpMethod = ((HttpServletRequest) request).getMethod().toLowerCase(Locale.ROOT);
// 禁用 get post options 之外的其他http请求,防止 put move 等上传攻击
try {
switch (httpMethod) {
case "get":
case "post":
case "options":
break;
default:
((HttpServletResponse) response).sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return;
}
// 使用语义waf策略过滤所有非Multipart提交的输入参数
Enumeration<String> enums = httpRequest.getParameterNames();
while (enums.hasMoreElements()) {
String pn = enums.nextElement();
String[] vales = httpRequest.getParameterValues(pn);
// i=1&i=2&i=3 这种情况下,所有的值也都必须过滤一次
for (String vale : vales) {
if (vale.length() > 5 && SqlParse.isSQLi(vale)) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
}
}
boolean isMultipart = ServletFileUpload.isMultipartContent(httpRequest);
if (isMultipart) {
// 校验Multipart上传时的文件后缀
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> fileItems;
fileItems = upload.parseRequest(new ServletRequestContext(httpRequest));
if (fileItems != null && fileItems.size() > 0) {
//遍历Multipart入参
for (FileItem item : fileItems) {
if (!item.isFormField()) {
String FileName = item.getName().toLowerCase(Locale.ROOT);
// 校验文件名中的特殊字符
if (FileName.contains("/") || FileName.contains("\") || FileName.contains(":") || FileName.contains("*")
|| FileName.contains("?") || FileName.contains(""") || FileName.contains("<") || FileName.contains(">")
|| FileName.contains("|") // windows文件名禁用 / : * ? " < > |
) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
// 校验文件后缀
String Extension = FileName.substring(FileName.lastIndexOf(".") + 1).trim();
if (Extension.startsWith("js") // jsp jspx js
|| Extension.startsWith("asp") // asp aspx
|| Extension.startsWith("jar") // jar
|| Extension.startsWith("war") // war
|| Extension.startsWith("php") // php
|| Extension.startsWith("htm") // htm html
|| Extension.startsWith("shtm") // shtml
|| Extension.startsWith("exe") // exe
|| Extension.startsWith("bat") // bat
) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
}
// 校验Multipart入参时的sql攻击
else {
if (item.getString().length() > 5 && SqlParse.isSQLi(item.getString())) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
}
}
}
}
// 全部使用wrapper进行处理
chain.doFilter(httpRequest, httpResponse);
// 请求的filter逻辑走完,就开始处理响应流
byte[] content = httpResponse.getContentBytes();
if (content.length > 0) {
// 如果存在敏感的sql关键词则输出错误,防止sql注入
String str = httpResponse.getContentString();
if (str.contains("You have an error in your SQL syntax")) {
((HttpServletResponse) httpResponse).sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
// 正常输出响应流的内容
ServletOutputStream out = response.getOutputStream();
out.write(content);
out.flush();
}
} catch (Exception e) {
}
}
protected Log getLogger() {
return log;
}
}
-
能够覆盖的类是有限制的,其中不包括 java.lang 包中的类,比如 java.lang.String 这种就不行 -
endorsed 目录:.[jdk 安装目录]./jre/lib/endorsed,不是 jdk/lib/endorsed,目录中放的是 Jar 包,不是.java 或.class 文件,哪怕只重写了一个类也要打包成 jar 包 -
可以在 dos 模式查看修改后的效果 (javac、java),在 eclipse 需要将运行选项中的 JRE 栏设置为 jre (若设置为 jdk 将看不到效果)。 -
重写的类必须满足 jdk 中的规范,例如:自定义的 ArrayList 类也必须实现 List 等接口。 -
这个特性最高只支持到 java8
比如我们要自定义一个 CJException 类,先如下图,将 lib 目录设置为依赖目录
之后我们如下图这样双击 CJException 类名,就可以看到源码了
如下图在 tomcat 源码中建好同样的 CJException 类
之后我们新建一个 maven 项目叫做 jarx,新建 jarx 项目是为了快速打包让 tomcat 引用的,必须保持两者中同名 CJException 类代码必须一致
同样在源码目录下放入 CJException
我们将 tomcat 和 jarx 中的 setVendorCode 都做如下的修改
直接 maven packge 打出一个 jar 包
然后在 tomcat 项目中引用 jarx 的 jar 目录
以调试模式启动 tomcat 项目,如下图所示可以成功下断点
原文始发于微信公众号(XDsecurity):安全中间件的设计思路和简单实践
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论