万能脚本助Web执行底层Linux命令
需求分析:
这里先要说明的是,这一篇不是QT系列的文章,而是关于Web的,之所以要写这篇,是因为以前做Web相关开发的时候,经常涉及到与linux底层命令打交道,比如说创建一个目录,删除一个目录,或者是执行一个自定义的脚本。关于PHP如何调用、执行Linux的底层命令,以前也研究过,基本上实现了自己需要的功能,但是有些地方一直没有弄明白。今天又偶然碰到了,趁着这个机会向大家描述一下一步一步应该如何实现,并最后附上相关C代码。
原理实现:
首先,一般搭建的Web站点都是采用Apache或Nginx,因此当使用php执行linux的命令的时候,在linux端体现出的时Apache或Nginx用户的身份来执行的。通常情况下基于安全考虑,Apache或Nginx在linux端的默认用户是不具有很高的权限的,比如删除、创建等,因此我们必须通过一种方式,为其赋予一定的权限。在我以前写过的文章中,我采用了一种方法,即将Apache或Nginx的默认用户修改了,给那个用户赋予了很高的权限,虽然达到了我的目的,但是恰恰造成了最大的隐患,即:web服务器默认用户权限设置过大,很容易受到来自外界的攻击,甚至不用外界,我自己在php端执行一个命令能将我整个站点删除掉,而在linux端对此基本没有任何防御能力。基于此,我们提出一种方式,在linux端不修改web服务器默认用户的权限,而是当执行命令时由我们来自己控制这条命令应当具有何种用户的执行权限,或者root用户,或者普通用户完全由我们传递的参数决定。
其次,基于上面的阐述,基本实现思路为:接受php传递来的username和passwd的参数,在linux端新建一个进程,然后将该进程模拟为一个用户的身份,即设置该进程的用户实际、有效的用户ID和用户组ID,然后再执行某条命令,此时执行该命令的过程就好像username用户本身执行那条命令一样。username能执行的有效命令只能在自己的职权范围之内,比如:如果username是普通用户,则它无法通过执行命令删除其他用户或root用户的文件。这样,在一定程度上实现了安全的可控性。
最后,根据上面提出的改变某个进程的实际、有效用户ID和用户组ID需要用到setuid()和setgid(),而关于这两个函数这里还有需要注意的地方,如果执行它的是特权用户,即root,则它可以任意设置自己的uid和gid,即可以模拟任意用户;而如果执行它的时普通用户,则它只能设置自己的uid和gid,而不能设置为其它用户的,即无法模拟其它用户。为了解决这个问题就要用到linux的一些特性,即设置文件的用户标记位:s,使文件在执行时具有文件所有者的权限。这样,Apache或Nginx默认用户执行这个文件的时候就类似于该文件的所有者在执行它,而我们利用root用户创建该文件,就相当于root用户在执行该文件,此时setuid和setgid就可以将进程模拟为任何用户的身份了。
实现代码:
- #include <stdio.h>
 - #include <string.h>
 - #include <stdlib.h>
 - #include <unistd.h>
 - #include <sys/types.h>
 - #include <sys/wait.h>
 - #include <pwd.h>
 - #include <math.h>
 - #include <time.h>
 - //Usage: exefile command work_directory username password
 - int main(int argc, char *argv[])
 - {
 - char *username = NULL;
 - char *password = NULL;
 - char *command = NULL;
 - char *workdir = NULL;
 - struct passwd *stpasswd = NULL;
 - if(argc == 5)
 - {
 - command = argv[1];
 - workdir = argv[2]; //work directory
 - username = argv[3];
 - password = argv[4];
 - }
 - else
 - {
 - return 1;
 - }
 - //printf("username = %s\n",username);
 - //printf("password = %s\n",password);
 - //printf("workdir = %s\n",workdir);
 - //printf("command = %s\n",command);
 - int result = 0;//auth(username,password); //Ensure the user is a legel user in the system
 - if(result == 0) //auth successfully
 - {
 - int kidstatus, deadpid;
 - //! fork()克隆出一个完全相同的进程出来;它会复制当前进程的所有变量,就好像一个进程克隆出来另一个一样
 - //! fork()函数的返回值有三种情况:=0代表克隆出来的那个进程 >0代表“父”进程 <0 执行出错
 - //! 这种情况一般就是fork出来新的进程,【可选:进行身份切换】,然后利用它来执行execlp(),执行相关的命令
 - pid_t kidpid = fork();
 - if (kidpid == -1)
 - {
 - printf("fork error");
 - return 1;
 - }
 - if (kidpid == 0)
 - {
 - //! getpwnam():获取用户登录相关的信息
 - //! 头文件:#include <pwd.h> #include <sys/types.h>
 - stpasswd = getpwnam(username);
 - //! setgid():设置实际用户组ID和有效用户组ID
 - //! setuid():设置实际用户ID和有效用户ID
 - //! chdir() : 修改当前目录
 - //! 注意:如果是一个非特权用户,则它执行setgid和setuid只能设置为自己的gid和uid,而不能设置其它任何数值
 - //! 如果是一个特权用户(即:拥有root权限),则可以利用setgid和setuid设置为任意数值,这也就是为什么最终
 - //! 编译出来的文件要通过:chmod u+s 设置特权位
 - setgid( (int)(stpasswd->pw_gid)); //Set current usergroup
 - setuid( (int)(stpasswd->pw_uid)); //Set current user
 - chdir(workdir); //change work directory
 - //! int execlp(const char *file, const char *arg, ..., (char *)0);
 - //! execlp()会从PATH环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后
 - //! 将第二个参数以后的参数当作该文件的argv[0], argv[1] ...,最后一个参数必须用空指针(NULL)结束
 - //! 下面的命令就是:/bin/bash -c command执行
 - int rv = execlp("/bin/bash", "/bin/bash", "-c", command, NULL);
 - fflush(stdout);
 - return rv;
 - }
 - //! we only get here if we're the parent process.
 - //! waitpid()阻塞等待子进程结束
 - //! 函数原型:
 - deadpid = waitpid(kidpid, &kidstatus, 0);
 - if (deadpid == -1)
 - {
 - printf("error to fork a process!");
 - return 1;
 - }
 - else
 - {
 - return 0;
 - }
 - }
 - else
 - {
 - printf("Authenticate failed\n");
 - return 1;
 - }
 - }
 
最后,利用gcc编译该文件:gcc test_exec.c -o test_exec;编译完成后为它赋予权限:chmod 777 test_exec;然后设置用户标记位:s, chmod u+s test_exec
执行测试:
1. 利用root用户创建一个del_test文件,然后切换到普通用户,测试能否删除;然后用我们的命令模拟root用户看能否删除
2. 利用root用户创建一个del_test的文件,切换到zhangsan用户,利用命令模拟lisi用户,看能够删除
通过以上,我们可以看出,利用该命令我们可以模拟任何用户的身份,相当于任何权限都是由你自己控制,在一定程度上保证了安全性。
总结:
对似懂非懂的知识点一定要明白其原理,不然始终不知道应该如何来做。加油!这段时间的网盘开发没有涉及太多挑战性的东西,期待UDT的研究。
参考:http://blog.csdn.net/houqd2012/article/details/34165381
万能脚本助Web执行底层Linux命令的更多相关文章
- 【夸QT十一】外来物品:通用脚本帮助Web运行基础Linux命令
		
需求分析: 需要注意的是在这里第一次,这个人是不是QT系列文章,它是关于Web的,之所以写这篇文章.这是因为碍着Web相关开发时间,而且往往涉及linux与底层指令处理.例如,创建一个文件夹,删除一个 ...
 - 后台执行UNIX/Linux命令和脚本的五种方法
		
hiveserver 后台启动 nohup "${HIVE_HOME}"/bin/hive --service hiveserver2 & 1. 使用&符号在后台执 ...
 - Linux centosVMware  shell脚本介绍、shell脚本结构和执行、date命令用法、shell脚本中的变量
		
一. shell脚本介绍 shell是一种脚本语言 aming_linux blog.lishiming.net 可以使用逻辑判断.循环等语法 可以自定义函数 shell是系统命令的集合 shell脚 ...
 - python模块之 - subprocess执行unix/linux命令
		
subprocess模块提供了一种一致的方法来创建和处理附加进程,与标准库中的其它模块相比,提供了一个更高级的接口,subprocess模块用来生成子进程,并可以通过管道连接它们的输入/输出/错误,以 ...
 - 批量执行(Linux命令,上传/下载文件)
		
前言: 每个公司的网络环境大都划分 办公网络.线上网络,之所以划分的主要原因是为了保证线上操作安全: 对于外部用户而言也只能访问线上网络的特定开放端口,那么是什么控制了用户访问线上网络的呢? 防火墙过 ...
 - shell脚本介绍、shell脚本结构和执行、date命令用法、shell脚本中的变量
		
7月11日任务 20.1 shell脚本介绍20.2 shell脚本结构和执行20.3 date命令用法20.4 shell脚本中的变量 20.1 shell脚本介绍 1.shell脚本语言是linu ...
 - 吻逗死(windows)系统下自动部署脚本(for java spring*)及linux命令行工具
		
转载请注明出处:https://www.cnblogs.com/funnyzpc/p/10051647.html (^^)(^^)自動部署腳本原本在上個公司就在使用,由於近期同事需要手動部署一個Spr ...
 - jenkins 执行远程linux命令
		
在Jenkins中进行构建时,可能需要首先SSH登录到一个远程服务器以执行必要的脚本,然后再执行构建.这时,需要安装SSH Plugin,并进行如下配置.1.在Jenkins界面,系统管理->管 ...
 - 一个shell脚本,让你的linux命令行酷炫起来
		
可调用如下函数达到echo出来带颜色的文字.._echo_error() { echo -ne "\033[31;1m $1\033[0m\n";}_echo_ok() { ech ...
 
随机推荐
- ionic+cordova+angularJs监听刷新
			
普通的js返回并刷新这里就不多说了,百度就有很多方法. 下面说的是使用了angularjs.ionic开发的一个手机app中我使用的返回上一页并刷新的方法. 场景:回复的页面是单独的,点击保存回复后会 ...
 - kettle Java Filter(表达式过滤)
 - Android更改桌面应用程序launcher的两种方式
			
http://blog.csdn.net/mdx20072419/article/details/9632779/ launcher,也就是android的桌面应用程序.下图是我正在使用的魅族手机的l ...
 - 解决VS报表.rdl 显示乱码“小方块”问题
			
报表在编辑状态显示文本显示小方块 如图 原因:字体格式是英文状态下. 解决:选中文本框,选择文本框属性,选择字体,字体改成宋体或微软雅黑.就可以了.
 - IOS开发之NSObject协议类方法说明
			
oc中NSObject类是所有类的基类,所有类都要继承自它,那么它的方法就显得特别重要,因为所有类都会有这些基本的方法. 看看oc的源码中NSObject是这样定义的: @interface NSOb ...
 - mybatis关联查询
			
场景:一个人有多个手机号,一个手机号对应一个人 CREATE TABLE test.mobile ( mid INT NOT NULL auto_increment, tel ), pid INT, ...
 - JavaScript基础-面向对象编程<1>
			
1.1 函数与对象 1.定义函数的方式定义类 定义类的方法: function class1(){ //类成员的定义及构造函数部分 } class1既是一个函数,也是一个类. 使用 new 操作符获 ...
 - 【HeadFirst设计模式】10.状态模式
			
定义: 允许对象在内部状态改变时改变它 行为,对象看起来好像修改了它的类. OO原则: 封装变化 多用组合,少用继承 针对接口编程,不针对实现编程 为交互对象之间的松耦合设计而努力 类应该对扩展开放, ...
 - start-stop-daemon 命令
			
Usage: start-stop-daemon [<option> ...] <command> Commands: -S|–start — <argument> ...
 - pycharm3.x 注册码
			
PyCharm 3.0 注册码 PyCharm3 序列号 License Key 用户名:yueting3527 注册码: ===== LICENSE BEGIN ===== 93347-120420 ...