美文网首页
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