You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Boost C++优化主机名/IP输入校验防范命令注入的技术问询

优化基于Boost.Asio的主机/IP输入校验(防命令注入)

问题场景

在Linux环境下调用ping工具(无需root权限)时,需要将主机名、IPv4或IPv6地址作为输入传递给它,但必须先校验输入合法性,防止命令注入。比如传入恶意内容google.com;cat > pwned.txt时,会被shell解析为ping -c 4 google.com;cat > pwned.txt,执行额外的恶意命令。

现有代码通过分别尝试解析IPv4、IPv6,再用字符白名单校验主机名的方式实现,但代码冗余且主机名校验不够严谨,希望找到更优的Boost C++校验方案。

现有代码的不足

  1. IP校验逻辑重复:分别尝试IPv4和IPv6解析,代码结构冗余
  2. 主机名校验不严谨:仅用简单字符白名单,未遵循RFC规范的主机名规则(如长度限制、标签不能以连字符/点开头结尾等)
  3. 依然存在命令注入风险:使用std::system()执行命令,即使校验通过,若存在规则遗漏,仍可能被shell解析执行恶意代码

优化方案

1. 简化IP地址校验

利用boost::asio::ip::address::from_string()自动识别IPv4/IPv6的特性,无需分开尝试,一次调用即可判断是否为合法IP地址。

2. 严谨的主机名校验

遵循RFC 1123规范,使用Boost.Regex实现符合标准的主机名校验:

  • 总长度1-253字符
  • 每个点分隔的标签长度1-63字符
  • 标签仅包含字母、数字、连字符,且不能以连字符开头或结尾

3. 彻底避免命令注入:替换std::system()为execvp()

std::system()会通过shell解析命令字符串,存在注入风险。改用execvp()直接传递参数数组,跳过shell解析,从根源杜绝命令注入。

优化后的代码

#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/wait.h>
#include <boost/asio.hpp>
#include <boost/regex.hpp>

// 校验是否为合法IP地址(IPv4/IPv6通用)
bool is_valid_ip(const std::string& ip) {
    try {
        boost::asio::ip::address addr = boost::asio::ip::address::from_string(ip);
        return true;
    } catch (const std::exception&) {
        return false;
    }
}

// 校验主机名是否符合RFC 1123规范
bool is_valid_hostname(const std::string& hostname) {
    if (hostname.empty() || hostname.size() > 253) {
        return false;
    }
    // RFC 1123主机名正则规则
    const boost::regex hostname_regex(
        R"(^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)*([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])$)"
    );
    return boost::regex_match(hostname, hostname_regex);
}

// 统一校验目标是否为合法IP或主机名
bool is_valid_target(const std::string& target) {
    return is_valid_ip(target) || is_valid_hostname(target);
}

int main(int argc, char* argv[]) {
    // 测试用例:可替换为合法/非法输入
    std::string target = "google.com;cat > pwned.txt";
    // std::string target = "google.com";
    // std::string target = "192.168.1.1";
    // std::string target = "2001:db8::1";

    if (!is_valid_target(target)) {
        std::cerr << "错误:输入不是合法的IP地址或主机名" << std::endl;
        return 1;
    }

    // 使用execvp执行ping,避免shell解析,彻底防止命令注入
    std::vector<char*> cmd_args;
    cmd_args.push_back(const_cast<char*>("ping"));
    cmd_args.push_back(const_cast<char*>("-c"));
    cmd_args.push_back(const_cast<char*>("4"));
    cmd_args.push_back(const_cast<char*>(target.c_str()));
    cmd_args.push_back(nullptr); // 参数列表以nullptr结尾

    std::cout << "执行命令:ping -c 4 " << target << std::endl;

    pid_t pid = fork();
    if (pid == 0) {
        // 子进程执行ping命令
        execvp(cmd_args[0], cmd_args.data());
        // 若execvp返回,说明执行失败
        std::cerr << "ping命令执行失败" << std::endl;
        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        // 父进程等待子进程结束,获取执行状态
        int status;
        waitpid(pid, &status, 0);
        if (WIFEXITED(status)) {
            int exit_code = WEXITSTATUS(status);
            if (exit_code != 0) {
                std::cerr << "ping命令退出码:" << exit_code << std::endl;
            }
        } else {
            std::cerr << "ping命令异常终止" << std::endl;
        }
    } else {
        std::cerr << "创建子进程失败" << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

关键优化说明

  • 模块化校验函数:将IP和主机名校验拆分为独立函数,代码更清晰易维护,避免冗余
  • 标准合规的主机名校验:正则表达式严格遵循RFC 1123,比简单白名单更可靠
  • 无shell执行:通过fork()+execvp()直接执行ping,参数以数组形式传递,完全避免shell解析,彻底解决命令注入风险

内容的提问来源于stack exchange,提问作者vengy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 04:45:04