记一次struts2漏洞修复带来的问题
struts2作为万年漏洞王,感觉已经被弃如敝屣了,除了一些古老的项目,比如我手上的一个项目,以前每次出现漏洞就如临大敌,手忙脚乱的赶在公司红头文件发出来前修复它。然后改了一两次后毅然决然用别的框架代替它了。
万事大吉,再也不用担心struts2出漏洞了。然而上个月又爆出了个,还有个还在维护期的古老项目需要修复。。。又要填坑了。
攻略是将struts2的jar包替换,然后加一些配置就完了。
修复办法:
然后带来了一系列问题。
由于struts包由2.3.?升级到2.5.16,很多依赖包、配置需要响应调整。找了个总结的:
1,2.5.X版本不再提供xwork.jar ,整合到了 struts-core包中。
2,方法不能访问的问题,需要在每个action配置文件中加上 strict-method-invocation="false":
<package name="login" namespace="/login" extends="struts-default" strict-method-invocation="false">
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"><struts>
3 ,session失效的问题,针对weblogic server,增加session-descriptor节点:
<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app xmlns="http://www.bea.com/ns/weblogic/90">
<context-root>/ynwjnw</context-root>
<container-descriptor>
<servlet-reload-check-secs>-1</servlet-reload-check-secs>
<prefer-web-inf-classes>true</prefer-web-inf-classes>
</container-descriptor>
<session-descriptor>
<cookie-name>JSESSIONID1</cookie-name>
</session-descriptor>
</weblogic-web-app>
4,2.5.16版本jdk要求1.7。1.8版本编译后部署有问题
web.xml中
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
修改为
org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
5.struts2对multipart/form-data解析需要自定义方法解析,否则丢失表单参数。
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest"
name="requestParser" class="com.***.omp.***.util.MyMultiPartRequest"
scope="default"/>
由于xwork做了大改,AbstractMultiPartRequest类的一些属性和方法也修改了。MyMultiPartRequest类也需要相应调整。
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.dispatcher.LocalizedMessage;
import org.apache.struts2.dispatcher.multipart.AbstractMultiPartRequest;
import org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest;
import org.apache.struts2.dispatcher.multipart.StrutsUploadedFile;
import org.apache.struts2.dispatcher.multipart.UploadedFile; public class MyMultiPartRequest extends AbstractMultiPartRequest{ static final Logger LOG = LogManager.getLogger(JakartaMultiPartRequest.class); // maps parameter name -> List of FileItem objects
protected Map<String, List<FileItem>> files = new HashMap<String, List<FileItem>>(); // maps parameter name -> List of param values
protected Map<String, List<String>> params = new HashMap<>(); // any errors while processing this request
protected List<LocalizedMessage> errors = new ArrayList<LocalizedMessage>(); // protected long maxSize;
// private Locale defaultLocale = Locale.ENGLISH; /**
* Creates a new request wrapper to handle multi-part data using methods adapted from Jason Pell's
* multipart classes (see class description).
*
* @param saveDir the directory to save off the file
* @param request the request containing the multipart
* @throws java.io.IOException is thrown if encoding fails.
*/
public void parse(HttpServletRequest request, String saveDir) throws IOException {
try {
setLocale(request);
processUpload(request, saveDir);
} catch (FileUploadBase.SizeLimitExceededException e) {
if (LOG.isWarnEnabled()) {
LOG.warn("Request exceeded size limit!", e);
}
LocalizedMessage errorMessage = buildErrorMessage(e, new Object[]{e.getPermittedSize(), e.getActualSize()});
if (!errors.contains(errorMessage)) {
errors.add(errorMessage);
}
} catch (Exception e) {
if (LOG.isWarnEnabled()) {
LOG.warn("Unable to parse request", e);
}
LocalizedMessage errorMessage = buildErrorMessage(e, new Object[]{});
if (!errors.contains(errorMessage)) {
errors.add(errorMessage);
}
}
} protected void setLocale(HttpServletRequest request) {
if (defaultLocale == null) {
defaultLocale = request.getLocale();
}
} protected LocalizedMessage buildErrorMessage(Throwable e, Object[] args) {
String errorKey = "struts.messages.upload.error." + e.getClass().getSimpleName();
LOG.debug("Preparing error message for key: [{}]", errorKey); return new LocalizedMessage(this.getClass(), errorKey, e.getMessage(), args);
} private void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {
for (FileItem item : parseRequest(request, saveDir)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Found item " + item.getFieldName());
}
if (item.isFormField()) {
processNormalFormField(item, request.getCharacterEncoding());
} else {
processFileField(item);
}
}
} private void processFileField(FileItem item) {
if (LOG.isDebugEnabled()) {
LOG.debug("Item is a file upload");
} // Skip file uploads that don't have a file name - meaning that no file was selected.
if (item.getName() == null || item.getName().trim().length() < 1) {
LOG.debug("No file has been uploaded for the field: " + item.getFieldName());
return;
} List<FileItem> values;
if (files.get(item.getFieldName()) != null) {
values = files.get(item.getFieldName());
} else {
values = new ArrayList<FileItem>();
} values.add(item);
files.put(item.getFieldName(), values);
} private void processNormalFormField(FileItem item, String charset) throws UnsupportedEncodingException {
if (LOG.isDebugEnabled()) {
LOG.debug("Item is a normal form field");
}
List<String> values;
if (params.get(item.getFieldName()) != null) {
values = params.get(item.getFieldName());
} else {
values = new ArrayList<String>();
} // note: see http://jira.opensymphony.com/browse/WW-633
// basically, in some cases the charset may be null, so
// we're just going to try to "other" method (no idea if this
// will work)
if (charset != null) {
values.add(item.getString(charset));
} else {
values.add(item.getString());
}
params.put(item.getFieldName(), values);
item.delete();
} private List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException {
DiskFileItemFactory fac = createDiskFileItemFactory(saveDir);
ServletFileUpload upload = new ServletFileUpload(fac);
upload.setSizeMax(maxSize);
// 设置监听器
FileUploadListener progressListener = new FileUploadListener(servletRequest);
upload.setProgressListener(progressListener);
return upload.parseRequest(createRequestContext(servletRequest));
} private DiskFileItemFactory createDiskFileItemFactory(String saveDir) {
DiskFileItemFactory fac = new DiskFileItemFactory();
// Make sure that the data is written to file
fac.setSizeThreshold(0);
if (saveDir != null) {
fac.setRepository(new File(saveDir));
}
return fac;
} /* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileParameterNames()
*/
public Enumeration<String> getFileParameterNames() {
return Collections.enumeration(files.keySet());
} /* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getContentType(java.lang.String)
*/
public String[] getContentType(String fieldName) {
List<FileItem> items = files.get(fieldName); if (items == null) {
return null;
} List<String> contentTypes = new ArrayList<String>(items.size());
for (FileItem fileItem : items) {
contentTypes.add(fileItem.getContentType());
} return contentTypes.toArray(new String[contentTypes.size()]);
} /* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFile(java.lang.String)
*/
public UploadedFile[] getFile(String fieldName) {
List<FileItem> items = files.get(fieldName); if (items == null) {
return null;
} List<UploadedFile> fileList = new ArrayList<>(items.size());
for (FileItem fileItem : items) {
File storeLocation = ((DiskFileItem) fileItem).getStoreLocation();
if (fileItem.isInMemory() && storeLocation != null && !storeLocation.exists()) {
try {
storeLocation.createNewFile();
} catch (IOException e) {
LOG.error("Cannot write uploaded empty file to disk: {}", storeLocation.getAbsolutePath(), e);
}
}
fileList.add(new StrutsUploadedFile(storeLocation));
} return fileList.toArray(new UploadedFile[fileList.size()]);
} /* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileNames(java.lang.String)
*/
public String[] getFileNames(String fieldName) {
List<FileItem> items = files.get(fieldName); if (items == null) {
return null;
} List<String> fileNames = new ArrayList<String>(items.size());
for (FileItem fileItem : items) {
fileNames.add(getCanonicalName(fileItem.getName()));
} return fileNames.toArray(new String[fileNames.size()]);
} /* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFilesystemName(java.lang.String)
*/
public String[] getFilesystemName(String fieldName) {
List<FileItem> items = files.get(fieldName); if (items == null) {
return null;
} List<String> fileNames = new ArrayList<String>(items.size());
for (FileItem fileItem : items) {
fileNames.add(((DiskFileItem) fileItem).getStoreLocation().getName());
} return fileNames.toArray(new String[fileNames.size()]);
} /* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameter(java.lang.String)
*/
public String getParameter(String name) {
List<String> v = params.get(name);
if (v != null && v.size() > 0) {
return v.get(0);
} return null;
} /* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterNames()
*/
public Enumeration<String> getParameterNames() {
return Collections.enumeration(params.keySet());
} /* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterValues(java.lang.String)
*/
public String[] getParameterValues(String name) {
List<String> v = params.get(name);
if (v != null && v.size() > 0) {
return v.toArray(new String[v.size()]);
} return null;
} /* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getErrors()
*/
public List<LocalizedMessage> getErrors() {
return errors;
} /**
* Returns the canonical name of the given file.
*
* @param filename the given file
* @return the canonical name of the given file
*/
// private String getCanonicalName(String filename) {
// int forwardSlash = filename.lastIndexOf("/");
// int backwardSlash = filename.lastIndexOf("\\");
// if (forwardSlash != -1 && forwardSlash > backwardSlash) {
// filename = filename.substring(forwardSlash + 1, filename.length());
// } else if (backwardSlash != -1 && backwardSlash >= forwardSlash) {
// filename = filename.substring(backwardSlash + 1, filename.length());
// }
//
// return filename;
// } /**
* Creates a RequestContext needed by Jakarta Commons Upload.
*
* @param req the request.
* @return a new request context.
*/
private RequestContext createRequestContext(final HttpServletRequest req) {
return new RequestContext() {
public String getCharacterEncoding() {
return req.getCharacterEncoding();
} public String getContentType() {
return req.getContentType();
} public int getContentLength() {
return req.getContentLength();
} public InputStream getInputStream() throws IOException {
InputStream in = req.getInputStream();
if (in == null) {
throw new IOException("Missing content in the request");
}
return req.getInputStream();
}
};
} /* (non-Javadoc)
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#cleanUp()
*/
public void cleanUp() {
Set<String> names = files.keySet();
for (String name : names) {
List<FileItem> items = files.get(name);
for (FileItem item : items) {
LOG.debug("Removing file {} {}", name, item );
if (!item.isInMemory()) {
item.delete();
}
}
}
} }
修改完成,以为完事大吉,但又出问题了。发现上传功能后台接受表单的参数会叠加,比如第一次传name是123,第二次再传123,name变成了123,123.经过定位,排除了前端问题。后端debug发现是每次参数都会叠加,目测是单例问题。但struts2默认是多例的,为什么会出现这个问题呢。配置没改,multi数据解析类也只是改了一些兼容性问题。难道我记错了?于是尝试把action上能加@scope("prototype")的地方都尝试加下,发现没解决问题,继续debug。发现自定义的MyMultiPartRequest类每次解析都会保存上一次的内容。原因找到了,这个类是单例的。看了下配置,bean的scope是“dufault”,默认是单例?不会呀。改成多例试试,问题解决。查了下原因,Struts2单独使用是多例,但交给spring管理后默认是单例了?百撕不得其解。
问题来了。之前的版本为什么没出现这个问题?未完待续。。。
记一次struts2漏洞修复带来的问题的更多相关文章
- Struts2漏洞修复总结
Struts2的S2-016漏洞是之前比较重大的漏洞,也是一些老系统的历史遗留问题 此漏洞影响struts2.0-struts2.3的所有版本,可直接导致服务器被远程控制从而引起数据泄漏,影响巨大 漏 ...
- Struts2 高危漏洞修复方案 (S2-016/S2-017)
近期Struts2被曝重要漏洞,此漏洞影响struts2.0-struts2.3所有版本,可直接导致服务器被远程控制从而引起数据泄漏,影响巨大,受影响站点以电商.银行.门户.政府居多. 官方描述:S2 ...
- struts2漏洞S2-046修复解决方案
项目验收通过半年之后, 甲方找了一些网络砖家用工具扫描我司做的社保卡申领系统, 找到了struts2漏洞S2-046, 真是服了, 只知道struts2有bug, 现在才知道它漏洞. 砖家们给出了修复 ...
- struts2架构网站漏洞修复详情与利用漏洞修复方案
struts2从开发出来到现在,很多互联网企业,公司,平台都在使用apache struts2系统来开发网站,以及应用系统,这几年来因为使用较多,被攻击者挖掘出来的struts2漏洞也越来越,从最一开 ...
- 苹果cms网站漏洞修复解决办法
苹果cms系统,是目前很多电影网站都在使用的一套网站系统,开源,免费,扩展性较好,支持一键采集,伪静态化,高并发的同时承载,获得的很多站长的喜欢,于近日被网站安全检测发现,maccms存在网站漏洞,s ...
- 应用安全-Web安全-漏洞修复方案整理
通过HTTP头部字段防御措施整理 X-Frame-Options #反劫持 X-XSS-Protection #开启浏览器防XSS功能 Set X-Frame-Options CSP X-Conte ...
- Web安全常见漏洞修复建议
转载地址:https://security.pingan.com/blog/17.html SQL注入 在服务器端要对所有的输入数据验证有效性. 在处理输入之前,验证所有客户端提供的数据,包括所有的参 ...
- struts2漏洞复现分析合集
struts2漏洞复现合集 环境准备 tomcat安装 漏洞代码取自vulhub,使用idea进行远程调试 struts2远程调试 catalina.bat jpda start 开启debug模式, ...
- 织梦dedecms漏洞修复大全(5.7起)
很多人说dedecms不好,因为用的人多了,找漏洞的人也多了,那么如果我们能修复的话,这些都不是问题. 好,我们来一个一个修复.修复方法都是下载目录下该文件,然后替换或添加部分代码,保存后上传覆盖(记 ...
随机推荐
- C语言基础 (2) linux命令
01.课程回顾 链接 ln 1.txt aaa.txt 硬链接 (两个相互独立 删除一个另外一个还在) ln -s 1.txt aaa.txt软连接 (后面的是快捷方式) 硬链接只能是文件,软连接可 ...
- Linux 磁盘坏道检测和修复
今天在实验室碰到一台机器,根分区和/upgrade分区变成了read-only system.当碰到这个问题的时候,我的第一反应很可能硬件出现了故障,我使用了如下的方法来检测和排除故障: 使用dmes ...
- 【转】 值得推荐的C/C++框架和库 (真的很强大)
[转] 值得推荐的C/C++框架和库 (真的很强大) 值得学习的C语言开源项目 - 1. Webbench Webbench是一个在linux下使用的非常简单的网站压测工具.它使用fork()模拟多个 ...
- windos环境python3.5安装 paramiko
一.执行命令pip install paramiko,情况如下: C:\Users\ZFH>pip install paramikoCollecting paramiko Downloadin ...
- COGS——T 1578. 次小生成树初级练习题
http://www.cogs.pro/cogs/problem/problem.php?pid=1578 ☆ 输入文件:mst2.in 输出文件:mst2.out 简单对比时间限制:1 ...
- Codeforces Round #271 (Div. 2) 解题报告
题目地址:http://codeforces.com/contest/474 A题:Keyboard 模拟水题. 代码例如以下: #include <iostream> #include ...
- 又见关系并查集 以POJ 1182 食物链为例
简单的关系并查集一般非常easy依据给出的关系搞出一个有向的环,那么两者之间的关系就变成了两者之间的距离. 对于此题: 若u.v不在一个集合内,则显然此条语句会合法(暂且忽略后两条.下同). 那么将f ...
- Linux平台不同解压缩命令的使用方法
作者:郭孝星 微博:郭孝星的新浪微博 邮箱:allenwells@163.com 博客:http://blog.csdn.net/allenwells github:https://github.co ...
- 由动态库文件dll生成lib库文件
本文基于OpenBlas的编译和安装.来说明怎样从一个dll文件生成lib库文件. 參考OpenBlas的说明"Howto generate import library for MingW ...
- JAVA学习(五):Java面向对象编程基础
Java面向对象编程基础 面向对象(Object oriented programming,OOP)技术是一种强有力的软件开发方法,它採用数据抽象与信息隐藏技术,来使软件开发简单化,以达到代码重用的目 ...