使用Java读取文件或流的最可靠方法(以防止DoS攻击)
目前,我有以下代码用于阅读InputStream
。我将整个文件存储到StringBuilder
变量中,然后再处理此字符串。
public static String getContentFromInputStream(InputStream inputStream)// public static String getContentFromInputStream(InputStream inputStream,
// int maxLineSize, int maxFileSize)
{
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String lineSeparator = System.getProperty("line.separator");
String fileLine;
boolean firstLine = true;
try {
// Expect some function which checks for line size limit.
// eg: reading character by character to an char array and checking for
// linesize in a loop until line feed is encountered.
// if max line size limit is passed then throw an exception
// if a line feed is encountered append the char array to a StringBuilder
// after appending check the size of the StringBuilder
// if file size exceeds the max file limit then throw an exception
fileLine = bufferedReader.readLine();
while (fileLine != null) {
if (!firstLine) stringBuilder.append(lineSeparator);
stringBuilder.append(fileLine);
fileLine = bufferedReader.readLine();
firstLine = false;
}
} catch (IOException e) {
//TODO : throw or handle the exception
}
//TODO : close the stream
return stringBuilder.toString();
}
该代码已向安全团队进行审查,并收到以下评论:
BufferedReader.readLine
容易受到DOS(拒绝服务)攻击(无限长的行,没有换行/回车的巨大文件)StringBuilder
变量的资源耗尽(当文件包含的数据大于可用内存时)
以下是我能想到的解决方案:
创建
readLine
方法(readLine(int limit)
)的替代实现,该方法检查否。读取的字节数,如果超过指定的限制,则抛出自定义异常。逐行处理文件而不完整加载文件。(纯非Java解决方案:))
请建议是否有任何实现上述解决方案的库。还建议比拟议的解决方案提供更鲁棒性或更易于实施的任何替代解决方案。尽管性能也是主要要求,但安全性是第一位的。
回答:
回答:
您想避免各种DOS攻击(在线,文件大小等)。但是在函数的最后,您试图将整个文件转换为一个String
!假设您将行限制为8
KB,但是如果有人向您发送包含两个8 KB行的文件会怎样?行读取部分将通过,但是最终当您将所有内容组合到一个字符串中时,字符串将阻塞所有可用内存。
因此,既然最终您将所有内容都转换为一个String,那么限制行大小并不重要,也不安全。您必须限制文件的整个大小。
其次,您基本上想做的是,您正在尝试分块读取数据。因此,您正在BufferedReader
逐行使用和阅读它。但是,您想要做的事情以及最终真正想要的是一种逐段读取文件的方式。为什么不一次读取2
KB,而不是一次读取一行?
BufferedReader
-顾名思义,其中有一个缓冲区。您可以配置该缓冲区。假设您创建的BufferedReader
缓冲区大小为2 KB:
BufferedReader reader = new BufferedReader(..., 2048);
现在,如果InputStream
您传递给BufferedReader
的数据具有100 KB的数据,则一次BufferedReader
将自动读取2
KB。因此它将读取流50次,每次2 KB(50x2KB = 100 KB)。同样,如果创建的BufferedReader
缓冲区大小为10
KB,它将读取10次输入(10x10KB = 100 KB)。
BufferedReader
已经完成了逐块读取文件的工作。因此,您不想在其上方逐行添加额外的层。只关注最终结果-
如果结尾的文件太大(>可用RAM)-您将如何将其转换为String
结尾?
一种更好的方法是将事物作为一个整体传递CharSequence
。这就是Android的功能。在整个Android
API中,您将看到它们CharSequence
随处可见。由于StringBuilder
也是的子类CharSequence
,因此Android会根据输入的大小/性质在内部使用String
,或a
StringBuilder
或其他一些优化的字符串类。因此,您宁可StringBuilder
在阅读完所有内容后直接返回对象本身,而不是将其转换为String
。这将对大数据更安全。StringBuilder
它还在内部保留了相同的缓冲区概念,它将在内部为大字符串分配多个缓冲区,而不是一个长字符串。
因此总体而言:
- 限制整体文件的大小,因为您将在某个时候处理全部内容。忘记限制或分割线
- 分块阅读
使用Apache Commons IO,这是将a中的数据读取BoundedInputStream
为的方式StringBuilder
,该方式分为2
KB的块而不是行:
// import org.apache.commons.io.output.StringBuilderWriter;// import org.apache.commons.io.input.BoundedInputStream;
// import org.apache.commons.io.IOUtils;
BoundedInputStream boundedInput = new BoundedInputStream(originalInput, <max-file-size>);
BufferedReader reader = new BufferedReader(new InputStreamReader(boundedInput), 2048);
StringBuilder output = new StringBuilder();
StringBuilderWriter writer = new StringBuilderWriter(output);
IOUtils.copy(reader, writer); // copies data from "reader" => "writer"
return output;
回答:
使用BoundedInputStream从Apache的百科全书IO库。您的工作变得更加轻松。
以下代码将执行您想要的操作:
public static String getContentFromInputStream(InputStream inputStream) { inputStream = new BoundedInputStream(inputStream, <number-of-bytes>);
// Rest code are all same
您只需InputStream
用来包装,BoundedInputStream
然后指定最大尺寸。BoundedInputStream
将注意将读取限制为最大大小。
或者,您可以在创建阅读器时执行以下操作:
BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(
new BoundedInputStream(inputStream, <no-of-bytes>)
)
);
基本上,我们在这里要做的是限制InputStream
层本身的读取大小,而不是在读取行时这样做。因此,您最终得到了一个可重用的组件,例如BoundedInputStream
它会限制InputStream层的读取,并且您可以在任何需要的地方使用它。
编辑:添加了脚注
编辑2:根据评论添加了更新的答案
以上是 使用Java读取文件或流的最可靠方法(以防止DoS攻击) 的全部内容, 来源链接: utcz.com/qa/426415.html