Linux下C语言开发的客户端-服务器如何防范路径遍历文件访问?
防止路径遍历攻击的解决方案
这是个典型的路径遍历安全漏洞,必须在服务器端对客户端传入的文件名做严格校验,绝对不能直接信任用户输入。下面给你几个实用的解决思路,从简单到进阶都有:
1. 直接过滤危险路径组件
最基础的做法是检查传入的文件名是否包含../、./或者绝对路径(以/开头),如果有直接拒绝请求。这种方法实现简单,能挡住大部分常见的攻击:
#include <string.h> #include <stdbool.h> bool is_filename_safe(const char *filename) { // 拒绝绝对路径 if (filename[0] == '/') { return false; } // 拒绝包含上级目录或当前目录的路径 if (strstr(filename, "../") != NULL || strstr(filename, "./") != NULL) { return false; } // 额外:拒绝包含路径分隔符的(如果只允许访问当前目录的文件) if (strchr(filename, '/') != NULL) { return false; } return true; }
2. 解析真实路径并校验目录范围
更可靠的方法是把用户输入的文件名拼接成完整路径后,用realpath()解析出它的真实绝对路径,然后检查这个路径是否落在你允许的服务器工作目录之内。这种方法能绕过一些过滤技巧(比如符号链接、特殊编码路径等):
#include <stdlib.h> #include <string.h> #include <limits.h> #include <stdio.h> bool is_path_within_allowed_dir(const char *filename, const char *allowed_dir) { char allowed_abs_path[PATH_MAX]; char requested_abs_path[PATH_MAX]; // 先获取允许目录的绝对路径 if (realpath(allowed_dir, allowed_abs_path) == NULL) { perror("Failed to resolve allowed directory"); return false; } // 拼接用户输入到允许目录下 snprintf(requested_abs_path, sizeof(requested_abs_path), "%s/%s", allowed_abs_path, filename); // 解析请求路径的真实路径(自动处理../、符号链接等) if (realpath(requested_abs_path, requested_abs_path) == NULL) { // 文件不存在或路径无效,直接拒绝 return false; } // 检查真实路径是否以允许目录的绝对路径开头,同时避免前缀误匹配 size_t allowed_len = strlen(allowed_abs_path); if (strncmp(requested_abs_path, allowed_abs_path, allowed_len) != 0 || (requested_abs_path[allowed_len] != '\0' && requested_abs_path[allowed_len] != '/')) { return false; } return true; }
3. 切换工作目录后仅操作文件名
可以让服务器进程先切换到指定的工作目录(比如chdir("/home/server/files")),然后只使用文件名的basename部分来操作文件。这样即使用户传入../try.txt,在当前目录下尝试访问上级目录也会失败(前提是限制服务器进程的上级目录权限):
#include <unistd.h> #include <libgen.h> #include <stdio.h> #include <stdlib.h> // 初始化时切换到工作目录 bool setup_working_dir(const char *work_dir) { if (chdir(work_dir) == -1) { perror("Failed to change to working directory"); return false; } return true; } // 处理客户端请求时,提取安全的纯文件名 char *get_safe_filename(const char *input_filename) { // basename会修改原字符串,所以先复制一份 char *filename_copy = strdup(input_filename); if (filename_copy == NULL) { return NULL; } char *safe_name = basename(filename_copy); // 额外校验,防止极端情况 if (strstr(safe_name, "../") != NULL) { free(filename_copy); return NULL; } // 返回的safe_name指向filename_copy内部,不要单独释放safe_name return filename_copy; }
4. 使用沙箱限制进程(进阶)
如果对安全性要求极高,可以用chroot把服务器进程限制在指定的目录“监狱”里。这样即使存在路径遍历漏洞,进程也无法访问监狱外的任何文件。不过这种方法需要root权限来设置,并且要正确配置环境(比如复制必要的库文件、设备文件到chroot目录),适合生产环境的高安全场景。
关键注意事项
- 永远不要信任客户端的任何输入,所有传入的文件名都必须经过校验
- 最好组合使用多种方法(比如先过滤危险字符,再校验真实路径),提升安全性
- 限制服务器进程的权限:用非root用户运行服务器,并且只给它工作目录的读写权限,即使被攻击也能降低损失
内容的提问来源于stack exchange,提问作者Ennio
相关产品推荐
相关产品推荐

