V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xiaoz
V2EX  ›  PHP

PHP 执行 shell 有哪些潜在风险?

  •  
  •   xiaoz · 2016-10-28 19:14:05 +08:00 · 3812 次点击
    这是一个创建于 2949 天前的主题,其中的信息可能已经有所发展或是发生改变。

    PHP 提供exec()/system()等函数去执行 shell ,那么 PHP 通过这些函数执行 shell 的时候会存在哪些风险,又该如何避免呢?有什么好的解决方案吗?

    34 条回复    2016-10-29 00:53:38 +08:00
    ids
        1
    ids  
       2016-10-28 19:21:05 +08:00 via Android
    不用安全
    EDDYCJY
        2
    EDDYCJY  
       2016-10-28 19:23:02 +08:00 via iPhone
    其他语言也存在的问题
    xiaoz
        3
    xiaoz  
    OP
       2016-10-28 19:27:31 +08:00
    @ids 但是最打算写个 php 程序可能需要执行系统的 shell 命令,因此请教下有没有其它好的解决方案?
    nfroot
        4
    nfroot  
       2016-10-28 19:36:29 +08:00
    和 sql 一样防止注入
    a15819620038
        5
    a15819620038  
       2016-10-28 19:37:52 +08:00 via iPhone
    php 执行 shell 没什么问题。

    不安全的是怕你的 php 脚本有漏洞,被别有用心的人利用了,后果严重程度不一。
    HarveyDent
        6
    HarveyDent  
       2016-10-28 19:40:08 +08:00
    应该有其他方案吧,不建议这么搞
    minbaby
        7
    minbaby  
       2016-10-28 19:46:41 +08:00   ❤️ 1
    执行的命令写死,不接受外界接受动态参数,做好过滤
    invalidtoken
        8
    invalidtoken  
       2016-10-28 19:53:08 +08:00 via iPhone
    外部程序漏洞被利用然后提权?
    Kokororin
        9
    Kokororin  
       2016-10-28 19:57:49 +08:00 via iPhone
    如果参数写死的话没有问题
    slixurd
        10
    slixurd  
       2016-10-28 20:02:21 +08:00   ❤️ 2
    不要用 root 跑 php 。
    kfll
        11
    kfll  
       2016-10-28 20:02:28 +08:00
    能写 extension 就写 extension
    billlee
        12
    billlee  
       2016-10-28 20:06:11 +08:00   ❤️ 1
    主要是要防止
    I. 任意命令执行
    II. 利用被执行的程序进行攻击

    任意命令执行也可以分两种情况
    1. 直接从外界接受命令名,这是一定要避免的
    2. shell 语言注入。理想情况下,如果能直接用 execve 搞定的事情,就不要调用 shell, 这样就不会有注入问题。我写 python 的时候也是这样做的。可 php 这智障没有 execve 接口,只能调用 shell. 一定要调用就做好 escape 啦。

    第 II 点就是说,你调用的外部程序可能设计和编写的时候没考虑那么多安全问题。尤其是 DOS 攻击,面向本地用户的程序是不会考虑这个问题的。
    xiaoz
        13
    xiaoz  
    OP
       2016-10-28 20:15:06 +08:00
    @slixurd 用的 www 跑 PHP ,但是 php 执行 shell 的时候我加了 sudo
    xiaoz
        14
    xiaoz  
    OP
       2016-10-28 20:15:58 +08:00
    @billlee 感谢您的耐心解答,非常有帮助。顺便再请教下 escape 是什么意思?
    xiaoz
        15
    xiaoz  
    OP
       2016-10-28 20:16:29 +08:00
    @minbaby 好的,谢谢。就调用一个 shell 命令,都是写死的。
    billlee
        16
    billlee  
       2016-10-28 20:19:38 +08:00
    @xiaoz 转义,把 | 换成 \|, $ 换成 \$ 等。我记得 php 里面自带了转义函数,但具体效果怎么样不清楚

    另外看到你上面说用 root 执行外部程序,最好不要这样做,很危险。除非你的 php 程序就是用来修改系统配置的(如路由器的 web 配置界面)
    xiaoz
        17
    xiaoz  
    OP
       2016-10-28 20:29:50 +08:00
    @billlee 服务器上会去读一个 json 文件, php 修改这个 json 后我需要服务器去重启某个服务重新载入 json 生效。
    loveyu
        18
    loveyu  
       2016-10-28 21:08:28 +08:00 via Android
    最近要用 root 执行些脚本!然后没辙,只能 php 了
    gouchaoer
        19
    gouchaoer  
       2016-10-28 21:17:57 +08:00 via Android   ❤️ 1
    你需要用 php-cli 去重启服务,不用在 php-fpm 里执行 shell
    gouchaoer
        20
    gouchaoer  
       2016-10-28 21:18:41 +08:00 via Android   ❤️ 1
    在 cron 里面定期调用 php-cli 脚本。。。或者你 php-cli 做成一个 daemon 也行啊
    justyy
        21
    justyy  
       2016-10-28 21:21:19 +08:00   ❤️ 1
    escapeshellarg + esacepshellcmd
    greenskinmonster
        22
    greenskinmonster  
       2016-10-28 21:27:28 +08:00 via Android   ❤️ 1
    用 inotify 监控文件更新,符合条件就重启服务好了。
    xiaoz
        23
    xiaoz  
    OP
       2016-10-28 21:48:19 +08:00
    @gouchaoer 感谢提供的思路,这个方案似乎可行。
    billlee
        24
    billlee  
       2016-10-28 21:50:17 +08:00   ❤️ 1
    @xiaoz 把那个配置文件的 group 改成运行 php 的 group, 权限改成 0664 。然后另外用 inotify 监视文件,重启服务就好了。不需要给 php 用户 sudo 权限。
    xiaoz
        25
    xiaoz  
    OP
       2016-10-28 21:50:36 +08:00
    @gouchaoer 这个方案不错
    XiaoxiaoPu
        26
    XiaoxiaoPu  
       2016-10-28 22:03:55 +08:00
    @xiaoz 另一个思路:写一个 setuid 程序专门去执行重启服务的操作,这样就不需要 sudo 了
    xiaoz
        27
    xiaoz  
    OP
       2016-10-28 22:08:59 +08:00
    @gouchaoer 您看我这样的思路可行吗?
    写一个 1.php 文件去检测 abc.json 文件是否更新,如果有更新的话,就通过 system()函数去重启服务,这个 php 文件不对外访问。然后 crontab 使用 php-cli 每隔 5 分钟执行 1.php
    falcon05
        28
    falcon05  
       2016-10-28 22:12:27 +08:00 via iPhone
    @xiaoz 然后你发现一堆僵尸进程,我试过
    Tink
        29
    Tink  
       2016-10-28 22:14:31 +08:00
    我就在微信公众号上直接用 php 执行 shell 了,没啥问题
    moult
        30
    moult  
       2016-10-28 22:19:46 +08:00
    1 、如果你执行 system(sprint_f("rm -rf %s", $_GET['path'])) 肯定是存在安全隐患的,类似 SQL 注入一样
    2 、如果你执行 system('/sbin/service restart nginx') 那一般不会有安全问题的,但是一般情况下你 PHP 肯定执行在非 root 用户下面吧,所以其实这个根本执行不了,所以你要加上 sudo ,然后把这条命令加到 sudo 的白名单就好了。千万不要把 php 所在的整个用户加到白名单!
    moult
        31
    moult  
       2016-10-28 22:22:37 +08:00
    @moult #30 的 sprint_f 改成 sprintf 。。不小心按到了下划线。
    xiaoz
        32
    xiaoz  
    OP
       2016-10-28 23:35:18 +08:00 via iPhone
    @falcon05 这个思路有什么不妥吗?
    fuxkcsdn
        33
    fuxkcsdn  
       2016-10-29 00:46:16 +08:00 via iPhone
    @xiaoz 按照你这思路的话,根本不需要用到 php , shell+cron 就能搞定了,而且还简单

    以前写过个类似的,不过脚本在老家的移动硬盘上…
    fuxkcsdn
        34
    fuxkcsdn  
       2016-10-29 00:53:38 +08:00 via iPhone
    https://linux.die.net/man/1/inotifywait

    Example 2

    A short shell script to efficiently wait for httpd-related log messages and do something appropriate.
    #!/bin/sh
    while inotifywait -e modify /var/log/messages; do
    if tail -n1 /var/log/messages | grep httpd; then
    kdialog --msgbox "Apache needs love!"
    fi
    done
    参考这例子写一个就 OK 了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3814 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 05:07 · PVG 13:07 · LAX 21:07 · JFK 00:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.