美文网首页
web应用框架——过滤器+验证表单数据(WTF)+转账例子+CS

web应用框架——过滤器+验证表单数据(WTF)+转账例子+CS

作者: 思君_4cd3 | 来源:发表于2020-06-12 12:29 被阅读0次

一、过滤器

1.字符串操作

  • 格式
{{variable | filter name[(*args)] | filter name2
  • safe 禁用转义
<p>{{ '<em>hello</em>'|safe }}</p>
  • capitalize变量首字母大写
<p>{{ 'hello'|capitalize }}</p>
  • upper 转化成大写
<p>{{ 'hello'|upper }}</p>
  • lower 转化成小写
<p>{{ 'HELLO'|lower }}</p>
  • title 单词首字母大写
<p>{{ 'hello lx'|title }}</p>
  • '%s is %d'|format('李现',28) 格式化输出
<p>{{ '%s is %d'|format('李现',28) }}</p>
  • reverse 字符串反转
<p>{{ 'hello'|reverse }}</p>
  • striptags 渲染之前把所有的html标签都去掉
<p>{{ '<em>hello</em>'|striptags }}</p>
示例:
  • 新建一个demo_for_filter.py文件
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():

    #render_template进行模板渲染
    return  render_template('demo-filter.html')
if __name__ == '__main__':
    app.run(debug=True)
  • 在templates文件夹下新建一个demo-filter.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>常见内建过滤器</h1>
<p>{{ '<em>hello</em>'|safe }}</p>
<hr>
<em>hello</em>
<hr>
<p>{{ 'hello'|capitalize }}</p>
<hr>
<p>{{ 'hello'|upper }}</p>
<hr>
<p>{{ 'HELLO'|lower }}</p>
<hr>
<p>{{ 'hello lx'|title }}</p>
<hr>
<p>{{ '%s is %d'|format('李现',28) }}</p>
<hr>
<p>{{ 'hello'|reverse }}</p>
<hr>
<p>{{ '<em>hello</em>'|striptags }}</p>
<hr>
</body>
</html>
  • 运行程序


2.列表操作

  • first 显示列表第一个元素
<p>{{ [1,2,3] |first }}</p>
  • last 显示列表最后一个元素
<p>{{ [2,3,4] |last }}</p>
  • length 显示列表长度
<p>{{ [2,3,4] |length }}</p>
  • sum 列表数列求和
<p>{{ [2,3,4] |sum }}</p>
  • sort 列表排序
<p>{{ [2,4,3] |sort }}</p>
示例:
<h1>列表操作</h1>
<p>{{ [1,2,3] |first }}</p>
<p>{{ [2,3,4] |last }}</p>
<p>{{ [2,3,4] |length }}</p>
<p>{{ [2,3,4] |sum }}</p>
<p>{{ [2,4,3] |sort }}</p>

二、验证表单数据(WTF)


1.表单验证

  • 新建一个demo_wrf1.py文件
from flask import Flask, render_template, request, redirect, url_for, flash

app = Flask(__name__)

app.secret_key = 'soda'

@app.route('/')
def index():
    return "soda"
@app.route('/demo1',methods=["get","post"])
def demo1():
    if request.method == 'POST':
        #获取表单的三个参数
        username = request.form.get("username","")
        password = request.form.get("password","")
        password2 = request.form.get("password2","")
  print(username,password,password2)
            return 'success'

    return render_template("login.html")
if __name__ == '__main__':
    app.run(debug=True)
  • 在templates文件夹内建一个login.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户登录页面</h1>
<form action="" method="post">
    <label>用户名</label><input type="text" name="username" placeholder="请输入用户名"><br>
    <label>密码</label><input type="password" name="password" placeholder="请输入密码"><br>
    <label>确认密码</label><input type="password" name="password2" placeholder="请输入确认密码"><br>
    <input type="submit" value="注册">
</form>
</body>
</html>
  • 运行





2.flash闪现

  • 修改demo_wrf1.py文件
from flask import Flask, render_template, request, redirect, url_for, flash

app = Flask(__name__)

app.secret_key = 'soda'

@app.route('/')
def index():
    return "soda"
@app.route('/demo1',methods=["get","post"])
def demo1():
    if request.method == 'POST':
        #获取表单的三个参数
        username = request.form.get("username","")
        password = request.form.get("password","")
        password2 = request.form.get("password2","")
        if not all([username,password,password2]):
            #向前端闪现一个参数不足的消息
            flash("参数不足")
        elif password != password2:
            flash("两次密码不一致")
        else:
            print(username,password,password2)
            return 'success'

    return render_template("login.html")
if __name__ == '__main__':
    app.run(debug=True)
  • 修改login.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户登录页面</h1>
<form action="" method="post">
    <label>用户名</label><input type="text" name="username" placeholder="请输入用户名"><br>
    <label>密码</label><input type="password" name="password" placeholder="请输入密码"><br>
    <label>确认密码</label><input type="password" name="password2" placeholder="请输入确认密码"><br>
    <input type="submit" value="注册">
</form>
{% for message in get_flashed_messages() %}
    {{ message }}
{% endfor %}

</body>
</html>
  • 运行



3.WTF表单验证

  • 安装flask_wtf
pip install flask_wtf
  • 新建一个demo_wtf2.py文件
from flask import Flask, render_template, request, redirect, url_for, flash
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, EqualTo

app = Flask(__name__)

class RegisterForm(FlaskForm):
    username = StringField("用户名:",validators=[DataRequired("请输入用户名")],render_kw={"placeholder":"请输入用户名"})
    password = PasswordField("密码:",validators=[DataRequired("请输入密码")],render_kw={"placeholder":"请输入密码"})
    password2 = PasswordField("确认密码:",validators=[DataRequired("请输入确认密码"),EqualTo("password","两次密码不一致")],render_kw={"placeholder":"请输入确认密码"})
    submit = SubmitField("注册")
app.secret_key = 'soda'
app.config['WTF_CSRF_ENABLED'] = False

@app.route('/')
def index():
    return "soda"
@app.route('/demo1', methods=["get","post"])
def demo1():
    register_form = RegisterForm()
    if register_form.validate_on_submit():
        #如果代码走到if里面证明表单验证有效
        username = request.form.get("username")
        password = request.form.get("password","")
        password2 = request.form.get("password2","")
        print(username,password,password2)
        return "success"
    else:
        if request.method =='POST':
            flash("表单参数有误或者不完整")
    return render_template("login2.html",form = register_form)
if __name__ == '__main__':
    app.run(debug=True)
  • 在templates文件夹下创建一个login2.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>WTF用户登录页面</h1>
<form method="post">
    {{ form.username.label }}{{ form.username }}<br>
    {{ form.password.label }}{{ form.password }}<br>
    {{ form.password2.label }}{{ form.password2 }}<br>
    {{ form.submit }}<br>
</form>
{% for message in get_flashed_messages() %}
    {{ message }}
{% endfor %}
</body>
</html>
  • 输入正确的密码时:



  • 输入不正确的密码时:


三、转账例子

  • 新创建一个webA.py文件
from flask import Flask, request, render_template, redirect, url_for, flash, make_response
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField,SubmitField
from wtforms.validators import DataRequired,EqualTo

app = Flask(__name__)
app.config['WTF_CSRF_ENABLED'] = False


app.secret_key = 'dasdasdsadas'

@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 == '1234':
                # 登录成功跳转到转账页面
                response = redirect(url_for('transfer'))
                # 因为转账要求用户登录,所以我们进行状态保持,设置用户名到cookie中
                response.set_cookie('username',username)
                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':
        to_account = request.form.get("to_account")
        money = request.form.get("money")
        print('假装执行转账')
        return '转账{}元到{}账户成功!'.format(to_account, money)
    response = make_response(render_template('transfer.html'))
    return response
if __name__ == '__main__':
    app.run(debug=True)
  • 在templates文件夹下创建一个login3.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户登录页面</h1>
<form action="" method="post">
    <label> 用户名</label> <input type="text" name="username" placeholder="请输入用户名"> <br>
    <label> 密码</label><input type="password" name="password" placeholder="请输入密码"> <br>
    <input type="submit" value="登录">
</form>


{% for message in get_flashed_messages() %}
    {{ message }}
{% endfor %}
</body>
</html>
  • 在templates文件夹下创建transfer.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>我是网站A,转账页面</h1>
<form action="" method="post">
    <label> 账户</label> <input type="text" name="to_account" placeholder="请输入要转账的账户"> <br>
    <label> 金额</label><input type="number" name="money" placeholder="请输入转账金额"> <br>
    <input type="submit" value="转账">
</form>
</body>
</html>
  • 运行程序


四、CSRF攻击

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本XSS相比,XSS利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。(百度百科)

  • 新建一个webB.html文件
from flask import Flask, request, render_template

app = Flask(__name__)
@app.route('/')
def index():
    return render_template('tempb-index.html')
if __name__ == '__main__':
    app.run(debug=True, port=8000)
  • 在templates文件夹下创建tempb-index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<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="点击领取优惠券"> >
</body>
</html>
  • 将webA.py文件和webB.py文件同时运行



点击网站B的时候它能够获取网站A的信息,这样的情况在转账的时候是十分不安全的,所以我们将避免这种情况:

  • 打开webA.py文件
import base64

from flask import Flask, request, render_template, redirect, url_for, flash, make_response
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField,SubmitField
from wtforms.validators import DataRequired,EqualTo
import os
app = Flask(__name__)
app.config['WTF_CSRF_ENABLED'] = False


app.secret_key = 'dasdasdsadas'

@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 == '1234':
                # 登录成功跳转到转账页面
                response = redirect(url_for('transfer'))
                # 因为转账要求用户登录,所以我们进行状态保持,设置用户名到cookie中
                response.set_cookie('username',username)
                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':
        to_account = request.form.get("to_account")
        money = request.form.get("money")
        # 取出表单的 crsf_token
        form_crsf_token = request.form.get("crsf_token")
        # 取出 cookie的crsf_token
        cookie_crsf_token = request.cookies.get('crsf_token')
        if form_crsf_token != cookie_crsf_token:
            return "token校验失败,可能是非法操作"


        print('假装执行转账')
        return '转账{}元到{}账户成功!'.format(to_account, money)
    #  使用 make_response, 相当于 django的httpresponse
    # 生成 crsf_token
    crsf_token = generate_crsf()
    response = make_response(render_template('transfer.html', crsf_token= crsf_token))
    # 用于提交验证
    response.set_cookie('crsf_token',crsf_token)
    return response

def generate_crsf():
    return bytes.decode(base64.b64encode(os.urandom(48)))

if __name__ == '__main__':
    app.run(debug=True)
  • 在templates/transfer.html文件中添加一行代码
<input type="hidden" name="crsf_token" value="{{crsf_token}}" >
  • 运行webA.py和webB.py文件
    在网站A页面进行登陆到转账页面:



    再打开网站B进行攻击,会发现攻击不了:


  • 代码解析:
def generate_crsf():
    return bytes.decode(base64.b64encode(os.urandom(48)))

这个代码的作用,就是产生一串48位的字符:



  • 如果要对表单设置防攻击的话直接写一行代码就可以:
{{ form.crsf_token }}
from flask_wtf import  CSRFProtect
CSRFProtect(app)

这算是个简单的阻断srsf攻击的方法,以后会进行完善。

(此文章仅作为个人学习笔记使用,如有错误欢迎指正~)

相关文章

网友评论

      本文标题:web应用框架——过滤器+验证表单数据(WTF)+转账例子+CS

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