Resolving IDOR in Asp.net MVC and Knockout js application - asp.net-mvc

We have an application which is already in production where an IDOR vulnerability was detected in an endpoint "CustomerKey". This contains customer ID which when used brute force allowed for session take over in our app.
Now we can't encrypt this as it might make the app slow as the CustomerKey is being used many times in the JS as well as backend. Our app allows many users to login at the same time we are using signalR in the app as well.
Some sample backend code where this CustomerKey is being used :
[HttpPost]
[AllowAnonymous]
public JsonResult UpLoadLog(string CustomerKey, string message)
{
try
{
var log = message.Split(new string[] { "\r\n" },
StringSplitOptions.RemoveEmptyEntries);
foreach (string s in log)
{
Logger.Write(LogType.Info, this.GetType(), "UpLoadLog", CustomerKey + ": "
+ s.TrimStart('\r', '\n'), null);
}
}
catch (Exception ex)
{
Logger.Write(LogType.Fatal, this.GetType(), "UpLoadLog", ex.Message, ex);
}
HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.OK;
return null;
}
[AuthorizeUser]
public ActionResult Customer(string CustomerKey, string FeedData, string FeedInfo)
{
if (string.IsNullOrEmpty(CustomerKey))
{
var owinContext = Request.GetOwinContext();
CustomerKey = owinContext.Get<string>("CustomerKey");
FeedData = owinContext.Get<string>("FeedData");
FeedInfo = owinContext.Get<string>("FeedInfo");
}
ViewBag.CustomerKey = CustomerKey;
ViewBag.FeedData = FeedData;
ViewBag.FeedInfo = FeedInfo;
ViewBag.UseSignalR = true;
ViewBag.isOffline = true;
return View("Offline", "CustomerLayout");
}
Sample js code where CustomerKey is being used:
var UpLoadLog = function (log, isAsync) {
if (isAsync== null || isAsync== undefined)
isAsync= true;
jQuery.ajax({
type: "POST",
async: isAsync,
contentType: "application/json;charset=utf-8",
url: rooturl + "Authentication/UpLoadLog",
data: JSON.stringify({ CustomerKey: jQuery("#customerKey").val(), message: "\r\n\r\n" + log + "\r\n\r\n" }),
dataType: "json",
success: function (response, status, jqXHR) {
},
error: function (jqXHR, status, error) {
}
});
LogMessages = "\r\n\r\n";
};
The app also contains a hidden field in the layout where the value of CustomerKey is being used
<input type="hidden" id="customerkey" value="#ViewBag.CustomerKey"/>
What I need help is how can I resolve this vulnerability without making huge changes in the application?

If I understood the problem correctly, then it lies in the fact that anyone can send a request with any CustomerKey, and get a response by CustomerKey, even if they are not authorized to receive such information.
If the information about the CustomerKey is associated with authorization filters (AuthorizeUser), then, in my opinion, the least time-consuming way to solve the problem would be to compare the CustomerKey with the corresponding property of the authorized user. And if the keys don't match, throw an exception.
To talk about specific lines of code, I need, in fact, to work with the project and have access to all the source code.

You could enter a third parameter which is the md5() of the CustomerKey parameter. This way when the method is contacted, it checks if the md5 parameter is really the md5 of the CustomerKey parameter, if so it writes the log. It is a sort of checksum made on the first parameter which guarantees that only those who know this rule can save logs. This can be an example of the new method signature
[HttpPost]
[AllowAnonymous]
public JsonResult UpLoadLog(string CustomerKey, string message, string md5)
This instead a way to calculate md5:
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
{
byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
byte[] hashBytes = md5.ComputeHash(inputBytes);
string calculatedMd5 = Convert.ToHexString(hashBytes);
}

Related

Request returns 200 on Postman but 404 testing on website

I have built a simple Api that returns a few places with associated data, and everything works fine, but i have a more specific research method that also is working fine in testing phase using Postman. I've made a few requests in my web application and they receive expected result, but now I've come to the point where I need to call this method I made, which returns proper response on Postman but 404 on my web application when I log the response message.
This is how I make my request:
<script>
function getCities() {
var selectedRegion = document.getElementById("region_select").value;
$.ajax({
type: "GET",
url: '#Url.Action("ShowCitiesByRegion", "ClientTourism")',
accepts: "/",
data: {
regionId: selectedRegion
},
success: function (data) {
$('#city_select_div').html(data);
},
failure: function (response) {
console.log(response.responseText);
},
});
console.log(selectedRegion);
}
</script>
This request calls up my controller, which looks like this:
public async Task<IActionResult> ShowCitiesByRegion(int regionId)
{
System.Diagnostics.Debug.WriteLine("ID DA REGIAO-" + regionId);
List<City> cities = new List<City>();
HttpClient httpClient = _api.Initial();
HttpResponseMessage httpResponseMessage = await httpClient.GetAsync("​api​/Cities​/region_cities​/region=" + regionId);
if (httpResponseMessage.IsSuccessStatusCode)
{
var response = httpResponseMessage.Content.ReadAsStringAsync().Result;
cities = JsonConvert.DeserializeObject<List<City>>(response);
System.Diagnostics.Debug.WriteLine("Cidades-" + cities);
}
else
{
System.Diagnostics.Debug.WriteLine("Erro" + httpResponseMessage);
}
return PartialView(cities);
}
This controller calls up the api and it never gets a SuccessStatusCode.
My API console run never indicates it is receiving the request, except when its coming from Postman.
I've narrowed the problem to the request making, although I checked every variable and every data is passing through view to controller as expected. It returns this response message:
Cant seem to figure why I am always getting 404 while doing this request from my web application.
Try to write the complete url in httpclient:
"https://hostname/api​/Cities​/region_cities​/region=" + regionId

Calling action method through AJAX throwing exception to return view

When I am doing AJAX request to controller, Action Method could not find the View which I am trying to return and throwing the exception as shown at end of the question.
Following is the AJAX method calling the Action Method:
$.ajax({
url: "/Courses/AddTime",
data: { DayName: DayValue, StartTimeValue:
starttimeval,EndTimeValue: EndtimeVal },
dataType: "json",
type: "POST",
error: function () {
alert(" An error occurred.");
},
success: function (data) {
window.location.href = '/Courses/listbatch/?BtchTimingid=' +
data.ID + "&InsertRetId=" + data.secndid;
}
});
Following is Action Method and it is called properly from AJAX request but while returning View it is throwing exception.
public ActionResult listbatch(string Search_name, int? page, int?
BtchTimingid = 0, int InsertRetId=0)
{
/// There Have some code which is working perfect
if (Request.IsAjaxRequest())
return PartialView("_BatchTimingList", model.pageCoursesList);
else
return View(model);
}
Exception Screenshot
The view 'listbatch' or its master was not found or no view engine
supports the searched locations. The following locations were
searched:
~/Views/Courses/listbatch.aspx
~/Views/Courses/listbatch.ascx
~/Views/Shared/listbatch.aspx
~/Views/Shared/listbatch.ascx
~/Views/Courses/listbatch.cshtml
~/Views/Courses/listbatch.vbhtml
~/Views/Shared/listbatch.cshtml
~/Views/Shared/listbatch.vbhtml
I had the same problem and got a solution for that after spending 2 days.
Instead of using
window.location.href = '/Courses/listbatch/?BtchTimingid=' +
data.ID + "&InsertRetId=" + data.secndid;
I suggest you to use
window.location.href = '#Html.Raw(Url.Action("listbatch","Courses",new{BtchTimingid=data.ID, InsertRetId = data.secndid}))
If you use it without #Html.Raw, your URL will have &amp instead of & for parameters, it shouldn't cause any problem but i don't know why it caused for me and got the same problem that you have i-e The view was not found... So use Html.Raw as well.
Your view is missing.
public ActionResult listbatch(string Search_name, int? page, int? BtchTimingid = 0, int InsertRetId=0)
{
if (Request.IsAjaxRequest())
return PartialView("_BatchTimingList", model.pageCoursesList);
else
return View(model); // <======= HERE (not an AJAX request) =========
}
The following JavaScript does not generate an Ajax request, its a normal GET.
window.location.href = '/Courses/listbatch/?BtchTimingid=' +
data.ID + "&InsertRetId=" + data.secndid;
So View(model) expects to find listbatch.cshtml but cannot (presumably because it is missing) and you get the error message.
You need to create /Views/Courses/listbatch.cshtml
Have a look in the Views folder, see if it is there...

Backbone.js: POST request with empty value

I am trying to make a POST request.
Here my code:
var myModel = new MydModel({
content: "ciao"
});
console.log(myModel.get("content")); // "ciao"
myModel.save();
If I look to the network activity it looks like this:
The response part {id:0, content:"", ……}
In the header part: Request Payload {"content":"ciao"}
Here my model:
define([], function () {
var MyModel = Backbone.Model.extend({
url: function url ()
{
return "http://localhost/users";
}
});
return MyModel;
});
Is it my problem or is it in the server part?
send/receive vs request/response
a server receives requests and sends responses
a client sends requests and receives responses
in short
if {id:0, content:"", ……} (the response) is wrong, it's your server
if {"content":"asdasdsa"} (the request) is wrong, it's your client
There is little problem with receiving JSON-payload that "Backbone-client" sends to your Apache-server.
All you need to do is to manually parse JSON-payload from input on the server side ("php://input", for PHP), like this:
if($_SERVER['REQUEST_METHOD'] == 'PUT' || $_SERVER['REQUEST_METHOD'] == 'POST') {
$postStr = file_get_contents("php://input");
//json_decode throws error or returns null if input is invalid json
try {
$json = json_decode($postStr, true);
if(empty($json)) {
throw new Exception("Not valid json");
}
//must not be json, try query str instead
} catch(Errfor $e) {
$postVars = parse_str($postStr);
foreach($postVars as $key=>$data) {
$_POST[$key] = $data;
}
}
}
Full explanation you can find here:
http://colinbookman.com/2014/04/08/php-puts-posts-and-backbone-js/

XMLHttpRequest different in IE8 vs. FireFox/Chrome

I'm having a problem similar to jQuery $.ajax Not Working in IE8 but it works on FireFox & Chrome, but with a different use case.
I'm using the jQuery Form plug-in to handle a file upload to an ASP.NET MVC controller, which sends the file off for parsing and processing. If an Exception is thrown, it should alert the user to the issue.
//client side code
//make an ajax call, sending the contents of the file
$("#ajaxUploadForm").ajaxSubmit({
dataType: 'json',
url: '/plan/Something/ExcelImport',
iframe: true,
beforeSend: function () {
$(".ui-dialog-buttonpane").find("#my_progress").fadeIn();
},
success: function (data, textStatus) {
output = "<center><span class='flash'>" + data.message + "</span></center>";
$("#flash_message").html(output).fadeIn('slow');
setTimeout(function () { $("#flash_message").fadeOut() }, 5000);
cleanup();
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("XMLHttpRequest is " + XMLHttpRequest);
var contents = "";
for (prop in XMLHttpRequest) {
contents += "\na property is " + prop + " it's value is " + XMLHttpRequest[prop];
}
alert("the contents are " + contents);
alert("textStatus is " + textStatus);
alert("errorThrown is " + errorThrown);
//comes back in an HTML envelope. This should be parsed with regex, but I can't get it to work. Dirty hack
response = XMLHttpRequest.responseText.substring(XMLHttpRequest.responseText.indexOf("<body>"));
response = response.replace("<body>", "");
response = response.replace("</body>", "");
alert("There was a problem with the upload.\r\n" + response);
},
complete: function (XMLHttpRequest, textStatus) {
$(".ui-dialog-buttonpane").find("#my_progress").remove();
something_import.dialog('close');
something_import.dialog('destroy');
}
});
//server side code
public FileUploadJsonResult ExcelImport()
{
FileUploadJsonResult result = new FileUploadJsonResult();
HttpPostedFileBase hpf = Request.Files[0] as HttpPostedFileBase;
if (hpf.ContentLength == 0)
return new FileUploadJsonResult { Data = new { message = "File contained no data" } };
String fileName = Path.GetFileName(hpf.FileName);
String timeStampedFile = fileName.Insert(fileName.IndexOf('.'),"_"+DateTime.Now.ToFileTimeUtc());
string savedFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "tempo", timeStampedFile);
hpf.SaveAs(savedFileName);
try
{
result = ProcessFile(savedFileName, Request["Id"]) as FileUploadJsonResult;
}
catch (ArgumentException e)
{
this.Response.StatusCode = 500;
this.Response.StatusDescription = System.Net.HttpStatusCode.BadRequest.ToString();
Response.Write(e.Message);
result = Json(new { message = e.Message, stackTrace = e.StackTrace }) as FileUploadJsonResult;
}
return result;
}
This works perfectly in Chrome and Firefox. In IE, the XMLHttpRequest object coming back is different:
FF:
IE:
I've been Googling around for differences between the browser implementations of XMLHttpRequest, but haven't found anything that deals specifically with this case. Stymied.
The reason this is happening is because of the iframe fallback strategy that ajaxSubmit employs. I think since the response gets posted into the iframe IE tries to figure out how to dipslay it and decides that it wants to ask you to download the response instead of just putting it in the iframe.
I came across this same situation a while ago and found an article (that I can't find now) that offered a workaround.
If you surround your json response in a textarea nobody is going to complain(IE,FF,Chrome,probably Safari) and you'll get your response parsed correctly.
E.g. if you are returning
{Id: 1, Name: 'Me'}
just return:
<textarea>{Id: 1, Name: 'Me'}</textarea>
You see now IE thinks it's html so it inserts it into the hidden iframe. Your ajaxSubmit function still gets called and parses the json correctly and then everybody's happy. :)
If you're using ASP.NET MVC you could shamelessly copy this extension method :)
public static class ControllerExtensions
{
public static ActionResult JsonSafe(this IController controller, object obj)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return new WriteResult(string.Format("<textarea>{0}</textarea>", serializer.Serialize(obj)));
}
}
The wikipedia article on XMLHttpRequest seems to give a good overview of the history behind the XMLHttpRequest. It seems Microsoft and Mozilla developed/adopted their own versions of the object and hence why you are probably seeing different properties.
Here is a link to Microsoft's implementation of the XMLHttpRequest interface members, which seem to match the properties in your alert.
Here is the a link to Mozilla's implementation of XMLHttpRequest.
So while we wait for the W3C to standardize the XMLHttpRequest you will continue to have different implementations across the browsers like you are seeing in this case.
For some added fun here is Apple's and Opera's specifications on XMLHttpRequest.

jquery load returns empty, possible MVC 2 problem?

I have a site that need to get some data from a different sit that is using asp.net MVC/
The data to get loaded is from these pages:
http://charity.hondaclassic.com/home/totaldonations
http://charity.hondaclassic.com/Home/CharityList
This should be a no brainer but for some reason I get an empty response, here is my JS:
<script>
jQuery.noConflict();
jQuery(document).ready(function($){
$('.totalDonations').load('http://charity.hondaclassic.com/home/totaldonations');
$('#charityList').load('http://charity.hondaclassic.com/home/CharityList');
});
</script>
in firebug I see the request is made and come back with a response of 200 OK but the response is empty, if you browse to these pages they work fine! What the heck?
Here are the controller actions from the MVC site:
public ActionResult TotalDonations() {
var total = "$" + repo.All<Customer>().Sum(x => x.AmountPaid).ToString();
return Content(total);
}
public ActionResult CharityList() {
var charities = repo.All<Company>();
return View(charities);
}
Someone please out what stupid little thing I am missing - this should have taken me 5 minutes and it's been hours!
The same origin policy prevents loading HTML from another web site via AJAX. The right way to do this would be to have the methods detect if the request is coming from AJAX and return JSONP instead.
public ActionResult TotalDonations( string callback )
{
var total = "$" + repo.All<Customer>().Sum(x => x.AmountPaid).ToString();
if (!string.IsNullOrEmpty(callback))
{
return Content( callback + "( { total: " + total + " } );" );
}
else
{
return Content(total);
}
}
...
$.getJSON('http://charity.hondaclassic.com/home/totaldonations?callback=?',
function(data) {
$('.totalDonations').html( data.total );
});
your totaldonations link is missing the o in total
> $('.totalDonations').load('http://charity.hondaclassic.com/home/ttaldonations');
should be
$('.totalDonations').load('http://charity.hondaclassic.com/home/totaldonations');
I ended up just doing it server side to avoid the same origin policy mentioned above:
Dim totalDonations As String
Dim charities As String
Using Client As New System.Net.WebClient()
totalDonations = Client.DownloadString("http://charity.hondaclassic.com/home/totaldonations")
charities = Client.DownloadString("http://charity.hondaclassic.com/home/CharityList")
End Using
worked like a charm.

Resources