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

一、过滤器

1.字符串操作

  • 格式
{{variable | filter name[(*args)] | filter name2
  • safe 禁用转义

{{ 'hello'|safe }}

  • capitalize变量首字母大写

{{ 'hello'|capitalize }}

  • upper 转化成大写

{{ 'hello'|upper }}

  • lower 转化成小写

{{ 'HELLO'|lower }}

  • title 单词首字母大写

{{ 'hello lx'|title }}

  • '%s is %d'|format('李现',28) 格式化输出

{{ '%s is %d'|format('李现',28) }}

  • reverse 字符串反转

{{ 'hello'|reverse }}

  • striptags 渲染之前把所有的html标签都去掉

{{ 'hello'|striptags }}

示例:
  • 新建一个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文件



    
    Title


常见内建过滤器

{{ 'hello'|safe }}


hello

{{ 'hello'|capitalize }}


{{ 'hello'|upper }}


{{ 'HELLO'|lower }}


{{ 'hello lx'|title }}


{{ '%s is %d'|format('李现',28) }}


{{ 'hello'|reverse }}


{{ 'hello'|striptags }}


  • 运行程序


2.列表操作

  • first 显示列表第一个元素

{{ [1,2,3] |first }}

  • last 显示列表最后一个元素

{{ [2,3,4] |last }}

  • length 显示列表长度

{{ [2,3,4] |length }}

  • sum 列表数列求和

{{ [2,3,4] |sum }}

  • sort 列表排序

{{ [2,4,3] |sort }}

示例:

列表操作

{{ [1,2,3] |first }}

{{ [2,3,4] |last }}

{{ [2,3,4] |length }}

{{ [2,3,4] |sum }}

{{ [2,4,3] |sort }}

二、验证表单数据(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文件



    
    Title


用户登录页面




  • 运行





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文件



    
    Title


用户登录页面




{% for message in get_flashed_messages() %} {{ message }} {% endfor %}
  • 运行



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文件



    
    Title


WTF用户登录页面

{{ form.username.label }}{{ form.username }}
{{ form.password.label }}{{ form.password }}
{{ form.password2.label }}{{ form.password2 }}
{{ form.submit }}
{% for message in get_flashed_messages() %} {{ message }} {% endfor %}
  • 输入正确的密码时:



  • 输入不正确的密码时:


三、转账例子

  • 新创建一个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文件



    
    Title


用户登录页面



{% for message in get_flashed_messages() %} {{ message }} {% endfor %}
  • 在templates文件夹下创建transfer.html文件



    
    Title


我是网站A,转账页面



  • 运行程序


四、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文件



    
    Title


我是网站B

> > >
  • 将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文件中添加一行代码

  • 运行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)+转账例子+CSRF攻击)