Skip to content

Added a CodeQL query and tests for Django CSRF protection check. - #3296

Closed
Dhayalanb wants to merge 5 commits into
github:mainfrom
Expl0itlabs:DjangoCSRF
Closed

Added a CodeQL query and tests for Django CSRF protection check.#3296
Dhayalanb wants to merge 5 commits into
github:mainfrom
Expl0itlabs:DjangoCSRF

Conversation

@Dhayalanb

Copy link
Copy Markdown

When a Django project is created, the CSRF middleware is activated by default in the MIDDLEWARE setting, thereby providing CSRF protection to all the views. It is also possible to disable this to make development easier and unless decorators such as csrf_protect() is used to protect the every single critical views, the application will be vulnerable to Cross-Site Request Forgery (CWE-352).

CSRF Protection can be enabled/ Disabled by adding/ removing the django.middleware.csrf.CsrfViewMiddleware from the MIDDLEWARE variable.

This PR adds a CodeQL query with unit tests, which checks if any forms of CSRF prevention mechanism is enabled.

CSRF mechanism tested:

  1. Presence of django.middleware.csrf.CsrfViewMiddleware in MIDDLEWARE variable.
  2. Presence of any decorators such as requires_csrf_token(), ensure_csrf_cookie(), csrf_protect()
  3. Presence of import of CsrfViewMiddleware class

@RasmusWL RasmusWL left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for your submission ✨ 🚀

I'm quite busy at the moment, so here are a few comments to get this PR moving forwards.

I would also like to see one example for vulnerable and one for not-vulnerable code. Could be great if you could include examples that are using the requires_csrf_token, ensure_csrf_cookie, and csrf_protect decorators.

Please also read our contributing guidelines and make sure you target the PR to add the new query to the experimental directory.

Comment thread python/ql/src/Security/CWE-352/DjangoCSRF.ql Outdated
Comment thread python/ql/src/Security/CWE-352/DjangoCSRF.ql Outdated
Comment thread python/ql/src/Security/CWE-352/DjangoCSRF.ql Outdated

@RasmusWL RasmusWL left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for updating your PR so fast 👍 I have been super swamped with other things, so thanks for your patience 😊

There are a few things I think we need to fix, but after an iteration or two, we should be ready to merge this query into our experimental set of queries.

One point is that using toString() in the where clause is strictly forbidden by our contributing guidelines (4. Compilation) -- usually it is also an indication of trying to write ql in a non-idiomatic way. Not to worry, we'll sort it out together 😜

Comment thread python/ql/src/experimental/Security/CWE-352/DjangoCSRF.ql Outdated
Comment thread python/ql/src/experimental/Security/CWE-352/DjangoCSRF.ql Outdated
Comment thread python/ql/src/experimental/Security/CWE-352/DjangoCSRF.ql Outdated
Comment thread python/ql/src/experimental/Security/CWE-352/DjangoCSRF.ql Outdated
@felicitymay
felicitymay removed their request for review May 4, 2020 08:46
@felicitymay

Copy link
Copy Markdown
Contributor

Removing the automatic request for review by a member of the docs team now that this pull request targets the experimental directory.

@Dhayalanb
Dhayalanb requested a review from a team as a code owner May 7, 2020 15:31
@RasmusWL

Copy link
Copy Markdown
Member

Thanks for making these updates! 👍

I ran your query against all python projects on LGTM.com (before the changes in 8b4b3bc), and after looking through some results, I found it quite hard to see if the alert was a real problem (true positive -- TP) or if it actually didn't matter (false positive -- FP).

For example:

  • The alert is a FP. Since this project doesn't require authentication for anything, you will not be able to perform any CSRF attacks.
  • This alert is probably a FP, since this is the settings for tests of a django library -- and we can't assume that the functionality of all django libraries will need to concern CSRF protection.
  • This alert is a FP, since it is for the default settings in a vendored version of the django library.

Do you happen to have an example of this problem in real code? Requirement 5 for submitting a new query says:

The query must have at least one true positive result on some revision of a real project.

Moving this query forwards

I think we have two options. Either we merge this query as-is with a fairly bad FP-rate (should be reflected in the @precision metadata); or we continue working on improving this query (which might be a lengthy process).

I'm thinking what could really improve this query is if it could highlight the exact location where I should have been using CSRF protection and I didn't. This requires some way to figure out when I should have been using CSRF protection, which is hard to get 100% correct.

My intuitive suggestion would be to look for non CSRF protected handling of state-changing HTTP requests such as POST, DELETE, etc., and flag the ones that require being authenticated. This will ignore some useful results, such as (1) wanting to allow form submission from anonymous users only after they visit your webpage, and (2) django applications that use GET requests for state-changes. My intuitive understand is that both would be a fair tradeoff.

But the decision is up to you, depending on how enthusiastic you are about working more on this query 😉

@RasmusWL RasmusWL left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A few minor things about the qhelp

Comment on lines +27 to +31
<code>
@csrf_protect
def index(request):
return HttpResponse("Hello, world.")
</code>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you turn this into using the <sample src="<name>.py" /> pattern?

I would also like you to substitute the "hello world" example with something that actually requires CSRF protection. Maybe something like this :)

@csrf_protect
def change_password(request):
    ...

<!DOCTYPE qhelp SYSTEM "qhelp.dtd">
<qhelp>
<overview>
<p>ACross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
<p>ACross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions
<p>A Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions


<p> When a Django project is created the CSRF middleware is activated by default in the MIDDLEWARE setting,
thereby providing CSRF protection to all the views. If disabled , which is not recommended,
csrf_protect() can be used on particular views that needs to be protected</p>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
csrf_protect() can be used on particular views that needs to be protected</p>
<code>csrf_protect()</code> can be used on particular views that needs to be protected</p>


<sample src="CSRFMiddleware.py" />

<p>The corrected version does not include django.middleware.csrf.CsrfViewMiddleware, which

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
<p>The corrected version does not include django.middleware.csrf.CsrfViewMiddleware, which
<p>The corrected version does not include <code>django.middleware.csrf.CsrfViewMiddleware</code>, which

@adityasharad
adityasharad changed the base branch from master to main August 14, 2020 18:34
@ghost

ghost commented Apr 26, 2021

Copy link
Copy Markdown

@Dhayalanb @RasmusWL Do you mind if I take this up?

@RasmusWL

RasmusWL commented Apr 27, 2021

Copy link
Copy Markdown
Member

I'm going to close the PR now due to inactivity. I'm not aware of such instances of a PR getting stale and someone else picking up the threads @porcupineyhairs, can we please take this conversation on the Security Lab slack? 😊

@RasmusWL RasmusWL closed this Apr 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants