赞
踩
Flask 作为一个 WEB 框架来说内部封装的安全性的措施很多,基本解决了常见的 web 漏洞,下面介绍 Flask 是如何来避免产生 常见的 web 漏洞。
Flask 使用 ORM 对象关系映射的数据库管理方式,同时 Flask 框架内部也封装了许多安全性的函数,对于 SQL 注入拥有一定防护力,如图
Flask 中单引号会自动进行转义
Flask 使用 Jinja2 模板引擎,Jinja 会默认将渲染变量进行 HTML 实体转义
@user_bp.route('/test2')
def test2():
code = '<h1>Flask</h1>'
return render_template('test.html', code=code)
# test.html
{{ code }}
如果需要让 HTML 标签生效 可以使用 safe 过滤器
例如:一些富文本编辑器以 HTML 格式进行文章的保存,此时需要加 safe 过滤器保证文章正常保存显示,此时 xss 潜在发生处就是富文本编辑器
Flask 配置 Jinja2 自动转义所有值,除非显式地指明不转义。这就排除了模板导致的所有 XSS 问题,但是你仍需要在其它的地方小心
Markup
虽然 Jinja2 可以通过转义 HTML 来保护你免受 XSS 问题,仍有一种情况,它不能保护你: 属性注入的 XSS 。为了应对这种攻击媒介,确保当在属性中使用 Jinja 表达式时,始终用单引号或双引号包裹属性
正确:<a href="{{ href }}">the text</a>
错误:<a href={{ href }}>the text</a>
# 官方文档所指明的,在实际测试中不论加没加引号,浏览器解析时都会有引号
# 注意尽量要让用户去控制 href 属性
@article_bp1.route('/test')
def article_test():
html = '''
<h1>111111</h1>
'''
href ='javascript:alert(document.body);'
return render_template('article/test.html', html=html, href=href)
http://127.0.0.1:5000/article/test?href=javascript:alert(document.cookie);
当用户输入被串联到模板中而不是作为数据传递时,服务器端模板注入漏洞就会出现,简单来说也就是不正确的使用 flask 中的render_template_string 方法会引发SSTI
确定模板引擎
常用的方法是使用来自不同模板引擎的语法注入任意数学运算
存在漏洞代码示例
from flask import Flask,render_template_string,request
app = Flask(__name__)
@app.route('/test/')
def test():
code = request.args.get('id') //get方式获取id
html = '''
<h3>%s</h3>
'''%(code)
return render_template_string(html)
#产生原因就是先进行了拼接,在渲染
app.run()
这个问题也并非是Jinja2的问题,而且在编写的过程中出现的人为问题
flask 中有许多提高安全性的函数,比如以下几个
generate_password_hash(password)
# 将 password 变量的值进行加盐 hash 加密
check_password_hash(user.password, password)
# 与上个函数配合使用,用来将 加密数据与为加密数据做对比,如果未加密数据加密后为已加密数据则返回 true,否则返回 false
secure_filename(icon_filename)
# 将传入文件的文件名进行安全处理,如将空格替换为下划线等
Flask 中并不存在表单验证,很容易造成 CSRF
基本上,对于每个修改服务器上内容的请求,应该使用一次性令牌,并存储在 cookie 里, 并且在发送表单数据的同时附上它,在服务器再次接收数据之后,你要比较两个令牌,并确保它们相等
Flask 使用 Flask-WTF 插件来避免出现 CSRF
使用方法
# 后端 class UserForm(FlaskForm): username = StringField(label='用户名', validators=[DataRequired(), Length(min=6, max=20, message='长度必须在6~20位之间')]) password = PasswordField(label='密码', validators=[DataRequired(), Length(min=6, max=20, message='长度必须在6~20位之间')]) confirm_password = PasswordField(label='确认密码', validators=[DataRequired(), Length(min=6, max=20, message='长度必须在6~20位之间'), EqualTo('password', '密码不一致')]) phone = StringField(label='手机号', validators=[DataRequired(), Length(min=11, max=11, message='长度必须是11位')]) email = EmailField(label='邮箱', validators=[DataRequired()]) recaptcha = StringField(label='验证码') def validate_recaptcha(self, data): input_code = data.data code = session.get('valid') if input_code.lower() != code.lower(): raise ValidationError('验证码错误!') ...... # 前端 <form class="form-horizontal" id="container" method="post" action="{{ url_for('user.register') }}"> {{ userform.csrf_token }} {# 防止csrf,必须设置secret_key #} <table id="register"> <tr> <td>{{ userform.username.label }}:</td> <td>{{ userform.username }}{% if userform.username.errors %} {{ userform.username.errors.0 }}{% endif %}<br> {# 如果有报错则输出报错的message, .0表示只输出内容 #}</td> </tr> ......
Flask 使用 jsonify 函数将数据转换为 json 数据返回
# 此段代码为后台验证码校验代码
@user_bp.route('/check_phone', methods=['GET', 'POST'], endpoint='check_phone')
def check_phone():
phone = request.args.get('phone')
user = User.query.filter(User.phone == phone).all()
# code:200可用 400不可用
if len(user) > 0:
code = 400
else:
code = 200
return jsonify(code=code)
开发文档:http://docs.jinkan.org/docs/flask/security.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。