Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task: Add CSRF for Pylons and Flask. #112

Open
wants to merge 4 commits into
base: ckan_2.8
Choose a base branch
from
Open

Task: Add CSRF for Pylons and Flask. #112

wants to merge 4 commits into from

Conversation

boykoc
Copy link
Contributor

@boykoc boykoc commented Jan 10, 2020

CKAN is currently running both pylons and flask and migrating
towards only flask. Current CSRF approaches only address pylons
requests.

After trying numerous options this seemed like the most reliable and enables
easy throw away once pylons is completely gone from CKAN.

Basic concept is to run the original CSRF with very minor changes. Then use a
modified version to handle Flask.

I used 2 repos as my bases: ckanext-security and ckan-ex-qgov.

anti_csrf3.py is the modified middleware.py that breaks apart the classes
and modified for Flask. I liked most of how this version, ckanext-security,
was implemented which is why I used it instead of the ckan-ex-qgov.

ckan-ex-qgov I used as the base for pylons as it didn't
require middleware or modifying CKAN directly (middleware implementation).

File references:

  1. orig_anti_csrf.py was from https://github.com/qld-gov-au/ckan-ex-qgov/blob/master/ckanext/qgov/common/anti_csrf.py
  2. anti_csrf.py was from https://github.com/data-govt-nz/ckanext-security/blob/master/ckanext/security/anti_csrf.py
  3. anti_csrf3.py was from https://github.com/data-govt-nz/ckanext-security/blob/master/ckanext/security/middleware.py

[x] Update anti_csrf.py to work with Flask requests.
[x] Update plugin for above
[] Set cookie on flask responses

Intercept is still called but just updates the html with a token.
before_app_request handles validation of token matching.
setting the cookie in the response currently only works for pylons responses, or if a pylons response was first given then going to a flask response. Otherwise the response object doesn't exist yet.

Also have to set streaming=False in pylons_app.py.
…inst.

orig_anti_csrf.py was from https://github.com/qld-gov-au/ckan-ex-qgov/blob/master/ckanext/qgov/common/anti_csrf.py
anti_csrf.py was from https://github.com/data-govt-nz/ckanext-security/blob/master/ckanext/security/anti_csrf.py
anti_csrf3.py was from https://github.com/data-govt-nz/ckanext-security/blob/master/ckanext/security/middleware.py

I restarted my approuch and wanted to track the changes against the original files
to help track my changes against for myself and others in the future.

anti_csrf.py in my first WIP commit was actually a modified version of the orig_anti_csrf.py above but naming got away from me.
CKAN is currently running both pylons and flask and migrating
towards only flask. Current CSRF approuches only address pylons
requests.

After trying numerous options this seemed like the most reliable and enables
easy throw away once pylons is completely gone from CKAN.

Basic concept is to run the original CSRF with very minor changes. Then use a
modified version to handle Flask.

I used 2 repos as my bases: ckanext-security and ckan-ex-qgov.

anti_csrf3.py is the modified middleware.py that breaks apart the classes
and modified for Flask. I liked most of how this version, ckanext-security,
was implemented which is why I used it instead of the ckan-ex-qgov.

ckan-ex-qgov I used as the base for pylons as it didn't
require middleware or modifying CKAN directly (middleware implementation).
@@ -0,0 +1,143 @@
"""Provides a filter to prevent Cross-Site Request Forgery,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file really necessary when we have orig_anti_csrf.py?

log = logging.getLogger(__name__)


CSRF_ERR = 'CSRF authentication failed. Token missing or invalid.'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't used?


def is_secure():
# allow requests which have the x-forwarded-proto of https (inserted by nginx)
if request.headers.get('X-Forwarded-Proto') == 'https':

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Headers can be forged.

return True

# get/head/options/trace are exempt from csrf checks
return request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TRACE should probably be entirely blocked, not given special treatment. It actually enables some types of CSRF filter bypass, by exposing the token cookie.

return None
token = post_tokens[0]
# drop token from request so it doesn't populate resource extras
#del request.POST[anti_csrf.TOKEN_FIELD_NAME]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this commented out? Does it not apply to Flask requests?


CSRF_ERR = 'CSRF authentication failed. Token missing or invalid.'

domain = config.get('ckan.ontario_theme.csrf_domain', '')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Referrer checks should probably be optional, but if they're going to be implemented, shouldn't they be derived from ckan.site_url?

@ThrawnCA
Copy link

ThrawnCA commented Apr 29, 2021

@boykoc This is implemented in https://github.com/qld-gov-au/ckanext-csrf-filter/tree/develop as a stand-alone extension now. Comments there are welcome.

Thanks for developing this, it helped a lot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants