Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/security/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 在脚本路径前面加上`/usr/bin/env-i`是否会确保system()和popen()的使用安全?_C_Security_Posix_Popen - Fatal编程技术网

C 在脚本路径前面加上`/usr/bin/env-i`是否会确保system()和popen()的使用安全?

C 在脚本路径前面加上`/usr/bin/env-i`是否会确保system()和popen()的使用安全?,c,security,posix,popen,C,Security,Posix,Popen,我现在正在做一个代码审查,我被这个家伙编写的代码量吓坏了,他只是为了执行一个脚本(使用硬编码路径,没有输入参数)并从中读取输出。(顺便说一句,他有很多错误。) 我以前遇到过类似的问题,有人建议我手动执行pipe/fork/exec“更安全”。我意识到两个潜在问题: 当system()和popen()执行shell命令时,可能会将潜在有害的环境变量值滑入以这种方式执行的程序 另一个问题是,当命令是由用户输入构造的时候。我可以想象潜艇会做各种有害的事情等等 我想知道在这种情况下,建议使用popen(

我现在正在做一个代码审查,我被这个家伙编写的代码量吓坏了,他只是为了执行一个脚本(使用硬编码路径,没有输入参数)并从中读取输出。(顺便说一句,他有很多错误。)

我以前遇到过类似的问题,有人建议我手动执行pipe/fork/exec“更安全”。我意识到两个潜在问题:

  • system()
    popen()
    执行shell命令时,可能会将潜在有害的环境变量值滑入以这种方式执行的程序
  • 另一个问题是,当命令是由用户输入构造的时候。我可以想象潜艇会做各种有害的事情等等
  • 我想知道在这种情况下,建议使用
    popen()
    是否合适。这将大大简化代码。第二点不是问题,因为没有用户输入。通过在执行脚本之前使用
    env-i
    清理环境,应消除第一个问题:

    FILE *fp = popen("/usr/bin/env -i /path/to/some/fancy/script.sh", "r");
    /* ... */
    

    我是否还遗漏了任何其他潜在问题,或者“手动”执行脚本是否仍然值得付出努力?

    从技术上讲,这不是您对如何安全调用
    popen()
    的问题的回答,而是对您应该问到的问题的回答:“如何制作更好的
    popen()

    函数
    child\u spawn(argv、env、flags)
    将设置与子进程通信的管道,并生成子进程。它将返回一个
    struct child
    ,它保存用于通信的子pid和文件描述符

    argv
    是命令和参数的
    NULL
    终止字符串数组,而
    env
    是环境变量的
    NULL
    终止字符串数组。如果
    env
    为空,则子级将从父级继承环境

    所以
    argv
    应该有

    const char* argv[] = {"/bin/ls", "-l", NULL};
    
    const char **env = NULL;
    
    or
    
    const char *env[] = 
       {
          "PATH=/bin:/usr/bin",
          "HOME=/tmp",
          "SHELL=/bin/sh",
          NULL
       };
    
    env
    应具有以下格式

    const char* argv[] = {"/bin/ls", "-l", NULL};
    
    const char **env = NULL;
    
    or
    
    const char *env[] = 
       {
          "PATH=/bin:/usr/bin",
          "HOME=/tmp",
          "SHELL=/bin/sh",
          NULL
       };
    
    完成子进程后,
    child\u wait()
    将关闭与子进程关联的文件描述符,并等待它退出

    要使用
    child\u spawn()
    替代
    popen()
    ,您可以这样称呼它:

    struct child c = child_spawn(argv, NULL, CHILD_PIPE_STDOUT);
    
    您现在可以从
    c->fd_out
    中读取以获取child标准输出的内容

    常量
    CHILD\u PIPE\u STDIN
    CHILD\u PIPE\u STDOUT
    CHILD\u PIPE\u STDERR
    可以“或”-组合在一起,以便在
    c->fd\u in
    c->fd\u out
    c->fd err中具有有效的文件描述符

    请注意,如果您使用
    child_PIPE_STDIN | child_PIPE_STDOUT
    生成子对象,则在读写时存在死锁风险,除非您执行非阻塞io

    函数
    my\u system()
    是一个关于如何使用
    child\u spawn()实现更安全的
    system()
    的示例

    /*
    我们必须定义GNU源才能访问“char**environ”`
    */
    #定义GNU源
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    结构子对象
    {
    pid_t pid;
    int fd_in;
    int fd_out;
    int fd_err;
    };
    静态空隙
    如果有效,则关闭(int fd)
    {
    如果(fd!=-1)关闭(fd);
    }
    /* 
    关闭子通信的所有文件描述符
    并等待子进程退出
    从waitpid()返回状态值。
    有关如何解释该值,请参见“man waitpid”
    */
    int child_wait(结构child*c)
    {
    如果有效,则关闭(c->fd\U in);
    如果有效,则关闭(c->fd\U out);
    如果有效,则关闭(c->fd\U err);
    智力状态;
    pid\u t p=waitpid(c->pid,&status,0);
    如果(p==0)
    错误(1,errno,“waitpid()失败”);
    返回状态;
    }
    int
    dup_如果有效(int fd1,int fd2)
    {
    如果(fd1!=-1&&fd1!=fd2)
    返回dup2(fd1,fd2);
    返回fd2;
    }
    皮杜
    子项生成fd(常量字符*常量argv[],常量字符*常量环境[],
    整数输入,整数输出,整数错误)
    {
    fflush(stdout);
    pid_t p=fork();
    如果(p)
    返回p;
    /***********************
    我们现在处于儿童时期
    ***********************/
    /* 
    将文件描述符设置为预期值,
    -1表示从父代继承
    */
    if(dup_if_有效(in,0)=-1)
    后进先出;
    if(dup_if_有效(out,1)=-1)
    后进先出;
    如果(dup_如果_有效(错误,2)=-1)
    后进先出;
    /*
    关闭所有不需要的文件描述符
    这将释放资源并保持文件和套接字属于
    父项打开的时间比需要的时间长
    在*BSD上,我们可以称之为“closefrom(3)”,但这可能不存在
    所以我们循环所有可能的文件描述符编号。
    更好的解决方案是查看`/proc/self/fs`
    */
    int max\u fd=sysconf(\u SC\u OPEN\u max);
    
    对于(int-fd=3;fd-Big-topic)。您的建议在某种程度上会有所帮助,但您需要提供一些环境变量(如路径),因此您突然陷入了困境。
    system()
    popen()
    应该被烧掉,作为对过去编程罪恶之神的祭品。试图让它们安全是很困难的。不管你怎么努力,你都会失败。创建自己的安全版本的
    popen()要容易得多
    从头开始在管道/fork/exec.Oh上,当你走fork/exec路线时,千万不要把你的代码和fork/exec混在一起。总是用通用的实用程序函数来包装它们。
    env-i/path/to/script.sh
    /path/to/script.sh更安全。但是这家伙写的代码可能更安全。还有“安全程度如何?”取决于脚本的功能及其所需的环境。例如,如果脚本没有收到来自命令行的输入,但确实收到来自计算机上某个文件的用户输入,或者如果脚本不可信,则您仍有顾虑。