Web 安全之重定向攻击

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Web 安全之重定向攻击

重定向攻击是攻击者利用网站开放重定向漏洞,将用户访问的网站重定向至恶意网站的一种攻击手段。

攻击原理

在编写 Web 程序时,如果不对重定向的 URL 进行检查,攻击者就可以诱导用户跳转到钓鱼网站进行恶意操作,如窃取用户信息、诱导用户转账等,以此来达到攻击目的。

攻击示例

这里我用 Flask 编写一个简单的 Web 应用来作为重定向攻击的示例。执行以下代码之前你需要使用 pip install flask 的方式安装 Flask

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from flask import Flask, request, redirect, make_response
app = Flask(__name__)
@app.route('/profile')
defprofile():
"""用户信息"""
    session_id = request.cookies.get('session_id')
if session_id:
return'已登录'
else:
return redirect('/login?next=/profile')
@app.route('/login', methods=['GET', 'POST'])
deflogin():
"""登录"""
# 如果为 POST 请求,则处理登录逻辑
if request.method == 'POST':
# 解析前端页面通过 form 表单传递过来的用户名、密码
        form = request.form
        username = form.get('username')
        password = form.get('password')
if username and password:
# 根据 next 参数决定登录成功后重定向到哪里
            next_url = request.args.get('next', '/')
            response = make_response(redirect(next_url))
            response.set_cookie('session_id', '025fcb1587eea0a25493653dfcecc6f1')
return response
return'请输入用户名和密码'
# GET 请求,则返回登录表单
else:
return"""
        <form method="post">
            <input name="username" placeholder="username">
            <input name="password" placeholder="password">
            <button>登录</button>
        </form>
        """
if __name__ == '__main__':
    app.run()

这个 Web 应用中包含了两个视图函数,profile视图函数通过判断请求中是否存在键为 session_idCookie 信息来确认用户是否登录,已登录则返回 已登录 提示信息,未登录则重定向到登录页面,并在 URL 中携带 next=/profile 参数。login 视图函数用来进行登录操作,其实就是给用户浏览器设置一个键为 session_idCookie 信息,登录成功后重定向到 next 参数所指定的 URL。

启动 Flask 应用,使用浏览器访问 http://127.0.0.1:5000/profile 则会被重定向到登录页面。

输入任意用户名和密码进行登录,登录成功后会自动重定向回用户信息页面,显示 已登录

 

总结一下操作流程:

  1. 使用浏览器访问 http://127.0.0.1:5000/profile
  2. 由于未认证,页面被重定向到登录页 http://127.0.0.1:5000/login?next=/profile
  3. 填写登录表单,点击登录按钮,数据通过 POST 请求方式传递给 login 视图函数。
  4. login 视图函数完成登录,同时重定向到 next 参数所指定的 URL,这里即为 /profile
  5. 此时 profile 视图函数判断用户已经登录,返回 已登录

这个流程中,漏洞的入口就在第 2 步,页面被重定向到登录页 http://127.0.0.1:5000/login?next=/profile。其中 next=/profile 参数作为登录成功后将要重定向的页面。

此时,如果将 next=/profile 替换成 next=https://www.jd.com/,再次尝试以上流程,就会发现登录成功后,程序将被重定向到京东首页 https://www.jd.com/

 

既然我们的程序可以被重定向到京东首页,那么就意味着也可以被重定向到任意网址,这是相当危险的。

假如攻击者做了一个跟我们的网站长得一样的网站,一般来说用户不会主动访问攻击者的网站,但攻击者通过短信、邮件等方式诱导用户点击了 next 参数指向恶意网站的链接,那么用户在登录成功以后将被重定向到恶意网站。而此时用户完全不自知,如果用户在恶意网站中的表单等提交了如银行账户、个人密码等信息,将会被直接发送至攻击者的服务器,攻击者拿到用户提交的信息,就可以做一些非法操作。

防范方法

防范重定向攻击最关键的就是要对重定向 URL 合法性进行校验。在进行重定向之前,判断如果 URL 不属于我们网站内部的 URL,则提示 重定向 URL 不正确,不进行跳转。修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from flask import Flask, request, redirect, make_response
app = Flask(__name__)
@app.route('/profile')
defprofile():
"""用户信息"""
    session_id = request.cookies.get('session_id')
if session_id:
return'已登录'
else:
return redirect('/login?next=/profile')
@app.route('/login', methods=['GET', 'POST'])
deflogin():
"""登录"""
# 如果为 POST 请求,则处理登录逻辑
if request.method == 'POST':
# 解析前端页面通过 form 表单传递过来的用户名、密码
        form = request.form
        username = form.get('username')
        password = form.get('password')
if username and password:
            next_url = request.args.get('next', '/')
ifnot next_url.startswith('/'):
return'重定向 URL 不正确'
            response = make_response(redirect(next_url))
            response.set_cookie('session_id', '025fcb1587eea0a25493653dfcecc6f1')
return response
return'请输入用户名和密码'
# GET 请求,则返回登录表单
else:
return"""
        <form method="post">
            <input name="username" placeholder="username">
            <input name="password" placeholder="password">
            <button>登录</button>
        </form>
        """
if __name__ == '__main__':
    app.run()

我们只需要在程序中判断 URL 是否以 / 开头,因为内部链接的跳转通常不会写成带有主机名的 URL,而是以 / 开头的路径。

启动 Flask 应用,再次使用浏览器访问重定向到京东首页的链接 http://127.0.0.1:5000/login?next=https://www.jd.com/

 

不出所料,现在外部链接已经能够被程序检测到,从而重定向失败。

但是,仅仅这样做是不够的。现在使用浏览器访问 http://127.0.0.1:5000/login?next=//www.jd.com/ 再次进行测试。

 

竟然重定向成功了!也就是说 //www.jd.com/ 是有效的网址,同时绕过了程序的检测。

所以我们不能单纯的以判断 URL 是否以 / 开头作为合法性校验,而要使用更加严谨的方式,修改代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from urllib.parse import urlparse, urljoin
from flask import Flask, request, redirect, make_response
app = Flask(__name__)
defis_safe_url(url):
    current_netloc = urlparse(request.host_url).netloc
    redirect_netloc = urlparse(urljoin(request.host_url, url)).netloc
    print(f'current_netloc: {current_netloc}, redirect_netloc: {redirect_netloc}')
return current_netloc == redirect_netloc
@app.route('/profile')
defprofile():
"""用户信息"""
    session_id = request.cookies.get('session_id')
if session_id:
return'已登录'
else:
return redirect('/login?next=/profile')
@app.route('/login', methods=['GET', 'POST'])
deflogin():
"""登录"""
# 如果为 POST 请求,则处理登录逻辑
if request.method == 'POST':
# 解析前端页面通过 form 表单传递过来的用户名、密码
        form = request.form
        username = form.get('username')
        password = form.get('password')
if username and password:
            next_url = request.args.get('next', '/')
ifnot is_safe_url(next_url):
return'重定向 URL 不正确'
            response = make_response(redirect(next_url))
            response.set_cookie('session_id', '025fcb1587eea0a25493653dfcecc6f1')
return response
return'请输入用户名和密码'
# GET 请求,则返回登录表单
else:
return"""
        <form method="post">
            <input name="username" placeholder="username">
            <input name="password" placeholder="password">
            <button>登录</button>
        </form>
        """
if __name__ == '__main__':
    app.run()

这次编写一个 is_safe_url 函数专门用来校验 URL 合法性。urlparse 函数能够根据 URL 协议格式将 URL 解析为 6 个部分,其中 netloc 即为主机名和端口号。urljoin 函数用来合并两个 URL,以此将以 / 开头的 URL 转换成绝对路径的 URL,从而避免获取不到主机名和端口号。通过对比当前访问 URL 的 netloc 和重定向 URL 的 netloc 就能够判断 URL 是否合法。

再次使用浏览器访问 http://127.0.0.1:5000/login?next=//www.jd.com/

 

1
2
# 控制台打印信息
current_netloc: 127.0.0.1:5000, redirect_netloc: www.jd.com

这次将会看到重定向失败的信息,说明此方法有效。

不要试图通过判断重定向 URL 是否包含当前网站的主机名来校验 URL 合法性,因为类似 https://example.org.attacker.com 也是一个合法的 URL。

如果网站确实需要重定向到合法的其他主机名中,则可以通过白名单的方式使校验通过。

当然,所谓安全都是相对的,并没有绝对的安全,开发者应该保持警惕,做到尽量降低安全风险。

相关文章
|
1天前
|
SQL 安全 数据库
深度揭秘:Python Web安全攻防战,SQL注入、XSS、CSRF一网打尽!
在Web开发领域,Python虽强大灵活,但安全挑战不容小觑。本文剖析Python Web应用中的三大安全威胁:SQL注入、XSS及CSRF,并提供防御策略。通过示例代码展示如何利用参数化查询、HTML转义与CSRF令牌构建安全防线,助您打造更安全的应用。安全是一场持久战,需不断改进优化。
10 3
|
8天前
|
SQL 安全 数据库
从入门到精通:Python Web安全守护指南,SQL注入、XSS、CSRF全防御!
【9月更文挑战第13天】在开发Python Web应用时,安全性至关重要。本文通过问答形式,详细介绍如何防范SQL注入、XSS及CSRF等常见威胁。通过使用参数化查询、HTML转义和CSRF令牌等技术,确保应用安全。附带示例代码,帮助读者从入门到精通Python Web安全。
29 6
|
9天前
|
SQL 安全 JavaScript
告别Web安全小白!Python实战指南:抵御SQL注入、XSS、CSRF的秘密武器!
【9月更文挑战第12天】在Web开发中,安全漏洞如同暗礁,尤其对初学者而言,SQL注入、跨站脚本(XSS)和跨站请求伪造(CSRF)是常见挑战。本文通过实战案例,展示如何利用Python应对这些威胁。首先,通过参数化查询防止SQL注入;其次,借助Jinja2模板引擎自动转义机制抵御XSS攻击;最后,使用Flask-WTF库生成和验证CSRF令牌,确保转账功能安全。掌握这些技巧,助你构建更安全的Web应用。
14 5
|
21天前
|
安全 关系型数据库 数据库
FastAPI数据库操作秘籍:如何通过高效且安全的数据库访问策略,使你的Web应用飞速运转并保持数据完整性?
【8月更文挑战第31天】在构建现代Web应用时,数据库操作至关重要。FastAPI不仅简化了API创建,还提供了高效数据库交互的方法。本文探讨如何在FastAPI中实现快速、安全的数据处理。FastAPI支持多种数据库,如SQLite、PostgreSQL和MySQL;选择合适的数据库可显著提升性能。通过安装相应驱动并配置连接参数,结合ORM库(如Tortoise-ORM或SQLAlchemy),可以简化数据库操作。使用索引、批量操作及异步处理等最佳实践可进一步提高效率。同时,确保使用参数化查询防止SQL注入,并从环境变量中读取敏感信息以增强安全性。
40 1
|
10天前
|
存储 安全 JavaScript
Web安全-XSS漏洞
Web安全-XSS漏洞
11 0
|
20天前
|
Rust 安全 开发者
惊爆!Xamarin 携手机器学习,开启智能应用新纪元,个性化体验与跨平台优势完美融合大揭秘!
【8月更文挑战第31天】随着互联网的发展,Web应用对性能和安全性要求不断提高。Rust凭借卓越的性能、内存安全及丰富生态,成为构建高性能Web服务器的理想选择。本文通过一个简单示例,展示如何使用Rust和Actix-web框架搭建基本Web服务器,从创建项目到运行服务器全程指导,帮助读者领略Rust在Web后端开发中的强大能力。通过实践,读者可以体验到Rust在性能和安全性方面的优势,以及其在Web开发领域的巨大潜力。
29 0
|
20天前
|
开发者 安全 SQL
JSF安全卫士:打造铜墙铁壁,抵御Web攻击的钢铁防线!
【8月更文挑战第31天】在构建Web应用时,安全性至关重要。JavaServer Faces (JSF)作为流行的Java Web框架,需防范如XSS、CSRF及SQL注入等攻击。本文详细介绍了如何在JSF应用中实施安全措施,包括严格验证用户输入、使用安全编码实践、实施内容安全策略(CSP)及使用CSRF tokens等。通过示例代码和最佳实践,帮助开发者构建更安全的应用,保护用户数据和系统资源。
34 0
|
20天前
|
Java 开发者 关系型数据库
JSF与AWS的神秘之旅:如何在云端部署JSF应用,让你的Web应用如虎添翼?
【8月更文挑战第31天】在云计算蓬勃发展的今天,AWS已成为企业级应用的首选平台。本文探讨了在AWS上部署JSF(JavaServer Faces)应用的方法,这是一种广泛使用的Java Web框架。通过了解并利用AWS的基础设施与服务,如EC2、RDS 和 S3,开发者能够高效地部署和管理JSF应用。文章还提供了具体的部署步骤示例,并讨论了使用AWS可能遇到的挑战及应对策略,帮助开发者更好地利用AWS的强大功能,提升Web应用开发效率。
42 0
|
20天前
|
开发者 前端开发 Apache
【绝不错过!】揭秘Wicket大神级插件,带你飞越编程极限,探索Web应用开发新大陆!
【8月更文挑战第31天】Apache Wicket是一个成熟的Java Web框架,以其组件化体系结构、直观的API及对MVC的支持著称。其活跃社区贡献了大量插件和扩展,显著提升了Wicket的功能性。本文推荐几个实用插件,如**Wicket Ajax Support**,可轻松添加Ajax功能,提升用户体验;**Bootstrap for Wicket**则将Bootstrap与Wicket结合,美化应用界面。
26 0
|
21天前
|
存储 SQL 安全
【绝密攻略】Flask应用如何抵御黑客入侵?七大安全技巧助你构建固若金汤的Web防线!
【8月更文挑战第31天】安全性是Web应用开发中的关键部分。Flask作为一款轻量级且高度可定制的框架,虽灵活但需开发者确保应用安全。本文介绍如何通过具体措施加固Flask应用,包括更新依赖项、启用CSRF保护、使用HTTPS、安全存储密码、防止SQL注入及清理用户输入等。通过示例代码展示如何在实际开发中应用这些策略,帮助提升应用安全性,为用户提供更可靠的服务。
42 0