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">
并修改strut2.xml配置文件头部为2.5版本的:
<!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漏洞修复带来的问题的更多相关文章

  1. Struts2漏洞修复总结

    Struts2的S2-016漏洞是之前比较重大的漏洞,也是一些老系统的历史遗留问题 此漏洞影响struts2.0-struts2.3的所有版本,可直接导致服务器被远程控制从而引起数据泄漏,影响巨大 漏 ...

  2. Struts2 高危漏洞修复方案 (S2-016/S2-017)

    近期Struts2被曝重要漏洞,此漏洞影响struts2.0-struts2.3所有版本,可直接导致服务器被远程控制从而引起数据泄漏,影响巨大,受影响站点以电商.银行.门户.政府居多. 官方描述:S2 ...

  3. struts2漏洞S2-046修复解决方案

    项目验收通过半年之后, 甲方找了一些网络砖家用工具扫描我司做的社保卡申领系统, 找到了struts2漏洞S2-046, 真是服了, 只知道struts2有bug, 现在才知道它漏洞. 砖家们给出了修复 ...

  4. struts2架构网站漏洞修复详情与利用漏洞修复方案

    struts2从开发出来到现在,很多互联网企业,公司,平台都在使用apache struts2系统来开发网站,以及应用系统,这几年来因为使用较多,被攻击者挖掘出来的struts2漏洞也越来越,从最一开 ...

  5. 苹果cms网站漏洞修复解决办法

    苹果cms系统,是目前很多电影网站都在使用的一套网站系统,开源,免费,扩展性较好,支持一键采集,伪静态化,高并发的同时承载,获得的很多站长的喜欢,于近日被网站安全检测发现,maccms存在网站漏洞,s ...

  6. 应用安全-Web安全-漏洞修复方案整理

    通过HTTP头部字段防御措施整理 X-Frame-Options #反劫持 X-XSS-Protection #开启浏览器防XSS功能 Set X-Frame-Options  CSP X-Conte ...

  7. Web安全常见漏洞修复建议

    转载地址:https://security.pingan.com/blog/17.html SQL注入 在服务器端要对所有的输入数据验证有效性. 在处理输入之前,验证所有客户端提供的数据,包括所有的参 ...

  8. struts2漏洞复现分析合集

    struts2漏洞复现合集 环境准备 tomcat安装 漏洞代码取自vulhub,使用idea进行远程调试 struts2远程调试 catalina.bat jpda start 开启debug模式, ...

  9. 织梦dedecms漏洞修复大全(5.7起)

    很多人说dedecms不好,因为用的人多了,找漏洞的人也多了,那么如果我们能修复的话,这些都不是问题. 好,我们来一个一个修复.修复方法都是下载目录下该文件,然后替换或添加部分代码,保存后上传覆盖(记 ...

随机推荐

  1. 学习jvm,关于MAT an internal error occurred during:"Parsing heap dump" from问题

    写了一个死循环不断的创建对象,模拟内存溢出 package com.zuo.test1; import java.util.ArrayList; import java.util.List; publ ...

  2. 5、AFM(Attention+FM)-----Attentional Factorization Machines:Learning the Weight of Feature Interactions via Attention Network

    1.摘要: 提出一个Attentional FM,Attention模型+因子分解机,其通过Attention学习到特征交叉的权重.因为很显然不是所有的二阶特征交互的重要性都是一样的,如何通过机器自动 ...

  3. 批量删除harbor中的镜像

    一 说明 这个是我第一篇博客,所以我想放上原创的东西,尽管我一直都很担心自己写得太low,但是总要学会尝试,学会改变自己,相信自己.在写这个脚本时,由于我接触LInux不是很多,能力有限,仅仅是为了让 ...

  4. 高并发web系统设计

    转载自:http://blog.csdn.net/qq_26562641/article/details/53170913 一.一般高并发web系统这里的一般指的是秒杀之类的电子商务系统,比如说小米抢 ...

  5. js 函数基础(方便复习使用)

    // 函数声明: function bbq(){ // ..... } // 函数表达式: // 1.命名函数表达式 var test = function abc(){ document.write ...

  6. ESRI.ArcGIS.Controls.AxMapControl

    今天在写DLL时发现,直接引用ESRI.ArcGIS.Controls,发现AxMapControl的参数仍然不好用,后来发现,需要引用ESRI.ArcGIS.AxControls这个DLL.而且还需 ...

  7. [React] Refactor a Stateful List Component to a Functional Component with React PowerPlug

    In this lesson we'll look at React PowerPlug's <List /> component by refactoring a normal clas ...

  8. MapReduce 的类型与格式【编写最简单的mapreduce】(1)

    hadoop mapreduce 中的map 和reduce 函数遵循下面的形式 map: (K1, V1) → list(K2, V2) reduce: (K2, list(V2)) → list( ...

  9. Experience Design for Sexable Forum

    Mars March 16, 2015

  10. 4. idea常用快捷键设置(改为eclipse相似)

    转自:https://blog.csdn.net/loveer0/article/details/82697877 idea常用快捷键设置(改为eclipse相似) 目录 idea常用快捷键设置改为e ...