How to restrict the ajax call from out side of the browser in django - ajax

i am working in a project, there is no user authentication and authorization. Bascially i am calling ajax in client side and it executes a view in django and return a json out. How can i validate this request is only coming from browser and how to restrict the if this not coming from the browser or any manual script?

You can use request.is_ajax() method
HttpRequest.is_ajax()
Returns True if the request was made via an XMLHttpRequest, by checking the HTTP_X_REQUESTED_WITH header for the string 'XMLHttpRequest'. Most modern JavaScript libraries send this header. If you write your own XMLHttpRequest call (on the browser side), you’ll have to set this header manually if you want is_ajax() to work.
If a response varies on whether or not it’s requested via AJAX and you are using some form of caching like Django’s cache middleware, you should decorate the view with vary_on_headers('HTTP_X_REQUESTED_WITH') so that the responses are properly cached.
docs
Im updating my answer to fit what we commented above
In your views
from django.core import signing
from django.views.generic import View, TemplateView
from django.http import HttpResponseBadRequest
class BrowserView(TemplateView):
template_name = 'yourtemplate.html'
def get_context_data(self, **kwargs):
ctx = super(BrowserView, self).get_context_data(**kwargs)
ctx['token'] = signing.dumps(self.request.session_id)
return ctx
class AjaxView(View):
def get(self, *args, **kwargs):
if self.request.is_ajax():
try:
sign = signing.loads(self.request.GET.get('token'), max_age=(20))
if sign == self.request.session_id:
## return ajax
return HttpResponseBadRequest('You are not authorized to see this page')
except signing.BadSignature:
return HttpResponseBadRequest('You are not authorized to see this page')
else:
return HttpResponseBadRequest('You are not authorized to see this page')
In your template
In this case I used a meta tag, but you get the idea
<meta name="validation" content="{{token}}" />
In your javascript
var t = document.querySelector("meta[name='validation']").getAttribute('content');
$.ajax({
url:'yoururl',
data: yourData + '&token=' + t,
type: 'get',
success: function(response){
// do whatever
},
error: function(e){
console.log(e);
}
});

I don't believe it's possible to 100% prevent this, but there are some things you can do:
a set-cookie header w/some unique ID on the page, but not on API responses.
if the cookie isn't received by your API, return a 401.
tracking API calls per unique ID could be a good indicator of "proper" usage.
associate IDs w/IPs.
the tracking metrics can be combined w/a threshold that blocks requests if exceeded.
you can check the referrer header (easy to spoof).
finally, lookup the is_ajax method of Django's, but this just checks for an XMLHttpRequest header (again, easy to spoof).

Related

springsecurity: using CSRF token obtained from CrossOrigin REST service to POST data

I have this REST service on domainA:
#CrossOrigin(origins={"http://domainB"})
#RequestMapping(value="/csrf", method=RequestMethod.GET)
public #ResponseBody
CsrfToken getCsrfToken(HttpServletRequest request) {
CsrfToken token = (CsrfToken)request.getAttribute(CsrfToken.class.getName());
return token;
}
Then I want to obtain CSRF token from above service (by using javascript on domainB) and add it to a <form action="http://domainA> on domainB and send this form to domainA (it is a simple form that has a submit button).
The problem is I get HTTP Status 403 – Forbidden.
As the opposite: when I manually set the _csrf value (obtained manually in the other browser tab pointing to domainA/csrf) in the <form action="http://domainA> and submit it then it works.
The difference which I noticed is that when I manually refresh browser's tab domainA/csrf then I get constantly the same value (and this value works), but when the domainA/csrf is obtained by the javascript from the domainB it is each time different and when using it - it does not work.
Can anyone help?
domainA: www.fridayweekend.com/rest/csrf
domainB: www.friwee.com/register (hit F12 and observe what call to www.fridayweekend.com/rest/csrf returns....)
As #dur said - the problem was in the JavaScript code. I used:
$.getJSON(domainA/csrf, callback)
which was ending up each time with a new session and a new CSRF token for it.
The solution was to use cors_ajax_call function except $.getJSON, defined as below:
var cors_ajax_call = function(address, callback){
$.ajax({
url: address,
context: document.body,
xhrFields: {
withCredentials: true
}
}).success(callback);
}
Thank you for your input! Hope this help someone :)

Django ajax warning on same page after DoesNotExist exception on form POST

I have a django form attached to a view. In the form a user types in a query which is passed to a Model.objects.get( query ) like so:
def post(self, request):
try:
Model.objects.get(query)
except Model.DoesNotExist:
# something here
Upon exception i'd like to send an ajax request to my template that stops it from refreshing, and displays a warning to the user that there's nothing in the database matching that get request. What would I put in the view and the template?
The http standard response would be a 404 response. Django has a shortcut function for this: get_object_or_404
def post(self, request):
my_object = get_object_or_404(Model, query)
If the lookup fails, django will raise an 404 error, which will result in a 404 http response back to the client. In your javascript ajax handling code, you should check the http status, and handle any 404 responses appropriately.
For example, if you are using the fetch api, the code might look like this.
fetch('/some/url/?query=foobar').then(response => {
if (response.ok) return response.json()
if (response.status == 404) throw new Error('404')
})

How to protect against CSRF on a static site?

I have a static website, being served from a CDN, that communicates with an API via AJAX. How do I protect against CSRF?
Since I do not have control over how the static website is served, I cannot generate a CSRF token when someone loads my static website (and insert the token into forms or send it with my AJAX requests). I could create a GET endpoint to retrieve the token, but it seems like an attacker could simply access that endpoint and use the token it provides?
Is there an effective way to prevent against CSRF with this stack?
Additional details: authentication is completely separate here. Some of the API requests for which I want CSRF protection are authenticated endpoints, and some are public POST requests (but I want to confirm that they are coming from my site, not someone else's)
I could create a GET endpoint to retrieve the token, but it seems like an attacker could simply access that endpoint and use the token it provides?
Correct. But CSRF tokens are not meant to be secret. They only exist to confirm an action is performed in the order expected by one user (e.g. a form POST only follows a GET request for the form). Even on a dynamic website an attacker could submit their own GET request to a page and parse out the CSRF token embedded in a form.
From OWASP:
CSRF is an attack that tricks the victim into submitting a malicious request. It inherits the identity and privileges of the victim to perform an undesired function on the victim's behalf.
It's perfectly valid to make an initial GET request on page load to get a fresh token and then submit it with the request performing an action.
If you want to confirm the identity of the person making the request you'll need authentication, which is a separate concern from CSRF.
My solution is as follows
Client [static html]
<script>
// Call script to GET Token and add to the form
fetch('https:/mysite/csrf.php')
.then(resp => resp.json())
.then(resp => {
if (resp.token) {
const csrf = document.createElement('input');
csrf.name = "csrf";
csrf.type = "hidden";
csrf.value = resp.token;
document.forms[0].appendChild(csrf);
}
});
</script>
The above can be modified to target a pre-existing csrf field. I use this to add to may pages with forms. The script assumes the first form on the page is the target so this would also need to be changed if required.
On the server to generate the CSRF (Using PHP : assumes > 7)
[CSRFTOKEN is defined in a config file. Example]
define('CSRFTOKEN','__csrftoken');
Server:
$root_domain = $_SERVER['HTTP_HOST'] ?? false;
$referrer = $_SERVER['HTTP_REFERER'] ?? false;
// Check that script was called by page from same origin
// and generate token if valid. Save token in SESSION and
// return to client
$token = false;
if ($root_domain &&
$referrer &&
parse_url($referrer, PHP_URL_HOST) == $root_domain) {
$token = bin2hex(random_bytes(16));
$_SESSION[CSRFTOKEN] = $token;
}
header('Content-Type: application/json');
die(json_encode(['token' => $token]));
Finally in the code that processes the form
session_start();
// Included for clarity - this would typically be in a config
define('CSRFTOKEN', '__csrftoken');
$root_domain = $_SERVER['HTTP_HOST'] ?? false;
$referrer = parse_url($_SERVER['HTTP_REFERER'] ?? '', PHP_URL_HOST);
// Check submission was from same origin
if ($root_domain !== $referrer) {
// Invalid attempt
die();
}
// Extract and validate token
$token = $_POST[CSRFTOKEN] ?? false;
$sessionToken = $_SESSION[CSRFTOKEN] ?? false;
if (!empty($token) && $token === $sessionToken) {
// Request is valid so process it
}
// Invalidate the token
$_SESSION[CSRFTOKEN] = false;
unset($_SESSION[CSRFTOKEN]);
There is very good explanation for same, Please check
https://cloudunder.io/blog/csrf-token/
from my understanding it seems static site won't face any issue with CSRF due to CORS restriction, if we have added X-Requested-With flag.
There is one more issue i would like to highlight here, How to protect your api which is getting called from Mobile app as well as Static site?
As api is publicly exposed and you want to make sure only allowed user's should be calling it.
There is some check we can add at our API service layer for same
1) For AJAX request(From Static site) check for requesting domain, so only allowed sites can access it
2) For Mobile request use HMAC token, read more here
http://googleweblight.com/i?u=http://www.9bitstudios.com/2013/07/hmac-rest-api-security/&hl=en-IN

Is Ajax POST an acceptable technique for changing server state?

I am designing a new website and I am considering using AJAX post requests for better user experience. Is using AJAX POST requests for changing server state an acceptable design practice? Are their any security concerns in using AJAX POST requests? Is it recommended to restrict the server state changes to HTTP POST only?
EDIT
I am using ASP.NET MVC web framework for implementation.
Post, Put, Patch and Delete (although the last one is barely used) are all request types that traditionally alter the server state.
In order to answer your question, it is important to consider which framework you are using, as each one might have different best practices.
From a technical point of view, they all do practically the same, they only have different semantic meanings and conventions attached to them. If you were to use Post for everything, I doubt that anybody would complain
Post back is traditional way to doing things on web application where whole page re-load on form submission. In this approach most of the codes runs at sever side.
AJAX is a modern way to building web application where most of the code runs at client side for better performance and user experience. Only required data post to server instead of posting whole page.
Post back & Ajax both create HTTP request so it is not right to say one is less secure than other. In both request attacker can inject script using cross-site scripting (XSS) or CSRF (Cross-site request forgery).
AJAX calls are itself protect CSRF using “Common Origin Policy” when CORS is disabled and JSONP requests are blocked. To prevent CSRF attack one step ahead, you can implement Anti Forgery token like in MVC framework. AJAX calls can be called from web application as well as from MVC.
In MVC, #html.antiforgerytoken() can be called on form load which store one key in hidden field and other key in cookie and using ValidateAntiForgeryToken filter, we can validate that CSRF token. The form token can be a problem for AJAX requests, because an AJAX request might send JSON data, not HTML form data. One solution is to send the tokens in a custom HTTP header.
Here is sample code snippet for more details…
Sample Server side Code to generate Anti forgery token.
/// <summary>
/// Get Anti Forgery token
/// </summary>
/// <returns></returns>
public static string GetAntiXsrfToken()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
var responseCookie = new HttpCookie("__AntiXsrfToken")
{
HttpOnly = true,
Value = cookieToken
};
if (FormsAuthentication.RequireSSL && HttpContext.Current.Request.IsSecureConnection)
{
responseCookie.Secure = true;
}
HttpContext.Current.Response.Cookies.Set(responseCookie);
return formToken;
}
Sample Server side Code to validate Anti forgery token.
/// <summary>
/// Validate Anti Forgery token coming from secure cookie & request header
/// </summary>
static void ValidateAntiXsrfToken()
{
string tokenHeader, tokenCookie;
try
{
// get header token
tokenHeader = HttpContext.Current.Request.Headers.Get("__RequestVerificationToken");
// get cookie token
var requestCookie = HttpContext.Current.Request.Cookies["__AntiXsrfToken"];
tokenCookie = requestCookie.Value;
AntiForgery.Validate(tokenCookie, tokenHeader);
}
catch
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.StatusCode = 403;
HttpContext.Current.Response.End();
}
}
Sample code to get Anti forgery token (one part) and save into hidden field
<input name="__RequestVerificationToken" type="hidden" value="<%= CommonUtils.GetAntiXsrfToken() %>" />
Sample client side code to pass one part to Anti Forgery token into request header from hidden field and another part will go automatically from client cookie if request is generated from same origin.
function CallServer(baseUrl, methodName, MethodArgument, callback) {
$.ajax({
type: "POST",
url: baseUrl + methodName,
data: MethodArgument,
contentType: "application/json; charset=utf-8",
async: false,
dataType: "json",
headers: {'__RequestVerificationToken': $("input[name='__RequestVerificationToken']").val()
},
success: function (data) {
if (callback != undefined && typeof (callback) === "function") {
callback(data.d);
}
},
error: function (data) {
if (data.status == 401 || data.status == 403)
window.location.href = "../Common/accessdenied";
else if (data.status == 419) {
displayUserMessage(commonMessage.RE_SESSIONINFO_NOT_FOUND, true);
window.location.href = "../Common/logout";
}
else
displayUserMessage(commonMessage.SERVICE_NOT_RESPONDING, true);
}
});
}
Finally, Call ValidateAntiXsrfToken() function before processing the each AJAX request at server side.
You can find more details here…
Which one is better? Ajax post or page post[Controller httppost] when only one form is there in a page?
http://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet

Redirect user in Grails if browsing an action

I have a Grails controller action that is used for Ajax purposes, though you can still navigate and view the page in the browser.
class QuoteController {
def quoteService
/**
* This page uses the ajaxRandom function defined below to display random quotes.
*/
def random = {
def randomQuote = quoteService.getRandomQuote()
[quote:randomQuote]
}
/**
* I do not want this to be a valid page, but maintain its use as a simple Ajax method.
*/
def ajaxRandom = {
def randomQuote = quoteService.getRandomQuote()
response.outputStream << "<q>${randomQuote.content}</q><p>${randomQuote.author}</p>"
}
}
Is there a way to redirect if someone visits the URL via browser while maintaining the method's Ajax functionality from within a page?
def ajaxRandom = {
if(!request.xhr) { // this calls the dynamic method request.isXhr()
redirect action: 'random'
} else {
def randomQuote = quoteService.getRandomQuote()
response.outputStream << "<q>${randomQuote.content}</q><p>${randomQuote.author}</p>"
}
}
This works because most of the Ajax JS Libraries add the X-Requested-With header to the request. Grails add this isXhr() method dynamically to the HttpServletRequest class.
// test whether the current request is an XHR request
HttpServletRequest.metaClass.isXhr = {->
'XMLHttpRequest' == delegate.getHeader('X-Requested-With')
}
A simple way is to append a param to the url when calling it via ajax e.g. ?ajax=true
Then check for it and redirect if it's not there (such as when a use hits it with their browser).
If that is too easy to work around, inspect the request to see what is different between a browser request and an ajax request.
cheers
Lee
If you AJAX requests are always POSTS then you could check the method and assume a POST is an AJAX call because it's pretty hard for the average user to create a POST accidentally, where as they can always GET any URL (if they know of it)
Hope this helps.

Resources