Flask(python web框架)安全

Flask 作为一个 WEB 框架来说内部封装的安全性的措施很多,基本解决了常见的 web 漏洞,下面介绍 Flask 是如何来避免产生 常见的 web 漏洞。

SQL注入:

Flask 使用 ORM 对象关系映射的数据库管理方式,同时 Flask 框架内部也封装了许多安全性的函数,对于 SQL 注入拥有一定防护力,如图
image-20210127135813901
Flask 中单引号会自动进行转义

XSS:

Flask 使用 Jinja2 模板引擎,Jinja 会默认将渲染变量进行 HTML 实体转义

@user_bp.route('/test2')
def test2():
    code = '<h1>Flask</h1>'
    return render_template('test.html', code=code)

# test.html
{{ code }}

image-20210304150751171
如果需要让 HTML 标签生效 可以使用 safe 过滤器

例如:一些富文本编辑器以 HTML 格式进行文章的保存,此时需要加 safe 过滤器保证文章正常保存显示,此时 xss 潜在发生处就是富文本编辑器
image-20210304151016148
Flask 配置 Jinja2 自动转义所有值,除非显式地指明不转义。这就排除了模板导致的所有 XSS 问题,但是你仍需要在其它的地方小心

  • 生成 HTML 而不使用 Jinja2
  • 在用户提交的数据上调用了 Markup
  • 发送上传的 HTML 文件,永远不要这么做,使用 Content-Disposition: attachment 标头来避免这个问题
  • 发送上传的文本文件。一些浏览器使用基于开头几个字节的 content-type 猜测,所以用户可能欺骗浏览器执行 HTML

虽然 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);

SSTI:

当用户输入被串联到模板中而不是作为数据传递时,服务器端模板注入漏洞就会出现,简单来说也就是不正确的使用 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安全函数

flask 中有许多提高安全性的函数,比如以下几个

generate_password_hash(password)
# 将 password 变量的值进行加盐 hash 加密
check_password_hash(user.password, password)
# 与上个函数配合使用,用来将 加密数据与为加密数据做对比,如果未加密数据加密后为已加密数据则返回 true,否则返回 false
secure_filename(icon_filename)
# 将传入文件的文件名进行安全处理,如将空格替换为下划线等

CSRF:

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>
......                        

JSON安全:

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OceanSec

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值