美文网首页
2020-06-09--flask05--flask基础05

2020-06-09--flask05--flask基础05

作者: program_white | 来源:发表于2020-06-10 15:41 被阅读0次
  • 登录+转账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是有区别的

相关文章

网友评论

      本文标题:2020-06-09--flask05--flask基础05

      本文链接:https://www.haomeiwen.com/subject/uzzetktx.html