想自己搭一个类似 codepad 的在线编译器
可以在线编译 php ruby python 等代码块
开始的思路是 在centos 里安装一个PHP环境 php执行shell_exec("docker run -v "$(pwd)":/usr/src/app -w /usr/src/app docker.io/php php index.php ");
能正常执行 可是花了5秒多的时间太不划算了,看来其他在线编译器速度都很快,他们是如何实现的呢?有没有大神具体分享下代码或者思路
想自己搭一个类似 codepad 的在线编译器
可以在线编译 php ruby python 等代码块
开始的思路是 在centos 里安装一个PHP环境 php执行shell_exec("docker run -v "$(pwd)":/usr/src/app -w /usr/src/app docker.io/php php index.php ");
能正常执行 可是花了5秒多的时间太不划算了,看来其他在线编译器速度都很快,他们是如何实现的呢?有没有大神具体分享下代码或者思路
1 回答4.1k 阅读✓ 已解决
3 回答1.9k 阅读✓ 已解决
2 回答2.3k 阅读✓ 已解决
2 回答2.5k 阅读✓ 已解决
2 回答848 阅读✓ 已解决
2 回答813 阅读✓ 已解决
1 回答1.4k 阅读✓ 已解决
关联项目
在线编译器后端接口:https://github.com/noxue/noxu...
在线编译器前端界面:https://github.com/noxue/noxu...
在线编译器原理
最基本的原理,利用docker运行编译执行命令
从这样一条命令开始
我们假设你已经安装好了docker
那么我们接下来安装ubuntu镜像
docker pull ubuntu
然后我们就相当于拥有一台 ubuntu 系统的电脑了,
在其中做的人和操作都不会影响到主机,哪怕是格式化系统
如何在docker中执行命令
docker run --rm ubuntu ls
启动了一个ubuntu系统,在其中执行了ls
命令,--rm
参数会在执行完毕之后删除容器,因为我们需要的就是一次性的rm /* -rf
命令的代码面临的问题
如何把代码放到容器中?
不安全的方案一
我们知道命令是可以通过
&&
符号连起来的,这样就可以执行任意多个命令了,于是我们可以构造如下命令:拼接好的完整命令如下:
存在的问题
echo
命令不支持多行,可以用 \n 转义,那么就会遇到第二个问题如果代码中包含这样的字符串
printf("\n")
,应该如何处理呢?\n
写入文件的时候会被当做换行符,可能会考虑反斜杠转义,那就会变成这样printf("\\n")
,最终写入到文件的是printf("\换行")
,\\n
的\n
被当做换行单独留下一个反斜杠,和我们要写入的内容不一样,编译报错。更重要的问题是,代码部分拼接的命令是在我们主机执行的,是完全可以被用户构造来入侵我们主机,这类似sql注入漏洞,举个例子。
代码中 ls是用户提交的代码,如果用户提交的代码是
1 && rm /* -rf &&echo 1
,那么最终的命令就是(千万不要复制到自己电脑尝试,会删除系统文件):可以看到 这是一条合法的语句,而且是完全受用户控制的,这非常危险,不管你做任何限制,都存在被入侵的可能性,只要有可能被入侵,就一定能被入侵。
安全的解决方案
cat
可以满足我们的要求cat 如下使用方式,可以原样输入到文件(注:EOF可以是其他字符串,不一定要EOF,只要首尾同样的字符串即可)
运行后1.c文件中内容是:
很遗憾,上面的命令依然有漏洞,假设用户提交如下代码(注意echo后面有个空格):
那就会形成如下的命令:
上面的命令非常合理,代码中第一个EOF和上面的EOF成对,后面的 cat<<EOF和后面的EOF成对,语法没错,正常运行了中间的rm语句,而这个语句是用户代码中可以任意修改的,一样危险。
上面这个bug出现的原因在于 EOF 是固定的,用户可以构造,如果说我用随机字符串来代替掉EOF,不就可以让用户无法构造了吗?是的可以解决。
$
开头的会被当做变量,比如echo $username
就会输出当前登录的用户名,那不完犊子了,php变量都是这样命名的解决方案(在EOF前面加个
\
,$username 这样的变量就会原样输入到文件了):终极解决方案
我在想有没有可能 cat 这种方式拼接命令依然有可能被人钻空子,我觉得还是有可能的,只是我不知道而已。如果用户传入的数据全部都是在容器中执行的,那就十分安全了。
所以有了这个方案
执行这个命令,实际上是在容器中启动了一个命令行,我们输入任意命令都是在容器中执行,而我们这个命令没有任何部分是用户可以控制的,所以绝对安全。
我们能把内容输入到容器中让 /bin/bash 接收到,主要就在于参数
-i
, 执行docker run --help
就清楚了,他是打开容器的标准输入,相当于主机的stdin(标准输入文件)的内容直接转到容器的stdin中,效果就是执行这个命令之后,我们输入任意内容,他都等于是输入到容器中。所以我们最终的命令就会变成这样
上面只是以python的运行作为例子,实际上我们 EOF 也是替换成 uuid字符串了,因为用户代码也很可能有单独一行的 EOF 出现。
至今为止,所有杜绝了一切可能在主机上执行命令的可能性。
总结
资源限制