- 登录+转账demo
- csrf演示
- 防止csrf攻击策略
- flask解决csrf攻击
登录+转账demo
新建webA.py:
from flask import Flask, render_template, request, redirect, url_for, make_response
app = Flask(__name__)
app.secret_key = 'defwefwfwef'
app.config['WTF_CSRF_ENABLED'] = False #关闭csrf的验证
@app.route('/',methods=['GET','POST'])
def index():
if request.method == 'POST':
#取表单数据
username = request.form.get('username')
password = request.form.get('password')
if not all([username,password]):
print('参数错误')
else:
print(username,password)
if username == 'admin' and password == 'admin':
response = redirect(url_for('transfer')) #验证通过后跳转到转账页面
#因为转账要求用户是登陆状态,要进行状态保持
response.set_cookie('username',username) #给这个页面设置cookie
return response
else:
print('错误')
return render_template('login3.html')
@app.route('/transfer',methods=['GET','POST'])
def transfer():
#取出cookie,确保登录
username = request.cookies.get('username',None)
if not username:
return redirect(url_for('index'))
if request.method == 'POST':
#取出form中的值
to_account = request.form.get('to_account')
money = request.form.get('money')
print('假装转账')
return '转账{}元到{}账户'.format(money,to_account)
#如果是get方式
response = make_response(render_template('tranfer.html'))
return response
if __name__ == '__main__':
app.run(debug=True)
login3.html:
<body>
<h1>登录页面</h1>
<form action="" method="POST">
<label for="uname">username:</label>
<input type="text" name="username" id="uname" placeholder="请输入用户名">
<br><br>
<label for="text">password:</label>
<input type="text" name="password" id="text" placeholder="请输入密码">
<br><br>
<input type="submit" value="登录">
</form>
</body>
tranfer.html:
<body>
<h1>我是网站A,转账页面</h1>
<form action="" method="POST">
<label for="to_account">账户:</label>
<input type="number" name="to_account" id="to_account" placeholder="请输入账户">
<br><br>
<label for="money">金额:</label>
<input type="number" name="money" id="money" placeholder="请输入金额">
<br><br>
<input type="submit" value="转账">
</form>
</body>
分析:
1.首先使用get方式进入index页面,返回login3.html,用户填写完成后,提交给当前的页面,在视图函数中判断方法是否为post,如果是,获取username和password,并判断是否为空,如果不为空,进行验证username和password是否与数据库中的信息一致(这里写死),若一致,重定向到transfer()中,并设置该页面的cookie值。
2.在transfer()中,先获取该页面的cookie值,如果没有,那要先进行登录,重定向到index页面,如果有cookie,判断方法是get还是post,如果是get,就返回tranfer.html页面。用户填写后,post方式提交给当前页面,并获取to_account和money的值,返回转账信息。
运行:
访问:127.0.0.1:5000
填写用户名和密码:

重定向到transfer:

该页面有一个cookie值:username

转账后显示转账信息:

csrf演示
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟 跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
例如:
假如一家银行用以运行转账操作的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资金。

A网站就是上边的webA.py。
新建webB.py:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/',methods=['GET','POST'])
def index():
return render_template('webB.html')
if __name__ == '__main__':
app.run(debug=True,port=8000)
webB.html:
<body>
<h1>网站B</h1>
<form action="http://127.0.0.1:5000/transfer" method="post">
<input type="hidden" name="to_account" value="99999">
<input type="hidden" name="money" value="199999">
<input type="submit" value="点击领取优惠卷">
</form>
</body>
分析:
当A网站的某用户已认证后,cookie信息会在浏览器上保存一段时间,在这段时间中,该用户点击了B网站的按钮,这个按钮把信息提交到A网站的转账页面,这时候该用户的钱已经转给B网站事先写好的账户中了。
运行:
当A网站的用户没有cookie时,B网站点击按钮后,没有cookie,直接跳转到index页面进行登录:


A网站的用户进行登录后,cookie值会被保存一段时间:

该用户在这段时间中点击了B网站的按钮信息:

转账信息:

防止csrf攻击策略
在上一步中,B网站攻击A网站没有一个能够阻挡B网站攻击的信息。
为了防止csrf攻击,必须在A网站的转账页面中设置一个随机字符串,在A网站的表单和cookie中设置字符串,而B网站没有该字符串,这样B网站在进入A网站时必须通过这个随机字符串的验证才能进入,并且每次访问A网站时,和该字符串不相同。
生成随机字符串:
#生成随机字符
def generste_csrf():
return bytes.decode(base64.b64encode(os.urandom(48)))
在进入A网站的转账页面时,就生成该随机字符,传给前端的form表单和cookie,并在用户post方法提交后获取这两个字符,如果这两个字符不相同,那么表示该用户不是正常的提交操作,是非法操作。
@app.route('/transfer',methods=['GET','POST'])
def transfer():
#取出cookie,确保登录
username = request.cookies.get('username',None)
if not username:
return redirect(url_for('index'))
if request.method == 'POST':
#取出form中的值
to_account = request.form.get('to_account')
money = request.form.get('money')
#取出提交时表单中的csrf_token
form_csrf_token = request.form.get('csrf_token')
#cookie中的csrf_token
cookie_csrf_token = request.cookies.get('csrf_token')
if form_csrf_token != cookie_csrf_token: #比较两个csrf_token
return 'token校验失败,可能是非法操作'
print('假装转账')
return '转账{}元到{}账户'.format(money,to_account)
# 如果是get方式
#生成csrf_token
csrf_token = generste_csrf()
response = make_response(render_template('tranfer.html',csrf_token=csrf_token))
#用于提交验证
response.set_cookie('csrf_token',csrf_token) #把随机字符设置到cookie中
return response
transfer.html:

运行:
A网站用户登陆后,设置了 两个cookie值:username和csrf_token

当该用户再次点击按钮时,username虽然通过,但是在B网站的form表单中并没有csrf_token,在提交给transfer后,没有通过验证,返回非法操作。

flask解决csrf攻击
CSRFProtect原理分析:
后端根据加密算法,生成csrf_token值,然后把csrf_token存储在session和g中,jinjia模版中根据{{ csrf_token }} 获取token值。提交表单的时候,前端把token值和其他参数一起传回给后端,后端拿到值之后开始验证,如果和session中的值一致,就验证成功,否则验证失败。
如果不想再某一个视图上添加csrf protect,可以使用如下方法取消csrf protect:
@csrf.exempt
使用CSRFProtect进行验证:
导入:
from flask_wtf import CSRFProtect #CSRF验证
关联app:
csrf = CSRFProtect(app)
在前端页面中显示:
方式一:
<form method="post" action="">
{{ form.csrf_token }}
</form>
方式二:(推荐)
<form method="post" action="/">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>
运行:

数据库
flask没有自己的orm框架,所以首先要安装要用到的库:
pip install sqlalchemy #orm框架
pip install flask_sqlalchemy #简化sqlalchemy操作的工具,依赖于sqlalchemy
#python连接数据库的两个常用库
pip install mysqlclient
pip install pymysql
新建db_demo.py:
1.配置mysql数据库:
from flask_sqlalchemy import SQLAlchemy
'''配置mysql数据库'''
#mysql数据库地址
# mysql://(用户名):(密码)@地址/数据库名
mysqlpath = 'mysql://root:root@127.0.0.1:3306/test3'
#设置数据库的路径
app.config['SQLALCHEMY_DATABASE_URI'] = mysqlpath
#关联app
db = SQLAlchemy(app)
2.创建model类:
#model类
class Role(db.Model):
#定义表名
__tablename__ = 'roles'
#id
id = db.Column(db.Integer,primary_key=True)
#name
name = db.Column(db.String(64),unique=True)
3.在数据库中创建将该model类映射为表:
#删除表
db.drop_all() #
#创建表
db.create_all()
#填加数据
ro1 = Role(name='admin')
ro2 = Role(name='user')
db.session.add_all([ro1,ro2])
db.session.commit() #提交
设置:
#动态追踪
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
#显示原始sql
app.config['SQLALCHEMY_ECHO'] = True
运行:

代码:
db.demo.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
'''配置mysql数据库'''
#mysql数据库地址
# mysql://(用户名):(密码)@地址/数据库名
mysqlpath = 'mysql://root:root@127.0.0.1:3306/test3'
#设置数据库的路径
app.config['SQLALCHEMY_DATABASE_URI'] = mysqlpath
#关联app
db = SQLAlchemy(app)
#动态追踪
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
#显示原始sql
app.config['SQLALCHEMY_ECHO'] = True
#model类
class Role(db.Model):
#定义表名
__tablename__ = 'roles'
#id
id = db.Column(db.Integer,primary_key=True)
#name
name = db.Column(db.String(64),unique=True)
@app.route('/')
def index():
return 'index'
# 删除表
db.drop_all()
# 创建表
db.create_all()
# 填加数据
ro1 = Role(name='admin')
ro2 = Role(name='user')
db.session.add_all([ro1, ro2])
db.session.commit() #提交
if __name__ == '__main__':
app.run(debug=True)
注:sqlalchemy在创建表时并不会自动创建id主键值和表名,需要自行设计,这与Django是有区别的
网友评论