ASP.NET MVC: prevent malicious request from javascript using AJAX - ajax

In my ASP.NET MVC controller, I accept a postback call to add error logs to my log file.
[HttpPost]
[AllowAnonymous]
public JsonResult AddError(string log)
{
//add log to log file here
...
...
...
return Json(new { Code = 0 });
}
This is to allow me to add any unhandled exception in the javascript to my log file. It will help to pinpoint if any problem arises.
This is also can be used by malicious people to put some crazy logs to the log file (eg. to bombard the website).
Is there a way or a technique to prevent this (ie. only allow my javascript to add the log)?
Thanks

Related

ASP.NET FileContentResult always 404 in production

I have an ASP.NET MVC 3 web app that's behaving strangely. There's an action that allows me to download a test file:
[HttpGet]
public ActionResult Download()
{
var cd = new System.Net.Mime.ContentDisposition
{
FileName = "test_file.txt",
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(System.Text.Encoding.Unicode.GetBytes("HELLO THERE"), "text/plain");
}
In debug mode it works no problem, I get a file download as expected ("test_file.txt", with contents "HELLO THERE").
When published, and deployed with IIS 6, a 404 error is always returned. Anyone know why the difference and how to fix it?
Turns out the issue was a combination of routing and IIS configuration. I had some parameters for the action, one of which contained a file extension. I had set up a route so that the parameters were in the format
../Download/[text].txt
instead of
../Download?[text].txt
IIS was reading this as trying to access a file on the file system. I removed the custom route and all was resolved.

This page isn't redirecting properly error with MVC3 + AJAX + Forms Authentication

EDIT: Removed non-relevant code/desc, since the issue was not just to do with the initial code there.
I have an MVC3 based application that uses a lot of Ajax calls (such as with jqGrid) and Forms Authentication. I also use the [Authorize] attribute on certain controller/actions that I call with Ajax in most cases. Every so often my application falls over with a 'This page isn't redirecting properly' or a 'this page has a redirect loop'.
I checked this out with fiddler. After logging in as a user and trying to access pages that require authentication, sometimes I get redirected to Account/LogOn which then goes into an infinite loop. This usually happens when I'm calling any controller/action with an Authorize attribute with an Ajax call. The application seems to send out a 302 redirect to Account/Logon. The Account/Logon call then seems to redirect to itself. And the textView on Fiddler shows the following.
<html><head><title>Object moved</title></head><body>
<h2>Object moved to here.</h2>
</body></html>
I have the following in my Global.asax file
protected void Application_EndRequest()
{
var context = new HttpContextWrapper(this.Context);
//if we are an ajax request and forms authentication caused a 302, then we actually need to do a 401
if (System.Web.Security.FormsAuthentication.IsEnabled && context.Response.StatusCode == 302 && context.Request.IsAjaxRequest())
{
context.Response.Clear();
context.Response.StatusCode = 401;
}
}
And this in my main Layout page
<script type="text/javascript">
$(document).ajaxError(function (xhr, props) {
if (props.status == 401) {
location.reload();
}
});
</script>
My web.config setting for Forms authentication has this
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880"/>
</authentication>
The redirect starts every so often and it is not with the same controller/action either. Just seems quite random. I am not sure if the cookie is expiry for the user and causing this issue for some reason or if it is an issue with the application pool recycling. Any suggestions on how to get around this would be most appreciated. Been struggling with this the last few days now, so any help from the experts will be great. Thank You
decorate your action method with Authorize attribute to make it available to authenticated/logged in users, after that check user role.
I don't see a loop in the above code. But maybe in one of controller1 or controller2 you have a redirect back to this home/index action.
I would suggest to run fiddler while testing the site, when a redirect loop happens you can easily detect it there, and then it is easier to find out what's wrong with the code.
Please see the answer in the linked question. This was the resolution to my redirect loop problem. Thank you for all your inputs.
IIS Session timeout and Forms Authentication loop

Extend/Override jquery.ajax.done function to detect .net logon screen

I'm using forms authentication on an asp.net MVC application and I'm using jquery and ajax to create a rich user interface. Some users tend to leave the page open for hours and their login session is expiring as expected. If the user click a link or reloads the page the login screen is returned as expected. If they click a button or trigger an ajax call the ajax call is returning the login html instead of the expected html or JASON result. Resulting in a mangled page with half a login screen.
As I've already coded a lot of ajax calls I thought I could extend or override the default .done event to add a check for the login screen before continuing with whatever event I've programmed.
For example i have this function :
function CallAjax() {
$.ajax({type: "GET", url: "foo" })
.done(function (data) { $('#result').val(data); });
}
is it possible to override the default implementation of .done so do a check without rewriting this function? My check would probably to see if the result is a html response if so search for "login" to see if its the login page.
I would recommend you to extend the ASP.NET Forms Authentication module as Phil Haack explained in this blog post so that it no longer redirects to the login page when requested with AJAX but returns a 401 HTTP status code instead. Then you could simply use the .fail callback and intercept the 401 status code. The .done handler will not be invoked in this case.
I tried Darin's suggestion but after hours of trying I just couldn't get it to work in my set up. However I stumbled across this rather simpler solution which worked for me first time:
just add this to the global.asax
protected void Application_EndRequest()
{
var context = new HttpContextWrapper(this.Context);
if (FormsAuthentication.IsEnabled &&
context.Response.StatusCode == 302 &&
context.Request.IsAjaxRequest())
{
context.Response.Clear();
context.Response.StatusCode = 401;
}
}
}
Taken from http://jvance.com/blog/2012/09/21/FixFormsAuthentication302.xhtml

ViewExpiredException not thrown on ajax request if JSF page is protected by j_security_check

I have a JSF page which is not protected by j_security_check. I perform the following steps:
Open the JSF page in a browser.
Restart the server.
Click a command button on the JSF page to initiate an ajax call.
Firebug shows that a ViewExpiredException is raised, as expected.
Post:
javax.faces.ViewState=8887124636062606698:-1513851009188353364
Response:
<partial-response>
<error>
<error-name>class javax.faces.application.ViewExpiredException</error-name>
<error-message>viewId:/viewer.xhtml - View /viewer.xhtml could not be restored.</error-message>
</error>
</partial-response>
However, once I configure the page to be protected by j_security_check and perform the same steps listed above, strangely (to me) the ViewExpiredException is no longer raised. Instead, the reponse is just a new view state.
Post:
javax.faces.ViewState=-4873187770744721574:8069938124611303615
Response:
<partial-response>
<changes>
<update id="javax.faces.ViewState">234065619769382809:-4498953143834600826</update>
</changes>
</partial-response>
Can someone help me figure this out? I expect it to raise an exception so I can process that exception and show an error page. Now it just responds with a new ViewState, my page just got stuck without any visual feedback.
I was able to reproduce your problem. What is happening here is that the container invokes a RequestDispatcher#forward() to the login page as specified in security constraint. However, if the login page is by itself a JSF page as well, then the FacesServlet will be invoked as well on the forwarded request. As the request is a forward, this will simply create a new view on the forwarded resource (the login page). However, as it's an ajax request and there's no render information (the whole POST request is basically discarded during the security check forward), only the view state will be returned.
Note that if the login page were not a JSF page (e.g. JSP or plain HTML), then the ajax request would have returned the whole HTML output of the page as ajax response which is unparseable by JSF ajax and interpreted as "empty" response.
It is, unfortunately, working "as designed". I suspect that there's some oversight in the JSF spec as to security constraint checks on ajax requests. The cause is after all understandable and fortunately easy to solve. Only, you actually don't want to show an error page here, but instead just the login page in its entirety, exactly as would happen during a non-ajax request. You just have to check if the current request is an ajax request and is been forwarded to the login page, then you need to send a special "redirect" ajax response so that the whole view will be changed.
You can achieve this with a PhaseListener as follows:
public class AjaxLoginListener implements PhaseListener {
#Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
#Override
public void beforePhase(PhaseEvent event) {
// NOOP.
}
#Override
public void afterPhase(PhaseEvent event) {
FacesContext context = event.getFacesContext();
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
String originalURL = (String) request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
String loginURL = request.getContextPath() + "/login.xhtml";
if (context.getPartialViewContext().isAjaxRequest()
&& originalURL != null
&& loginURL.equals(request.getRequestURI()))
{
try {
context.getExternalContext().invalidateSession();
context.getExternalContext().redirect(originalURL);
} catch (IOException e) {
throw new FacesException(e);
}
}
}
}
Update this solution is since OmniFaces 1.2 been built into the OmniPartialViewContext. So if you happen to use OmniFaces already, then this problem is fully transparently solved and you don't need a custom PhaseListener for this.
The above AjaxLoginListener solution works for me. Interestingly we are using omnifaces 3.11.1 but the OmniPartialViewContext is not working in my scenario. This is because the check for the loginViewId does not match the current viewId since I have an error-page in my web.xml for org.jboss.weld.contexts.NonexistentConversationException. Note that when AjaxLoginListener is fired for me it throws an exception on the call to context.getExternalContext().invalidateSession(); so it never calls the redirect(). So I'm not sure if my scenario is exactly the same as the original one in this thread. Here are the steps I use to recreate my scenario:
Visit an xhtml page with an ajax command button.
Wait for the session to timeout.
Click the ajax command button.
User is redirected to the error-page mapped to the NonexistentConversationException in web.xml
Click a link on that page which requests a secured url
System shows login page - login.
Click the link that takes you to the xhtml page that contained the ajax command button in step 1.
System shows the partial response containing the NonexistentConversationException error-page contents.
Is it possible that the AjaxLoginListener is working because it is mapped to PhaseId.RESTORE_VIEW whereas the OmniPartialViewContext is mapped to PhaseId.RENDER_RESPONSE?

How do I return a File() ActionResult as well as a View()?

My MVC3 site downloads a .txt file to the user. I want the Download method of my controller to contact the business logic layer, then perform the download, then redirect to a new view that will announce the successful download. The problem is that I can return a view or a file but not both.
[HttpPost]
public ActionResult Download(FormCollection collection)
{
//Contact BLL no problem
return File(Encoding.ASCII.GetBytes("Testing"), "text/plain", "Test.txt");
//OR
return RedirectToAction("OtherActionWithOtherView");
}
I'm happy to go about this differently; any suggestions would be appreciated!
You need to provide a View page that has an HTML link to the file download action or an Embedded IFrame with src tag assigned to the file download action.
HTTP can't do that.
Instead, return the view only, and put a <meta http-equiv="refresh"> tag in the view that redirects to the download.

Resources