我写过服务器(Ubuntu 14.04 LTS 服务器)- 客户端 - Java 程序。
通过这个程序,我可以从我的 Android 智能手机发送文本到我的服务器并将其保存到文件中receivedData
。
该程序在默认情况下未分配 shell 且无权访问敏感数据或程序的用户下运行。
我能想到的唯一安全问题是由于大量 GB 的receivedData
文件没有大小检查而导致内存不足。或者,当然是 DoS 攻击,但我认为我的服务器没那么重要...
该程序仅用于学习目的,不得由任何客户/黑客使用(当然;))但您现在永远不会知道......
那么,有人能告诉我是否可以直接创建一个receivedData
具有文件大小限制的文件吗?当文件大小达到限制时,它应该拒绝receivedData
阻止它导致 OOM。
如果没有的话,我将需要将其实现到 Java 程序中......
答案1
有两种方法可以实现此目的:
- 使用
ulimit
shell 实用程序,或者使用setrlimit
系统调用(依次ulimit
调用)。 - 使用文件系统配额,以及服务器进程的特殊用户,将限制全部的该用户的使用情况
ulimit
/setrlimit
RLIMIT_FSIZE
The maximum size of files that the process may create. Attempts
to extend a file beyond this limit result in delivery of a
SIGXFSZ signal. By default, this signal terminates a process,
but a process can catch this signal instead, in which case the
relevant system call (e.g., write(2), truncate(2)) fails with
the error EFBIG.
我不确定如何setrlimit
从 Java 调用该函数,但是这个 U&L 问题可能会有帮助。或者,将 Java 服务器包装在脚本中:
#! /bin/bash
ulimit -f 1073741824 # 1GB
java ....
文件系统配额:
我假设是 ext4 文件系统,其他的我不确定。首先,我们需要在文件系统上启用配额。编辑/etc/fstab
,并添加usrquota
到相应分区的挂载选项中。/
例如,如果我们对 执行此操作,则最终条目将如下所示:
UUID=... / ext4 errors=remount-ro,usrquota 0 1
然后重启后启用它:
sudo quotaon /
我假设您的服务器以其他用户身份运行(例如restricted_user
)。如果不是,请创建一个,如果您不希望正常流程受到影响。
然后做:
sudo edquota restricted_user
这将打开一个编辑器。
Disk quotas for user restricted_user (uid 1001):
Filesystem blocks soft hard inodes soft hard
/dev/sdd1 1087940 943718 1048576 9956 0 0
将软限制和硬限制设置为适当的值(它们是 1024 字节块,因此将限制除以 1024)。保存并退出。
那么用户将不能超过全部的该文件系统上使用的 1G 空间,计算其拥有的所有文件。
当然,您仍然应该在服务器程序中检查大小。
答案2
更新:
muru 的回答对于文件大小来说是完美的,我在这里给出如何调整目录本身的大小,而不仅仅是文件的大小的解决方案。
不存在直接这样的事情,因为文件系统将文件夹视为文件的文件,并且其限制是整个文件系统本身。
因此,为了解决您的问题,您可以采取一些技巧,创建一个虚拟文件系统并将其挂载到特定的(空)目录中。
创建要挂载的虚拟文件系统的挂载点(对我来说是〜/ test)
mkdir ~/test
创建一个充满 /dev/zero 的文件,足够大到您想要为虚拟文件系统保留的最大大小(我在这里将其设置为 1MB)
dd if=/dev/zero of=test.img bs=1024 count=0 seek=1024
使用 ext4 文件系统格式化此文件
mke2fs test.img
将新格式化的磁盘空间挂载到您创建的目录中作为挂载点
mount -o loop test.img ~/test
然后使用该目录
答案3
我认为检查 java 程序内部的文件大小是最安全的。
只要分区中有足够的空间,您就可以向任何您有写权限的文件附加内容,所以您不能直接限制文件的大小。
我唯一能想到的办法就是只为这个文件创建一个分区,这样文件大小就会被限制在分区大小之内。另外,如果你将分区格式化为 FAT32,文件的最大大小可以达到 4 GiB。
答案4
如果看不到你的代码,就很难判断你的应用程序是否会 OOM。
通常,您会从套接字读取一个可管理的数据块(例如 1MB),然后将该数据块直接写入文件。这样,您每次在内存中存储的数据都不会超过临时数据块。
通常这看起来像:
FileOutputStream fos = new FileOutputStream("receivedData");
InputStream is = clientSocket.getInputStream();
byte[] buffer = new byte[1024];
int read;
while((read = is.read(buffer)) != -1) {
fos.write(buffer);
};
然后,您可以向 while 循环添加一个计数器,以计算已写入文件的字节数,并在超过预定数量或接近消耗所有可用磁盘空间时终止套接字。