百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

安全攻防七:CSRF/SSRF,为啥避免了XSS,还是“被发送”了一条微博

zhezhongyun 2025-03-25 19:33 127 浏览

前面我们说了 2 种常见的 Web 攻击:XSS 和 SQL 注入。它们分别篡改了原始的 HTML 和 SQL 逻辑,从而使得黑客能够执行自定义的功能。那么除了对代码逻辑进行篡改,黑客还能通过什么方式发起 Web 攻击呢?

我们还是先来看一个例子。在平常使用浏览器访问各种网页的时候,是否遇到过,自己的银行应用突然发起了一笔转账,又或者,你的微博突然发送了一条内容?

在我们了解了 XSS 之后,你可能会联想到,这是银行或者微博中出现了某个 XSS 漏洞。但问题是,你今天并没有访问过银行或者微博的页面,所以并没有“被 XSS”的机会。这时,你想到,会不会是你今天访问的其他网页里存在一些恶意的攻击,实现了你不知道的转账和发博行为呢?

CSRF攻击如何产生的

当我们在访问一个 Web 页面的时候,并不是我们自己去获取页面信息,而是浏览器去获取了这些信息,并将它们进行了展示。这就说明,你允许浏览器代表你去和 Web 的服务端进行交互。为了能够准确地代表你的身份,浏览器通常会在 Cookie 中存储一些必要的身份信息。所以,在我们使用一个网页的时候,只需要在首次访问的时候登录就可以了。

从用户体验上来说,这当然是非常方便的。但是,黑客正是利用这一点,来编写带有恶意 JavaScript 脚本的网页,通过“钓鱼”的方式诱导你访问。然后,黑客会通过这些 JavaScript 脚本窃取你保存在网页中的身份信息,通过仿冒你,让你的浏览器发起伪造的请求,最终执行黑客定义的操作。而这一切对于你自己而言都是无感知的。这就是 CSRF(Cross-Site Request Forgery,跨站请求伪造)攻击。

接下来,我们就以银行转账为例子,来详细讲解一下这个攻击过程。

当你在银行页面发起一笔转账时,这个过程其实是通过一个转账接口来完成的。这个接口的内容可能包括下面这些内容:

  • 接口地址:http://bank.com/transfer ;
  • HTTP 方法:POST;
  • 接口参数:to(目标账户)、amount(金额)。

在转账之前,你肯定进行了一次登录。这样一来,这个转账接口就可以通过你之前存储在 Cookie 中的相关字段来完成认证了。所以,这个接口参数中不需要包含任何身份认证相关的信息。也正是因为如此,这个接口满足了 CSRF 攻击的基本条件:

  • 使用 Cookie 进行认证;
  • 参数中不包含任何隐私信息。

于是,黑客可以构造一个如下的空白网页。我们假设这个网页的地址为 hacker.com。


  
    
<script> document.forms[0].submit(); </script>

在 HTML 中,<script>标签内的 JavaScript 脚本会在打开网页的时候自动执行。因此,一旦用户访问了这个 hacker.com 的页面,它就会自动提交 form 表单,向http://bank.com/transfer这个接口(假设为转账接口)发起一个 POST 请求。

其中,to 和 amount 这两个参数,代表着用户向黑客的账号转账 10000 元。只要这个用户之前登录过 bank.com,并且账户余额大于 10000 元,那么黑客就能够成功地收到这 10000 元的转账了。在这个网页中,的标签带有“hidden”属性,所以这整个过程对于用户来说都是不可见的。如下图所示:

通过 CSRF 攻击,黑客能做什么

和 XSS 一样,CSRF 也可以仿冒用户去进行一些功能操作的请求,比如修改密码、转账等等,相当于绕过身份认证,进行未授权的操作。

值得一提的是,尽管黑客通过 CSRF 能进行的操作没有 XSS 丰富,但 CSRF 在传播和攻击成本上都低于 XSS。这也就是说,即使你的网页中没有任何注入漏洞,但只要接口配置不当,就能够被 CSRF 利用。而黑客也只需要在自己的域名中,搭建一个诱导性的网页,就可以让任何访问网页的用户都遭受到 CSRF 攻击。而且,用户每天需要访问大量的网页,根本没有办法确认每一个网页的合法性。而从严格意义上来说,用户根本没有办法防止 CSRF 攻击。因此,我们只能从应用本身入手去加强防护。

如何进行 CSRF 防护

那究竟该怎么进行 CSRF 防护呢?我们有两种方法。

  • 行业内标准的 CSRF 防护方法是 CSRFToken

我们知道,CSRF 是通过自动提交表单的形式来发起攻击的。所以,在前面转账的例子中,黑客可以通过抓包分析出 http://bank.com/transfer 这个接口所需要的参数,从而构造对应的 form 表单。因此,我们只需要在这个接口中,加入一个黑客无法猜到的参数,就可以有效防止 CSRF 了。这就是 CSRF Token 的工作原理。它的工作流程如下图所示:

因为 CSRF Token 是每次用户正常访问页面时,服务端随机生成返回给浏览器的。所以,每一次正常的转账接口调用,都会携带不同的 CSRF Token。黑客没有办法进行提前猜测,也就没有办法构造出正确的表单了。

  • 通过二次验证来加强防护

回想一下,当你进行各类支付操作的时候,银行网页通常会要求你输入支付密码。你可能会觉得奇怪,明明自己已经登录了,为什么还需要输入一个独立的支付密码呢?这其实和 CSRF Token 的原理一样:这个独立的支付密码是需要用户输入的,只存在于用户的记忆中,因此,也是黑客无法获取到的参数。

怎么理解呢?假如说,黑客通过 CSRF 攻击,替你发起了一笔转账。在支付的时候,银行会发起一个全新的页面,让你验证支付密码。这个时候你发现,这个支付请求不是你本人发起的,那你肯定不会输入支付密码来完成验证。所以,在用户进行支付这样的敏感操作时,应用通常会要求用户提供一些私密的信息,就是为了对 CSRF 攻击进行防护。

你现在对 CSRF 的攻击和防护,应该有了一个大概的了解。简单来说,CSRF 其实就是黑客利用浏览器存储用户 Cookie 这一特性,来模拟用户发起一次带有认证信息的请求,比如转账、修改密码等。防护 CSRF 的原理也很简单,在这些请求中,加入一些黑客无法得到的参数信息即可,比如 CSRF Token 或者独立的支付密码等。掌握了这些内容,其实 CSRF 的知识基本上就差不多了。

SSRF:同样的原理,发生在服务端又会发生什么

在 CSRF 中,黑客通过诱导用户访问某个网站,让用户的浏览器发起一个伪造的请求。那么,如果服务端发起了这个伪造的请求,又会发生什么呢?

我们知道,服务端也有代理请求的功能:用户在浏览器中输入一个 URL(比如某个图片资源),然后服务端会向这个 URL 发起请求,通过访问其他的服务端资源来完成正常的页面展示。

这个时候,只要黑客在输入中提交一个内网 URL,就能让服务端发起一个黑客定义的内网请求,从而获取到内网数据。这就是 SSRF(Server Side Request Forgery,服务端请求伪造)的原理。而服务端作为内网设备,通常具备很高的权限,所以,这个伪造的请求往往因为能绕过大部分的认证和授权机制,而产生很严重的后果。

比方说,当我们在百度中搜索图片时,会涉及图片的跨域加载保护,百度不会直接在页面中加载图片的源地址,而是将地址通过 GET 参数提交到百度服务器,然后百度服务器请求到对应的图片,再返回到页面展示出来。

这个过程中,百度服务器实际上会向另外一个 URL 地址发起请求(比如,上图中的http://s1.sinaimg.cn)。利用这个代理发起请求的功能,黑客可以通过提交一个内网的地址,实现对内网任意服务的访问。这就是 SSRF 攻击的实现过程,也就是我们常说的“内网穿透”。

通过 SSRF 攻击,黑客能做什么

了解了 SSRF 攻击的过程之后,我们知道,在服务端不做任何保护措施的情况下,黑客可以利用 SSRF 向内网发起任意的 HTTP 请求。那么,这些请求会产生什么样的后果呢?我总结了一下,主要会有这样两种动作:内网探测和文件读取。

  • 1.内网探测

内外网一般是隔离的。所以,黑客在外网环境中,是无法知道内网有哪些服务器,这些服务器又分别提供了哪些服务。但是,通过一个加载图片的 SSRF 漏洞,黑客就能够对内网进行探测。这是怎么做到的呢?别着急,我们慢慢来看。

在前面百度搜图的例子中,我们请求的地址是:
https://image.baidu.com/search/detail?objurl=
http://s1.sinaimg.cn/picture.jpg。因为
http://s1.sinaimg.cn/picture.jpg会正常返回一个图片,所以网页会展示出来对应的图片。

我们假定这样一个服务端逻辑:在这个请求过程中,服务端会判断 objurl 返回数据的 Content Type 是否为 image/jpeg。那么,可能的返回结果就有三种:

  • “是”,则展示图片;
  • “不是”,则返回“格式错误”;
  • 无响应,则返回“找不到图片”。

基于这三种返回逻辑,黑客可以构造一个恶意的请求地址:
https://image.baidu.com/search/detail?objurl=127.0.0.1:3306。如果服务器返回“格式错误”,则代表服务端本地的 3306 端口可用;如果返回“找不到图片”,则代表不可用。我们知道,3306 是 MySQL 对应的端口号,因此,根据这个返回的信息,黑客就能够知道服务端本地是否开启了一个 MySQL 服务。接下来,黑客只需要不断重复这个过程,尝试不同的 IP 和端口号,就能够一点一点探测出整个内网的结构。

  • 2.文件读取

服务器除了对图片的代理不做合法性判断之外,对很多其他的代理也不做判断,而是直接将代理的结果返回到前端。我们称这种情况为“有回显的 SSRF”。在这种情况下,黑客不仅能够知道请求是否成功了,还能够知道具体返回的内容。这时候你肯定会好奇,黑客究竟是怎么做到呢?

在 URI 中,开头的 http:// 和 https:// 代表需要使用什么协议去进行请求。除了 HTTP 之外,URI 还有很多种协议可以选择,比如 file:// 就是直接读取本地的文件。通过输入 file://etc/passwd,黑客就能够通过一个请求获取到本地的 passwd 文件,从而知道本地有哪些用户。经过不断地尝试,黑客就能够把整个服务器中的文件内容都给拉取出来,这其中包括密钥、源码等极度敏感的信息。

一般情况下,黑客会通过 SSRF 攻击拿到了服务端的源码,然后通过对源码的分析,找到了一个 SQL 注入的漏洞,再利用 SSRF 发起对内网的 SQL 注入攻击,从而拿到了内网的命令执行权限。

如何进行 SSRF 防护

因为 SSRF 漏洞起源于业务的正常功能需求(比如百度图片的图片请求等等)。因此,我们很难真正消除它。尽管如此,我还是会为你介绍几种常见的防护手段,来尽可能地提高应用的安全性。这些常见的手段主要包括:白名单限制、协议限制和请求端限制。

  • 白名单的限制

白名单的限制永远是最简单、最高效的防护措施。SSRF 中的白名单,就是对用户提交上来的目标 URL 进行限制。比如,只允许是同一个域名下的 URL。你可以理解为,让百度图片的代理服务只允许代理 baidu.com 的 URL。但是,很多时候,因为业务功能的设计,白名单的限制并不可行。比如,上述百度图片的例子,这个功能的设计思路就是,baidu.com 这个域名下能够请求各类域名下的图片资源(比如上述例子中的 sinaimg.cn)。

  • 协议和资源类型等进行限制

在这种时候,我们可以对协议和资源类型等进行限制。比如:对于使用协议,我们只允许 HTTP 或者 HTTPS 协议;对于返回的内容,我们只允许图片格式的内容。通过这些限制,虽然不能完全阻止黑客发起 SSRF 攻击,但也大大降低了黑客能够造成的危害。

  • 请求端限制

除此之外,因为 SSRF 最终的结果,是接受代理请求的服务端发生数据泄漏。所以,SSRF 防护不仅仅涉及接收 URL 的服务端检测,也需要接受代理请求的服务端进行配合。在这种情况下,我们就需要用到请求端限制,它的防护措施主要包括两个方面。

第一,为其他业务提供的服务接口尽量使用 POST,避免 GET 的使用。因为,在 SSRF 中(以及大部分的 Web 攻击中),发起一个 POST 请求的难度是远远大于 GET 请求的。因为默认的请求方式是 GET,而发起 POST 请求,需要在发起 HTTP 请求的时候进行配置。很多安全漏洞中不包含能够配置协议的地方。在上述百度图片的例子中,黑客显然就只能发起 GET 请求。如果某个敏感服务是 POST 的,黑客就无法请求到相关资源了。

第二,为其他业务提供的服务接口,最好每次都进行验证。通过 SSRF,黑客只能发起请求,并不能获取到服务端存储的验证信息(如认证的 key 和 secret 等)。因此,只要接受代理请求的端对每次请求都进行完整的验证,黑客无法成功通过验证,也就无法完成请求了。

总结

我们介绍了 CSRF 和 SSRF 这两种攻击方式。其中,CSRF 是黑客控制用户的浏览器发起伪造的请求,SSRF 则是黑客控制服务端发起伪造的请求。通过伪造的请求,黑客可以伪造用户或者服务器的身份,越权获取数据或者发起请求。应用中的请求接口越敏感,黑客能够造成的伤害就越大。

除此之外,CSRF 和 SSRF 产生于正常的业务功能逻辑中,因此,我们没有办法从根本上组织黑客发起伪造的请求。但是,你可以通过加强接口的安全验证,来避免伪造请求造成影响。在 CSRF 中,我们可以通过 CSRF Token 或者二次验证等操作来加强防护。这样,黑客无法获取到隐私信息,也就无法发起连续的请求了。在 SSRF 中,我们则需要限制可请求的域名,来限制黑客能够访问到的资源。另外,目标服务端,也需要加强接口的验证,来避免伪造请求成功通过授权。

相关推荐

Python入门学习记录之一:变量_python怎么用变量

写这个,主要是对自己学习python知识的一个总结,也是加深自己的印象。变量(英文:variable),也叫标识符。在python中,变量的命名规则有以下三点:>变量名只能包含字母、数字和下划线...

python变量命名规则——来自小白的总结

python是一个动态编译类编程语言,所以程序在运行前不需要如C语言的先行编译动作,因此也只有在程序运行过程中才能发现程序的问题。基于此,python的变量就有一定的命名规范。python作为当前热门...

Python入门学习教程:第 2 章 变量与数据类型

2.1什么是变量?在编程中,变量就像一个存放数据的容器,它可以存储各种信息,并且这些信息可以被读取和修改。想象一下,变量就如同我们生活中的盒子,你可以把东西放进去,也可以随时拿出来看看,甚至可以换成...

绘制学术论文中的“三线表”具体指导

在科研过程中,大家用到最多的可能就是“三线表”。“三线表”,一般主要由三条横线构成,当然在变量名栏里也可以拆分单元格,出现更多的线。更重要的是,“三线表”也是一种数据记录规范,以“三线表”形式记录的数...

Python基础语法知识--变量和数据类型

学习Python中的变量和数据类型至关重要,因为它们构成了Python编程的基石。以下是帮助您了解Python中的变量和数据类型的分步指南:1.变量:变量在Python中用于存储数据值。它们充...

一文搞懂 Python 中的所有标点符号

反引号`无任何作用。传说Python3中它被移除是因为和单引号字符'太相似。波浪号~(按位取反符号)~被称为取反或补码运算符。它放在我们想要取反的对象前面。如果放在一个整数n...

Python变量类型和运算符_python中变量的含义

别再被小名词坑哭了:Python新手常犯的那些隐蔽错误,我用同事的真实bug拆给你看我记得有一次和同事张姐一起追查一个看似随机崩溃的脚本,最后发现罪魁祸首竟然是她把变量命名成了list。说实话...

从零开始:深入剖析 Spring Boot3 中配置文件的加载顺序

在当今的互联网软件开发领域,SpringBoot无疑是最为热门和广泛应用的框架之一。它以其强大的功能、便捷的开发体验,极大地提升了开发效率,成为众多开发者构建Web应用程序的首选。而在Spr...

Python中下划线 ‘_’ 的用法,你知道几种

Python中下划线()是一个有特殊含义和用途的符号,它可以用来表示以下几种情况:1在解释器中,下划线(_)表示上一个表达式的值,可以用来进行快速计算或测试。例如:>>>2+...

解锁Shell编程:变量_shell $变量

引言:开启Shell编程大门Shell作为用户与Linux内核之间的桥梁,为我们提供了强大的命令行交互方式。它不仅能执行简单的文件操作、进程管理,还能通过编写脚本实现复杂的自动化任务。无论是...

一文学会Python的变量命名规则!_python的变量命名有哪些要求

目录1.变量的命名原则3.内置函数尽量不要做变量4.删除变量和垃圾回收机制5.结语1.变量的命名原则①由英文字母、_(下划线)、或中文开头②变量名称只能由英文字母、数字、下画线或中文字所组成。③英文字...

更可靠的Rust-语法篇-区分语句/表达式,略览if/loop/while/for

src/main.rs://函数定义fnadd(a:i32,b:i32)->i32{a+b//末尾表达式}fnmain(){leta:i3...

C++第五课:变量的命名规则_c++中变量的命名规则

变量的命名不是想怎么起就怎么起的,而是有一套固定的规则的。具体规则:1.名字要合法:变量名必须是由字母、数字或下划线组成。例如:a,a1,a_1。2.开头不能是数字。例如:可以a1,但不能起1a。3....

Rust编程-核心篇-不安全编程_rust安全性

Unsafe的必要性Rust的所有权系统和类型系统为我们提供了强大的安全保障,但在某些情况下,我们需要突破这些限制来:与C代码交互实现底层系统编程优化性能关键代码实现某些编译器无法验证的安全操作Rus...

探秘 Python 内存管理:背后的神奇机制

在编程的世界里,内存管理就如同幕后的精密操控者,确保程序的高效运行。Python作为一种广泛使用的编程语言,其内存管理机制既巧妙又复杂,为开发者们提供了便利的同时,也展现了强大的底层控制能力。一、P...