一、过滤器
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攻击的方法,以后会进行完善。
(此文章仅作为个人学习笔记使用,如有错误欢迎指正~)
网友评论