Add global CSRF protection

This commit is contained in:
Lance Edgar 2016-12-14 18:37:17 -06:00
parent ab09314ed3
commit 4ed522ae47
15 changed files with 28 additions and 22 deletions

View file

@ -129,6 +129,9 @@ def make_pyramid_config(settings):
config.set_authentication_policy(SessionAuthenticationPolicy()) config.set_authentication_policy(SessionAuthenticationPolicy())
config.set_authorization_policy(TailboneAuthorizationPolicy()) config.set_authorization_policy(TailboneAuthorizationPolicy())
# always require CSRF token protection
config.set_default_csrf_options(require_csrf=True, token='_csrf')
# Bring in some Pyramid goodies. # Bring in some Pyramid goodies.
config.include('pyramid_beaker') config.include('pyramid_beaker')
config.include('pyramid_mako') config.include('pyramid_mako')

View file

@ -28,7 +28,7 @@ from __future__ import unicode_literals, absolute_import
from formencode import Schema from formencode import Schema
from .core import Form, Field, FieldSet, GenericFieldSet, invalid_csrf_token from .core import Form, Field, FieldSet, GenericFieldSet
from .simpleform import SimpleForm, FormRenderer from .simpleform import SimpleForm, FormRenderer
from .alchemy import AlchemyForm from .alchemy import AlchemyForm
from .fields import AssociationProxyField from .fields import AssociationProxyField

View file

@ -33,7 +33,6 @@ from pyramid.renderers import render
from webhelpers.html import HTML, tags from webhelpers.html import HTML, tags
from tailbone.db import Session from tailbone.db import Session
from tailbone.forms import invalid_csrf_token
class TemplateEngine(fa.templates.TemplateEngine): class TemplateEngine(fa.templates.TemplateEngine):
@ -114,8 +113,5 @@ class AlchemyForm(Object):
self.session.flush() self.session.flush()
def validate(self): def validate(self):
if invalid_csrf_token(self.request):
self.request.session.flash("Invalid CSRF token", 'error')
return False
self.fieldset.rebind(data=self.request.params) self.fieldset.rebind(data=self.request.params)
return self.fieldset.validate() return self.fieldset.validate()

View file

@ -33,17 +33,6 @@ from formalchemy.helpers import content_tag
from pyramid.renderers import render from pyramid.renderers import render
def invalid_csrf_token(request):
"""
Returns boolean indicating whether the given request has an *invalid* CSRF token.
"""
if request.method == 'POST':
csrf_token = request.session.get_csrf_token()
if request.POST.get('_csrf') != csrf_token:
return True
return False
class Form(object): class Form(object):
""" """
Base class for all forms. Base class for all forms.

View file

@ -33,7 +33,7 @@ from pyramid_simpleform import renderers
from webhelpers.html import tags from webhelpers.html import tags
from webhelpers.html import HTML from webhelpers.html import HTML
from tailbone.forms import Form, invalid_csrf_token from tailbone.forms import Form
class SimpleForm(Form): class SimpleForm(Form):
@ -53,9 +53,6 @@ class SimpleForm(Form):
return super(SimpleForm, self).render(**kwargs) return super(SimpleForm, self).render(**kwargs)
def validate(self): def validate(self):
if invalid_csrf_token(self.request):
self.request.session.flash("Invalid CSRF token", 'error')
return False
return self._form.validate() return self._form.validate()

View file

@ -34,7 +34,7 @@ from rattail.util import pretty_quantity
from webhelpers.html import * from webhelpers.html import *
from webhelpers.html.tags import * from webhelpers.html.tags import *
from tailbone.util import pretty_datetime from tailbone.util import csrf_token, pretty_datetime
def pretty_date(date): def pretty_date(date):

View file

@ -5,6 +5,7 @@
<div class="form"> <div class="form">
${h.form(url('change_password'))} ${h.form(url('change_password'))}
${form.csrf_token()}
${form.referrer_field()} ${form.referrer_field()}
${form.field_div('current_password', form.password('current_password'))} ${form.field_div('current_password', form.password('current_password'))}
${form.field_div('new_password', form.password('new_password'))} ${form.field_div('new_password', form.password('new_password'))}

View file

@ -23,6 +23,7 @@
<div class="form"> <div class="form">
${form.begin()} ${form.begin()}
${form.csrf_token()}
${form.hidden('user', value=request.user.uuid if request.user else None)} ${form.hidden('user', value=request.user.uuid if request.user else None)}
<p> <p>

View file

@ -36,6 +36,7 @@
<br /> <br />
${h.form(request.current_route_url())} ${h.form(request.current_route_url())}
${h.csrf_token(request)}
<div class="buttons"> <div class="buttons">
<a class="button" href="${form.cancel_url}">Whoops, nevermind...</a> <a class="button" href="${form.cancel_url}">Whoops, nevermind...</a>
<button type="button" id="confirm-delete">Yes, please DELETE this data forever!</button> <button type="button" id="confirm-delete">Yes, please DELETE this data forever!</button>

View file

@ -71,6 +71,7 @@ ${rows_grid|n}
<div id="execution-options-dialog" style="display: none;"> <div id="execution-options-dialog" style="display: none;">
${h.form(url('{}.execute'.format(route_prefix), uuid=batch.uuid), name='batch-execution')} ${h.form(url('{}.execute'.format(route_prefix), uuid=batch.uuid), name='batch-execution')}
${h.csrf_token(request)}
% if master.has_execution_options: % if master.has_execution_options:
${rendered_execution_options|n} ${rendered_execution_options|n}
% endif % endif

View file

@ -32,6 +32,7 @@
<div class="form"> <div class="form">
${h.form(request.current_route_url())} ${h.form(request.current_route_url())}
${h.csrf_token(request)}
<div class="field-wrapper"> <div class="field-wrapper">
<label for="batch_type">Batch Type</label> <label for="batch_type">Batch Type</label>
@ -54,6 +55,7 @@
<div class="form"> <div class="form">
${h.form(request.current_route_url())} ${h.form(request.current_route_url())}
${h.csrf_token(request)}
<div class="field-wrapper"> <div class="field-wrapper">
<label for="provider">Batch Type</label> <label for="provider">Batch Type</label>

View file

@ -7,6 +7,7 @@
<br /> <br />
${h.form(request.current_route_url())} ${h.form(request.current_route_url())}
${h.csrf_token(request)}
<div class="field-wrapper"> <div class="field-wrapper">
<label for="department">Department</label> <label for="department">Department</label>

View file

@ -5,6 +5,7 @@
<div class="timesheet-wrapper"> <div class="timesheet-wrapper">
${form.begin(id='filter-form')} ${form.begin(id='filter-form')}
${form.csrf_token()}
<table class="timesheet-header"> <table class="timesheet-header">
<tbody> <tbody>

View file

@ -283,6 +283,7 @@
<%def name="edit_form()"> <%def name="edit_form()">
${h.form(url('schedule.edit'), id='schedule-form')} ${h.form(url('schedule.edit'), id='schedule-form')}
${h.csrf_token(request)}
</%def> </%def>
<%def name="edit_tools()"> <%def name="edit_tools()">
@ -299,6 +300,7 @@ ${timesheet_wrapper(edit_form=edit_form, edit_tools=edit_tools, context_menu=con
${edit_tools()} ${edit_tools()}
${h.form(url('schedule.edit'), id="clear-schedule-form")} ${h.form(url('schedule.edit'), id="clear-schedule-form")}
${h.csrf_token(request)}
${h.hidden('clear-schedule', value='clear')} ${h.hidden('clear-schedule', value='clear')}
${h.end_form()} ${h.end_form()}
@ -318,6 +320,7 @@ ${h.end_form()}
and then new shifts will be created based on the week you specify. and then new shifts will be created based on the week you specify.
</p> </p>
${h.form(url('schedule.edit'), id='copy-schedule-form')} ${h.form(url('schedule.edit'), id='copy-schedule-form')}
${h.csrf_token(request)}
<label for="copy-week">Copy from week:</label> <label for="copy-week">Copy from week:</label>
${h.text('copy-week')} ${h.text('copy-week')}
${h.end_form()} ${h.end_form()}

View file

@ -31,11 +31,21 @@ import datetime
import pytz import pytz
import humanize import humanize
from webhelpers.html import HTML from webhelpers.html import HTML, tags
from rattail.time import timezone, make_utc from rattail.time import timezone, make_utc
def csrf_token(request, name='_csrf'):
"""
Convenience function. Returns CSRF hidden tag inside hidden DIV.
"""
token = request.session.get_csrf_token()
if token is None:
token = request.session.new_csrf_token()
return HTML.tag("div", tags.hidden(name, value=token), style="display:none;")
def pretty_datetime(config, value): def pretty_datetime(config, value):
""" """
Formats a datetime as a "pretty" human-readable string, with a tooltip Formats a datetime as a "pretty" human-readable string, with a tooltip