保护bottle.py web应用程序免受XSS和CSRF攻击的方法



我正在使用bottle.py, Beaker会话和自定义AAA编写模块编写web应用程序,因为许多我担心安全性和最好的方法来防止一些像我提到的有针对性的攻击。

作为一个例子,我有以下代码:

@route('/manage/adddomain',method='POST')
def adddomain():
    #This checks if user has enough power to create a domain
    aaa.require(50,'/forbidden') 
    user = aaa.getusername() # This is retrieved from a server side session
    domainname = request.forms.get('domain')
    description = request.forms.get('description')
    # Additional checks are performed in the sql module 
    # to protect against forged requests with valid login
    return sql.createdomain(user,domainname,description)

你会做哪些额外的检查来保护你的web应用程序?

Blender几乎涵盖了您需要的内容,但我想添加另一个方法。不需要检查每个POST,您可以添加这样的包装器:

def wrap_requires_csrf(*methods):
    def wrapper(fn):
        @wraps(fn)
        def wrapped(*args, **kwargs):
            if request.method in methods:
                if request.method == 'POST':
                    csrf = request.form.get('csrf')
                elif request.method == 'GET':
                    csrf = request.args.get('csrf')
                if not csrf or csrf != session.get('csrf'):
                    abort(400)
                session['csrf'] = generate_csrf_token()
            return fn(*args, **kwargs)
        return wrapped
    return wrapper
@app.route('/some/page', methods=['GET','POST'])
@wrap_requires_csrf('POST')
def some_page():
    ...

然后,在模板中,您将使用

提供隐藏字段
<input name="csrf" type="hidden" name="{{session.csrf}}" />

您需要在每个重要的表单字段中包含一个CSRF令牌,并使用模板引擎清理所有输出。

下面是一个Flask代码片段,你可以把它应用到你的Bottle应用中:

对付CSRF攻击的常用技术是添加一个随机字符串属性中的隐藏字段检查该字符串职位。

@app.before_request
def csrf_protect():
    if request.method == "POST":
        token = session.pop('_csrf_token', None)
        if not token or token != request.form.get('_csrf_token'):
            abort(403)
def generate_csrf_token():
    if '_csrf_token' not in session:
        session['_csrf_token'] = some_random_string()
    return session['_csrf_token']
app.jinja_env.globals['csrf_token'] = generate_csrf_token    

然后在你的模板中:

<form method=post action="">
    <input name=_csrf_token type=hidden value="{{ csrf_token() }}">

至于清理,这取决于您的模板引擎。Jinja2具有eescape过滤器:

<h2>No results for {{ search_query|escape }}</h2>

我将@Blender的解决方案改编为瓶子,如下:

from string import ascii_letters, digits
from random import choice
from bottle import Bottle, request, Jinja2Template, abort
app = Bottle()
@app.hook('before_request')
def csrf_protect():
    if request.method == 'POST':
        sess = request.environ['beaker.session']
        req_token = request.forms.get('csrf_token')
        # if no token is in session or it doesn't match the request one, abort
        if 'csrf_token' not in sess or sess['csrf_token'] != req_token:
            abort(403)
def str_random(length):
    '''Generate a random string using range [a-zA-Z0-9].'''
    chars = ascii_letters + digits
    return ''.join([choice(chars) for i in range(length)])
def gen_token():
    '''Put a generated token in session if none exist and return it.'''
    sess = request.environ['beaker.session']
    if 'csrf_token' not in sess:
        sess['csrf_token'] = str_random(32)
    return sess['csrf_token']
# allow access of the token generator using csrf_token
Jinja2Template.defaults['csrf_token'] = gen_token

默认情况下,Bottle没有附带会话,所以我使用Bottle -beaker。

需要包含在每个表单中的输入:

<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> 

最新更新