Skip to content
Tom Purucker edited this page Nov 9, 2017 · 1 revision

static code analyses

code climate

codacy


Cross Site Request Forgery Protection

Updated: 08/23/2017

Cross site request forgeries is where a malicious site is able to cause a user's browser to make POST requests to our servers hijacking a submitted form. CSRF attacks can be particuluar damaging for sites that have user accounts, users login, and where data on the server can be modified by that user. Additional CSRF information can be found here.

Django CSRF Protection

Csrf protection is enabled by default in django, where it is set in settings.py (or for qed settings_docker.py) under MIDDLEWARE_CLASSES as 'django.middleware.csrf.CsrfViewMiddleware'. Complete Django CSRF protection documentation can be found here.

If csrf protection is enabled, the token can be found in the request object under cookies. Even though the value of the csrf token is visible to a user, the token can not be hijacked because the token has a random salt added and then is scrambled.

Setup instructions:

The two primary cases where csrf tokens are used are in html form submissions and ajax post calls.

For html forms:

  • Insert '{% csrf_token %}' inside the form tags of your template file. Sample code from templates_qed/hms/04hms_input_start_drupal.html (lines 47-48)
	<form method="post" action="/hms/{{ MODEL }}/output/" name="input_table">
		{% csrf_token %}

Django will know to insert a hidden field into the form containing the csrf token.

  • For django to know what the csrf_token is, the request object has to be passed to the template when it is rendered in django. Sample code from hms_app/models/hydrology/hydrology_inputs.py (lines 19-24)
    html += render_to_string('04hms_input_start_drupal.html',
    		{
    			'MODEL': model,
    			'SUBMODEL': submodel,
    		},
    		request=request)

If this does not correct a csrf missing error, ensure that the request object you are passing into your render function contains the csrf cookie.

For ajax calls:

  • On the template page, the csrf cookie will need to be extracted from the cookies. Using the follow function:
    function getCookie(name) {
    	var cookieValue = null;
    	if (document.cookie && document.cookie !== '') {
    		var cookies = document.cookie.split(';');
    		for (var i = 0; i < cookies.length; i++) {
    			var cookie = jQuery.trim(cookies[i]);
    			if (cookie.substring(0, name.length + 1) === (name + '=')) {
    				cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
    				break;
    			}
    		}
    	}
    	return cookieValue;
    }			
    var csrftoken = getCookie('csrftoken');

The csrftoken variable needs to be a 'global' variable so that it can be assigned to the appropriate header value of your ajax call. The following code can be placed inside the $.ajaxSetup or in the $.ajax block. Multiple examples can be found in templates_qed/pisces/pisces_watershed_map.html

	beforeSend: function (xhr, settings) {
		xhr.setRequestHeader("X-CSRFToken", csrftoken);
	}

These two sections of code will take the csrf token from the requests.Cookies and insert the token into the request header for your ajax calls.

  • Just like the html form case, the request object needs to be provided to the render function for that template page so that django can build the html page with the csrf cookie.

In the case where your url endpoint does not need a csrf token, or you are unable to use one:

  • Import the following into your .py that the url is pointing to, the function receiving the POST request.
	from django.views.decorators.csrf import csrf_exempt
  • Decorate your function with
	@csrf_exempt

Example code can be found in cts_app/cts_api/views.py