首页 > 其他分享 >多种语言---安全的文件操作示例

多种语言---安全的文件操作示例

时间:2022-11-21 14:59:03浏览次数:50  
标签:文件 示例 多种语言 校验文件 st --- file 普通用户 root

文件校验方式

读取或者写入文件时必须文件进行校验,防止软连接攻击或者提权攻击,如果校验后再打开文件操作,很容易被构造条件竞争攻击。因此较安全的方式是先将文件打开,然后再校验,校验不通过时关闭文件,打开文件后文件不可能再被修改。

常见文件相关攻击路径

OOM(Out of Memory):

角色: 所有
原理: 构造超大文件或者超长数据报文,程序一次性读入到内存中,导致内存不够导致被操作系统杀掉或者进入死循环。
防御:

  1. 读文件前校验文件类型,不能是/dev/zero、/dev/urandom等设备类型
  2. 读取前校验文件大小是否超过预期中的最大值
  3. 读取文件、收取数据时传入最大期望大小

root修改普通用户的文件权限

角色: root
原理: root用户修改普通用户文件的权限。普通用户将文件改成软连接指向root自身的某个文件。导致root实际修改了自身文件的权限。普通用户可将系统任意文件修改为特定权限。可对系统可用性造成影响。
防御:

  1. root不应修改普通用户文件的权限。
  2. root修改文件权限前必须校验不是软连接
  3. chown命令必须加-h选项
  4. root用chmod修改普通用户文件的权限时, 先切换到普通用户再做修改
  5. 明确文件owner,root修改时应校验文件owner为期望值

root写普通用户的文件,导致任意文件破坏

角色: root
原理: root写普通用户文件。普通用户将文件改成软连接指向系统任意文件。由于root可写任意文件,可导致任意文件被破坏。
防御:

  1. root写普通用户文件前必须校验文件是否为软连接。
  2. root写普通用户文件前必须校验文件owner是否是期望的owner

root写普通用户的目录中的某个文件,导致特定文件破坏

角色: root
原理: root到普通的目录写入某个文件。普通用户将目录软连到/etc,/usr等系统目录。可导致将特定文件放到任意位置,当特定文件与系统文件重名时,可破坏系统可用性
防御:

  1. root写普通用户文件前必须判断文件上层目录是否是软连接,需向上递归到root自己的目录为止

root执行普通用户的文件

角色: root
原理:

  1. root执行普通用户的脚本或者二进制
  2. root加载普通用户的动态库
  3. root解析普通用户的配置文件,并执行其中内容

防御:

  1. 禁止root执行普通用户的脚本或者二进制
  2. 禁止root加载普通用户的动态库
  3. 禁止root加载普通用户的配置文件,如果必须加载必须做充分校验

判断文件是否存在

打开文件前不需要判断文件存在,通过打开文件的成功和失败来判断文件是否存在。

  • 错误的写法
 
1 if (access(file_path, F_OK) != 0) {
2 log("file not exist")
3 return -1;
4 }
5 // access 和实际打开存在时间差。
6 int fd = open(file_path);
7 read(fd, buf, size);
  • 正确的写法
 
1 int fd = open(file_path);
2 if (fd < 0) {
3 log("open failed: %s", strerror(errno)); // 可通过errno记录失败原因
4 return -1;
5 }
6 read(fd, buf, size);

C 文件校验

 
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <unistd.h>
4 #include <fcntl.h>
5  
6 #define MAX_FILE_SIZE (1024 * 10)
7  
8 // 系统调用方式
9 struct stat st = {0};
10 // 打开是可选择是否打开软连接,当设置O_NOFOLLOW时,如果文件是软连接,将打开失败
11 int fd = open(file_path, O_NOFOLLOW | O_RDONLY);
12 if (fd < 0) {
13 return -1;
14 }
15  
16 if (fstat(fd, &st) != 0
17 || st.st_size > MAX_FILE_SIZE // 校验文件大小
18 || st.st_uid != os.geteuid // 校验文件owner
19 || S_ISLNK(st.st_mode)) { // 校验是否是软连接
20 return -1;
21 }
22 close(fd)
23  
24 // 文件流方式
25 FILE *file = fopen(file_path, "rb");
26 if (file == NULL) {
27 return -1;
28 }
29 // 打开后校验 1. 文件大小 2. 软连接
30 if (fstat(fileno(file), &st) !=0 ||
31 || st.st_size > MAX_FILE_SIZE // 校验文件大小
32 || st.st_uid != os.geteuid() // 校验文件owner
33 || S_ISLNK(st.st_mode)) { // 校验是否是软连接
34 return -1;
35 }
36  
37  
38 // 如果需要修改文件权限,也需要校验后通过fd修改
39 int fchmod(fd, S_IRUSR | S_IWUSR);
40 fclose(file);

C++ 文件校验

 
1 ifstream input(file_path, std::ios::binary | std::ios::ate);
2 if (!input.is_open()) {
3 return FAILED;
4 }
5 // 校验文件大小
6 if (input.tellg() > MAX_FILE_SIZE) {
7 return FAILED;
8 }
9 // 校验完之后需要将偏移回文件开头
10 input.seekg(0, std::ios::beg);
  • 注意C++为了保持操作系统之间的兼容性,并未提供获取fd和FILE的标准方式,因此无法实现打开后通过文件描述符校验文件owner和修改文件权限等较安全的操作。文件操作应使用C方式。

Python 文件校验

 
1 import os
2 import stat
3 with open(file_path) as f:
4 file_info = os.stat(f.fileno())
5 # 校验文件是否是软连接
6 if stat.S_ISLNK(file_info.st_mode):
7 raise ...
8 # 校验文件大小
9 if file_info.st_size > MAX_SIZE:
10 raise ...
11 # 校验文件owner
12 if file_info.st_uid != os.geteuid():
13 raise ...
14 # 如果需要修改文件权限
15 os.fchmod(f.fileno(), 0o600)
  • 注意: Python的read函数默认会读取文件所有内容,所以调用read一定要传入期望的最大文件大小,防止OOM
 
1 content = file.read(MAX_SIZE)

go 文件校验

 
1 file, err := os.Open(file_path)
2 if err != nil {
3 return err
4 }
5 defer file.Close()
6 file_info, err := file.Stat()
7 if err != nil {
8 return err
9 }
10 // 校验文件大小
11 if file_info.Size() > MAX_SIZE {
12 return errors.New(fmt.Sprintf("file size error %v", file_info.Size()))
13 }
14 // 校验文件是否软连接
15 if (file_info.Mode() & fs.ModeSymlink) != 0 {
16 return errors.New("file is softlink")
17 }
18  
19 // 校验文件owner
20 if st := file_info.Sys(); st.(*syscall.Stat_t).Uid != uint32(os.Geteuid()) {
21 return errors.New("file owner incorrect")
22 }
23  
24 // 如果需要修改文件权限
25 if err := file.Chmod(0600); err != nil {
26 return err
27 }
  • 注意 Go中的ioutil.ReadAll函数会一次性读入文件所有内容,不建议使用

标签:文件,示例,多种语言,校验文件,st,---,file,普通用户,root
From: https://www.cnblogs.com/gongxianjin/p/16911359.html

相关文章

  • Vue form-create的基本使用
    Vueform-create的基本使用 原文地址:https://blog.csdn.net/muguli2008/article/details/105738336/前言由于之前有个Web项目,大部分都是表单,而且这些表单是通过请求......
  • docker-entrypoint.sh 文件的用处
    参考出处很多著名库的Dockerfile文件中,通常都是ENTRYPOINT字段会是这样:ENTRYPOINT["docker-entrypoint.sh"]这里我们参考分析下MySQL的Dockerfile文件,来认识下d......
  • AtCoder Regular Contest 152 (A-D)
    根本不知道有ARC。然后unratedregister。然后一直在聊天,只写了A。难蚌。按照pog的说法,这场应该不看题直接写代码!!1这样才能写的飞快。摆了一上午。我好像一直在贺题,所以......
  • vue3中的v-model
    V-model具体的变化内容:-组件上单个v-model:属性以及事件的默认名称变了-组件上单个v-model别名:v-bind的.sync修饰符和组件的model......
  • 延时定时器-本地储存-数组的map初了解
    了解windowwindow对象是一个全局对象,也可以说时js中的顶级对象win对象是一个全局对象,也可以说js中的顶级对象,所有通过var定义在全局作用域中的变量,函数,都会变成win......
  • kali linux 拓荒-202203版本
    记录学习过程。如有疑问请在下方留言。感谢!!梅西梅西1、kali是基于debian系统改编2、默认账户/密码:kali/kali3、更新系统:sudoaptupdate&&aptupgrade&&aptdist-......
  • k8s源码分析4-create命令执行流程
    本节重点总结:newCmdCreate调用cobra的Run函数调用RunCreate构建resourceBuilder对象调用visit方法创建资源底层使用resetclient和k8s-api通信架构图create的......
  • 易基因|RNA m7G甲基化测序(m7G-MeRIP-seq)
    N7-甲基鸟苷(N7-methylguanosine,m7G)是真核生物tRNA、rRNA和mRNA5'cap中最丰富的修饰之一。作为一种重要的表观遗传修饰,m7GRNA甲基化在基因表达、加工代谢、蛋白质合成、转......
  • 多项式轨迹--五次多项式轨迹
    在轨迹规划中,规划起始点与终点的加速度连续非常重要,如果在终点或起始点的端点处,加速度有跳变,对于乘坐舒适性有很大影响。采用五次多项式进行轨迹规划时,需要六个条件才能求......
  • avue-data使用记录
    1、大屏需要在bladeX框架里使用,是vue项目,avue-data生成的是html,愣转成vue代码没有成功解决:转为跳转至html文件中,文件存放在public文件夹下.vue文件中直接跳转文件存放......