JAVA开发常见安全问题:任意文件读取与写入
专栏链接
一、数据的校验
- SQL注入
- 命令注入
- 跨站脚本
- 任意文件上传
- 任意文件下载
- 任意文件删除
- 任意文件读取与写入
- Iframe 框架钓鱼
数据的校验:任意文件读取与写入
漏洞描述:
在文件读取过程中,根据系统的业务需求,程序往往会从前端传入文件的名称或者文件的完整地址用于文件读取。由于程序中未对“…/”目录跳转字符进行安全过滤,导致攻击者可以读取或写入任意目录下的文件。
检测方法:
检查文件路径是否可以被控制(传入的参数值是否为路径的一部分)。
检查是否对不可信参数值过滤了“…/”这样的特殊字符, 防止操作任意目录下的文件。
检查过滤“…/”的代码是否存在可被绕过的危险,过滤应该采用循环过滤的方式,直到过滤完全(即过滤之后的值中不存在“…/”)。
不合规的代码示例:
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
String op = request.getParameter("op"); String pb = request.getParameter("pb"); response.setCharacterEncoding("UTF-8"); if (op.equals("r")) {
String fn = request.getParameter("fileName"); String dir = request.getParameter("rdir"); String fp = request.getRealPath("/duobee/data/" +
dir + fn + ".xml");
File f = new java.io.File(fp);
if (f.exists()) {
String line; // 用来保存每行读取的内容StringBuffer stringBuffer = new StringBuffer();
InputStream is = null;
BufferedReader reader = null;
try {
is = new FileInputStream(fp); reader = new BufferedReader(new
InputStreamReader(is, "UTF-8"));
line = reader.readLine(); // 读取第一行
while (line != null) { // 如果 line 为空说明读完了
stringBuffer.append(line);
line = reader.readLine(); // 读取下一行
}
} catch (Exception e) { e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (reader != null) { reader.close();}
}Catch(Exeception E2){
}
}
response.getWriter().print(stringBuffer.toString());
response.getWriter().flush(); response.getWriter().close();
} else {
ServletOutputStream output = response.getOutputStream();
// 返回一个空文件if (pb == null) {
output.println("<div id="diagram"
class="osdiagram">");
output.println("");
output.println("
<dl><dt>width</dt><dd>801</dd><dt>height</dt><dd>601</d d></dl>");
output.println("不合规说明:
程序根据传入的 op 参数值区分应执行读取文件还是执行写入文件操作。但是对于不可信的 fileName 参数和 rdir 参数,程序将这些参数的值组合成完整的文件路径,且未对目录跳转字符(“…/”)进行安全过滤。
合规的代码示例:
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
String op = request.getParameter("op");
String pb = request.getParameter("pb");
response.setCharacterEncoding("UTF-8");
if (op.equals("r")) {
String fn = request.getParameter("fileName");
String dir = request.getParameter("rdir");
String fp = request.getRealPath("/duobee/data/" +dir + fn + ".xml");
int len1=0,len2=0;
do{
len1 = fp.length();
fp = fp.replaceAll("../", "");
len2 = fp.length();
}while(len1 != len2);
File f = new java.io.File(fp);
if (f.exists()) {
String line; // 用来保存每行读取的内容
StringBuffer stringBuffer = new StringBuffer();
InputStream is = null;
BufferedReader reader = null;
try {
is = new FileInputStream(fp);
reader = new BufferedReader(new
InputStreamReader(is, "UTF-8"));
line = reader.readLine(); // 读取第一行
while (line != null) { // 如果 line 为空说明读完了
stringBuffer.append(line);
line = reader.readLine(); // 读取下一行
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (reader != null) { reader.close();
}
} catch (Exception e2) {}
}
response.getWriter().print(stringBuffer.toString());
response.getWriter().flush();
response.getWriter().close();
} else {
ServletOutputStream output =response.getOutputStream();
// 返回一个空文件if (pb == null) {
output.println("");
output.println(" ");
output.println("
<dl>
<dt>width</dt>
<dd>801</dd>
<dt>height</dt>
<dd>601</dd>
</dl>
");
output.println("");
}
else {
output.println(" ");
}
output.flush(); output.close();
}
} else if (op.equals("w")){
ServletOutputStream output =response.getOutputStream();
String fn = request.getParameter("fileName");
String dir = request.getParameter("rdir");
String filename =request.getRealPath("/duobee/data/" + dir + fn + ".xml"); int len1=0,len2=0;
do{
len1 = filename.length();
filename = filename.replaceAll("../","");
len2 = filename.length();
}while(len1 != len2);
java.io.File f = new java.io.File(filename);
out.println(filename);
if (!f.exists())// 如果文件不存,则建立
{
f.getParentFile().mkdirs();
}
try {
OutputStream os = new FileOutputStream(filename);
PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
writer.print(request.getParameter("xhtml"));
writer.flush();
os.close();
writer.close();
out.clear();
out = pageContext.pushBody();
} catch (java.io.IOException e) { out.println(e.getMessage());
}
output.println("OK "); output.flush();
output.close();
}
out.clear();
out = pageContext.pushBody();
>
合规说明:
对于不可信的 fileName 参数和 rdir 参数,程序采用循环过滤的方式,对目录跳转字符(“…/”)进行安全过滤。








