MVC架构网站URL访问原理及伪静态的区分
Posted XINYU2428
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MVC架构网站URL访问原理及伪静态的区分相关的知识,希望对你有一定的参考价值。
0x00 前言
这是一篇简单易理解的文章.
文章目录如下:
0x01 什么是MVC(了解)
0x02 Thinkphp5目录理解(了解)
0x03 ThinkPHP5多个配置文件关系(了解)
0x04 一个URL访问请求的执行流程(重点)
0x05 .htaccess文件的作用(入口文件的隐藏)
0x06 前后端传参(重点)
0x07 伪静态(重点)
为什么这个网站找不到参数在哪里?
为什么这个网站全是html文件? 这TM还测个毛线.
为什么看网站源码的时候找不到想要的代码位置?
...
那么...看完本篇文章你也不一定懂.[手动滑稽]
本篇文章通过Thinkphp5框架作为示例, 请注意文章的重点不是学习代码审计, 而是浅显的理解MVC架构网站URL是怎么完成一次访问请求的, 也是为了最后面讲伪静态链接怎么分辨做铺垫.
0x01 什么是MVC(了解)
MVC是一个编程思想, 是一种设计模式.
M: 模型model, 处理业务数据, 与数据库做交互.
V: 视图view, 显示html页面, 用户能够看到并与之交互的页面.
C: 控制器controller, 接收请求, 调用模型处理数据,调用视图显示页面.
MVC设计模式的网站不能直接请求模型与视图
(介绍来源于网络)
0x02 ThinkPHP5目录理解(了解)
以上为ThinkPHP5开发手册中目录结构
关键目录理解
application 项目开发代码都是写在这里的, 如果做代码审计, 要看的功能代码都在这里
index 模块1(例如这是一个前台展示模块, 默认有一个index目录)
admin 模块2(例如这是一个后台管理模块, 这是一个自己添加的模块)
extend 扩展类库(这里的代码可以被自动加载, 了解即可)
public WEB 部署目录(对外访问目录, 默认情况下入口文件所在目录)
static 静态资源文件目录
index.php 默认存在的index入口文件
admin.php 这是一个自己添加的后台模块入口文件
runtime 缓存目录(提高网站访问速度, 了解即可)
vendor 第三方类库(了解即可)
thinkphp 框架自身的核心代码存放位置(实际开发时, 该目录可能会被重命名)
什么是入口文件?
关于入口文件, 开发手册上介绍如下:
ThinkPHP采用单一入口模式进行项目部署和访问, 无论完成什么功能, 一个应用都有一个统一(但不一定是唯一)的入口. 应该说, 所有应用都是从入口文件开始的, 并且不同应用的入口文件是类似的.
也就是说, 网站的所有请求都需要经过入口文件才能正确访问, 且入口文件可以是多个(例如前台一个后台一个).
http://127.0.0.1/index.php/aaa/bbb/ccc
在上面这个URL链接中, index.php即为入口文件
需要注意的是, 即使是ThinkPHP5框架开发的网站, 入口文件也并不一定存放在public目录下, 它可以通过配置存放在其它的位置, 例如: 网站根目录; 除入口文件与静态资源文件(js/css/image)外, 其他网站文件均不能通过路径的形式直接进行访问
0x03 ThinkPHP5多个配置文件关系(了解)
框架主配置文件 thinkphp/convention.php (对整个应用生效, 但开发时基本不会改动它)
应用公共配置文件 application/config.php, application/database.php (对整个应用生效)
模块配置文件 application/模块目录/config.php (对当前模块生效)
其中database.php文件存放的是数据库的配置信息, 它的存放位置也是可以改变的.
剩余的配置文件看起来很多很乱, 但它们所书写的内容其实是一样的
加载顺序: 框架主配置文件 --> 应用公共配置文件 --> 模块配置文件
它们按照上述顺序加载, 后面的配置文件会覆盖掉前面的(在其作用范围内, 后加载的配置文件生效)
0x04 一个URL访问请求的执行流程(重点)
(图片来自网络)
见上图, 我们最后能访问到的是一个控制器方法, 控制器方法通过与模型交互拿到数据结果, 返回给视图输出响应页面.
这里有一个问题, 怎么通过URL正确的找到对应的控制器方法呢?
关键点在于路由检测与请求分发, 但本篇文章中我们并不以代码的角度去理解它, 所以不需要知道具体是怎么实现.
我们换一个更容易理解的角度, 即实际访问中URL目录与网站真实目录的对应关系
这里我搭建了一个ThinkPHP5网站来具体说明
首先我们访问URL: http://www.y.com/index.php/index/index/index
可以看到, 我们访问到了ThinkPHP5框架欢迎页面.
那么它是如何通过URL找到这个页面的呢?
如下图, 看下URL其与网站真实目录的对应关系
URL通过入口文件底层的一系列处理后, 会自动去application目录中寻找对应的模块/控制器/方法
方法中会执行指定的代码, 返回相应的页面.
下面看下几种常见的报错截图来加深理解
application目录下没有index123目录, 报错: 模块不存在
application/index/controller目录下没有index123.php文件, 报错: 控制器不存在
application/index/controller/index.php文件中没有名为index123()方法, 报错: 方法不存在
在实际测试中, 我们直接访问网站也是能得到一个默认页面的, 那么这个页面是怎么得到的呢?
答案是通过配置文件来配置默认的访问方法
上面三张图的一对比, 结果已经一目了然.
再补充一个知识点, ThinkPHP5默认是使用PATH_INFO方式URL, 也就是路径的形式.
其本身是可以自定义路由的, 也就是说它可以通过代码控制某个URL路径定位到指定的代码块.
但需要通过配置去开启, 相关配置项如下:
混合模式(开启路由, 并使用路由定义+默认PATH_INFO方式的混合)
'url_route_on' => true,
'url_route_must'=> false,
0x05 .htaccess文件的作用(入口文件的隐藏)
.htaccess是作为Apache的补充配置文件(当然需要配置一些文件才能生效, 具体可百度)
.htaccess文件需要与入口文件放在同一目录中
在ThinkPHP5.0中, 出于优化的URL访问原则, 还支持通过URL重写隐藏入口文件, 下面以Apache为例说明隐藏应用入口文件index.php的设置.
看下该配置文件后三句, 大概意思就是如果URL可以找的到目录就去访问, 可以找的到文件也去访问, 如果都找不到, 就通过正则在URL中加上index.php
如http://127.0.0.1/index/index/index 变为 http://127.0.0.1/index.php/index/index/index
从而达到了不管写不写入口文件, 都能找的到入口文件的效果.
如下是开发手册中官方给的配置文件内容
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>
它与上图中我配置的内容一个微小的差别, index.php变为了index.php?, 即多加了一个问号.
这是一个小坑, 因为我的环境是由phpstudy搭建的, 正常写法不行, 查资料看到加上一个问号就好了.
PS: 默认情况下, URL是不区分大小写的, 也就是说URL里面的模块/控制器/操作名会自动转换为小写, 控制器在最后调用的时候会转换为驼峰法处理.
(也许通过网站是否识别大小写来判断服务器是windows或是linux并不准确)
0x06 前后端传参(重点)
参数绑定
按名称绑定
http://www.y.com/index.php/index/test/hello/id/123/name/xinyu
http://域名/入口文件/模块/控制器/方法/参数名/参数值/参数名/参数值
打乱参数顺序也是可以的
从上图代码中可以看到, 我并没有使用一些方法来接受参数, 而是直接将需要传递的参数写成了方法形参的形式.
但无论怎么变, 我们都应该知道PHP底层接收参数是用 $_GET/$_POST/$_REQUEST三个全局变量来接收的.
框架对这些底层的东西进行了封装, 从而帮助开发者更好更快的写代码.
既然效果是一样的, 我们不妨将URL变形成GET传参的形式再访问一下看看
http://www.y.com/index.php/index/test/hello?id=123&name=xinyu
可以看出, 实现的效果是一样的, 但又有些许的不同, 见下图
两种形式都可以将参数传递到后台, 但是它们各自所走的底层代码是不一样的(个人猜测, 具体也不懂)
举上面这个例子主要是想说明, 如果一个目录传参的URL可以转换成GET传参的形式, 可以说明它是一个类似伪静态的形式, 而GET传参的形式可能比目录传参少过滤一些特殊字符或者格式上的限制.
如果我们少给了一个参数了呢?
可以看到页面报错了.
当然这个问题是可以避免的, 那就是给操作方法的参数定义默认值.
这就是为什么有的地方把参数都删除掉也会正常回显的原因.
按顺序绑定
config.php配置文件中有一项内容乳如下
// URL参数方式 0 按名称成对解析 1 按顺序解析
'url_param_type' => 1,
将其改成按照顺序解析
一样的URL, 可以看到按照顺序接收参数时, id=id, name=123, URL后面再写任何内容已经没有意义了.
按照顺序接收参数时不能转换成GET传参的形式.
ThinkPHP5框架中, 默认使用PATH_INFO方式的url进行访问, 也就是我们上面举例的通过目录形式访问网站.
下面我们来对比下普通的GET传参形式的URL访问链接.
http://127.0.0.1/index.php?kzq=test&ff=hello&id=10&name=nihao
URL简单分析:
首先找到了入口文件, 接着因为没有给模块名, 就去配置文件中读取默认的模块名, 然后通过 kzq与ff 两个参数得到到了控制器名与方法名, 最后接收了id与name两个参数的值.
很明显, 它比 http://127.0.0.1/index.php/index/test/hello/id/123/name/xinyu 更好理解.
但它并不合适做出伪静态的效果.
在实际测试中, 传入后台的参数我们可以将其分为两类, 第一类参数是用于代入数据库中作相关的增删查改操作或交互显示, 可以通过控制它们来实现SQL注入等漏洞的利用; 第二类参数是用于代码中的逻辑判断, 可以通过控制它们来执行指定的代码块, 例如ThinkPHP框架的RCE漏洞.
关于这一点, 我们看下开发手册
标箭头的地方是不是很熟悉呢? 没错, 它就是ThinkPHP RCE漏洞的利用POC的形式
s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
s=模块/控制器/方法&参数1=xxx&参数2=xxx&参数3=xxx
(至于为什么可以定位到这里执行代码, 就需要大家去分析这个漏洞了, 我也没具体分析过)
我们尝试将参数1转换成目录的形式
s=index/think\app/invokefunction/function/call_user_func_array&vars[0]=phpinfo&vars[1][]=1
可以看到依然可以执行
我们尝试将参数2转也换成目录的形式
可以看到报错了, 利用失败, 因为 vars[0] 这个参数名其实是一个数组的形式, 这也验证了我前面弹窗那里的猜想.
0x07 伪静态(重点)
如下为配置文件中相关配置项, 以 | 隔开可以设置多个伪静态后缀
伪静态后缀在URL访问时可有可无
// URL伪静态后缀
'url_html_suffix' => 'html|shtml',
下面我们来看一个经过伪静态处理的完整的URL
http://127.0.0.1/index.php/index/index/index.html
http://127.0.0.1/入口文件/模块/控制器/方法/伪静态后缀
因为有入口文件的存在, 上面的URL看起来并不像是一个静态的页面链接, 我们隐藏入口文件.
http://127.0.0.1/index/index/index.html
http://127.0.0.1/模块/控制器/方法/伪静态后缀
按照名称传入参数
http://127.0.0.1/index/index/index/id/10/name/210.html
http://127.0.0.1/模块/控制器/方法/参数1/值1/参数2/值1/伪静态后缀
按照顺序传入参数
http://127.0.0.1/index/index/index/10/210.html
http://127.0.0.1/模块/控制器/方法/值1/值2/伪静态后缀
去掉方法名, 走配置文件中的默认方法名
http://127.0.0.1/index/index/10/210.html
http://127.0.0.1/模块/控制器/值1/值2/伪静态后缀
再去掉控制器名, 走配置文件中的默认控制器名
http://127.0.0.1/index/10/210.html
http://127.0.0.1/模块/值1/值2/伪静态后缀
再去掉模块名, 走配置文件中的默认模块名
http://127.0.0.1/10/210.html
http://127.0.0.1/值1/值2/伪静态后缀
到这里这个URL看起来就像是 命名为10的目录下有一个 210.html文件.
下面给大家分享几种我实际中遇到的传递参数的变形URL
01
http://xxx.com/shunxin/th-10-1002077A
th/10/1002077A 是三个参数, 以 "-" 隔开, 第一个参数是用来做逻辑判断的, 第二个参数不清楚, 第三个参数位置存在SQL注入
02
http://xxx.com/xxx/index-view-id-153.html
http://xxx.com/admin.php/xxx/index/view/id/1691.html
这两个链接一个是用 "-" 号隔开, 一个使用 "/" 隔开, 但本质是一样的.
它们是按照名称传入参数的, 所以它们都可以转换成GET传参的形式, 转换后唯一的参数是id, 也是一个SQL注入点. 这是一个较为常见的传参写法, 去年找到某家开发公司开发的网站都是以这种形式传参, 并存在注入, 最后换了个CNVD证书.
03
http://xxx.com/s/0_0_0_ceshi_0_0_0_1
这个链接形式是在补天上看见的, 看起来只有 ceshi 这个参数为传入参数, 其他位置不是很清楚, 链接会将传入的参数包裹在一个目录中, 只要这个目录名传入后端, 就可以通过正则匹配把这个参数拿出来用, 参数代入了数据库查询, 输入单引号会报数据库相关错误信息, 但这里做了很多关键字的过滤, 没有成功, 有人测过应该知道我说的是哪个站哈哈.
04
http://xxx.com/index.php
[post]
aaa=111|222|333
aaa看起来为一个参数, 实际上它一次传入了三个值, 以 "|" 隔开.
如果把它当作一个参数, 在333位置后面注入, 是不存在漏洞的.
而在222位置后面注入, 则发现存在漏洞.
观察上面的各种URL我们可以发现, 我们经常遇见的参数传入形式有三种:
1.正常传参(GET形式, POST形式等)
2.通过路径的形式传参
3.通过 "-", "_", "|" 等特殊符号分割来传参, 后端再将其匹配出来.
前面讲了那么多, 到伪静态这里应该就很容易理解了, 以后看到一个URL, 不管它是不是MVC的架构, 我们都能很清楚的理解整个URL每一部分的组成.(指PHP)
结束.
说明: 文章不保证内容完全准确, 文中如有错误还请多多指出, 共同进步.
以上是关于MVC架构网站URL访问原理及伪静态的区分的主要内容,如果未能解决你的问题,请参考以下文章