I am working on a MVC application and I have a requirement of dealing with errors and session timeouts by redirecting the user to different error pages based on few parameters in the query string.
The issue I am facing is that i tried to implement this by saving the required parameters from querystring into a session and then redirecting to error pages. But before every HttpGet and Post action in my controllers I am checking if session is active.
So in case of a situation where session values are lost and not able to read them.
How can I implement this thing in any other way?
You need to check whether the session exists, has the fields you expect and is active. If the session does not exist or does not have a fields you expect, then handle the case when the session does not exist yet/expired. If it is not active, then handle the case when the session is no longer active. If everything is ok, then handle the request normally. If the session expired, then handle it as expired.
to check about session, you can use an ActionFilter like this:
public class SessionActiveFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var activeSession = Session["user"];
if (activeSession == null)
//Here, do a redirect
base.OnActionExecuting(filterContext);
}
}
Also, you can use a third option to save the session, like Redis Cache http://blogs.msdn.com/b/webdev/archive/2014/05/12/announcing-asp-net-session-state-provider-for-redis-preview-release.aspx
I know this is a dead story now. But I post this answer for the new comers. Please see the nice tutorial in codeproject about how to check session values in Action Filters.
In a dynamic web application, the session is crucial to hold the information of current logged in user identity/data. So someone without authentication cannot have access to some Page or any ActionResult, to implement this kind of functionality, we need to check session exists (is not null) in every action which required authentication.So, the general method is as follows:
[HttpGet]
public ActionResult Home()
{
if(Session["ID"] == null)
return RedirectToAction("Login","Home");
}
We have to check the above 2 statements each time and in each ActionResult, but it may cause 2 problems.
Repeat Things: As per the good programming stranded, we don't have to repeat the things. Create a module of common code and access it multiple times/repeatedly
Code missing: We have to write code multiple times so it might happen some time we forget to write code in some method or we missed it.
How To Avoid?
The ASP.NET MVC provides a very great mechanism i.e., Action Filters. An action filter is an attribute. You can apply most action filters to either an individual controller action or an entire controller.
If you want to know more about action filter, please click here.
So we will create a custom Action Filter that handles session expiration and if session is null, redirect to Login Action.
Create a new class in your project and copy the following code:
namespace YourNameSpace
{
public class SessionTimeoutAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
if (HttpContext.Current.Session["ID"] == null)
{
filterContext.Result = new RedirectResult("~/Home/Login");
return;
}
base.OnActionExecuting(filterContext);
}
}
}
Now our Action Filter is created and we are ready to use it. The following code will show you how we can apply attribute to Action or to complete controller.
Apply to Action
[HttpGet]
[SessionTimeout]
public ActionResult MyProfile()
{
return View();
}
Apply to Controller
[SessionTimeout]
public class HomeController : Controller
{
}
Now all actions of Home Controller will check for session when hit with the help of Action Filter. So we have reduced the code and repetitive things. This is the benefits of Action Filters.
Related
Currently, my application can track whenever user has logged in or out. The logging out activity is saved to database because User simply click a button. However, I would be interested in how to implement something similar but when user's ticket is no longer valid, either because it's been removed or expired.
I would like to handle any event (if possible) that is fired whenever user is no longer valid.
Is it possible to implement anything like this?
Cheers!
EDIT
I found two solutions. One uses BaseController--the code below from Handle asp.net MVC session expiration. This is simply what the author claims the easiest and working solution. First, is it a good idea to inherit all conrollers from a custom controllers? Second, I have already customized my own [Authorize] attribute. I don't know how all this together is going to work, and three, can i still have access to the cookie of a logged user where I hold UserID and some other data such as UserRole? And also it is not triggered automatically but whenver user trying to access and cookie is expired. It could be 6 hours later.
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Check if session exists
if (filterContext.HttpContext.Session != null)
{
//if new session
if (filterContext.HttpContext.Session.IsNewSession)
{
string cookie = filterContext.HttpContext.Request.Headers["Cookie"];
//if cookie exists and session id index is greater than zero
if ((cookie != null) && (cookie.IndexOf("ASP.NET_SessionId") >= 0))
{
//redirect to desired session expiration action and controller
filterContext.Result = RedirectToAction("SessionExpired", "Home");
return;
}
}
}
//otherwise continue with action
base.OnActionExecuting(filterContext);
}
}
The second solution I found is based on Global.aspx's function
void Session_End(object sender, EventArgs e)
{
Response.Redirect("~/Account/Timeout");
}
but I read it only works when I use InProc. I don't know what that is, though.
If you go with the filter [Authorize] you can add it as a global filter.
filters.Add(new AuthorizeAttribute());
Which is what your controller does too.
To answer your questions:
Yes, if you are building a framework that impacts all controllers. There is no problem with inheriting from a base controller.
Looks like the global filter is right up your alley.
You can't access the cookie once the session expires since there is no client!
When a session expires, the screen gets frozen to information they've already been authorized for until any subsequent request is made.
Hope that helps.
I have an ASP.NET MVC Application using MVC 4.5. I have been instructed to put a query string authorization to the application. The application serves as a data processor for another application at the front.
We decided to append a querystring in the requesting URLs containing a hash code for instance http://dr.appbox.us/DataReport/?passcode=HASHCODE.
The HASHCODE can be checked and if it matches the request is permitted further.
Now I have about 20 controllers in the application, is there a way that I can check if the HASHCODE is valid in global.asax and redirect the user to error page from there?
Also please tell me if there is a way to check if I can bypass this hashcode for Ajax requests in the application.
Thanks
You can use a custom action filter for this. If you create a new class that inherits from ActionFilterAttribute and override the OnActionExecuting method. Here you can validate the passcode and redirect if required.
public class HashCodeCheckFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var passcode = filterContext.HttpContext.Request.QueryString["passcode"];
// Validate passcode
var valid = false;
// If invalid then do some error processing
if (!valid)
{
// Redirect to errro page....
filterContext.Result = new RedirectToRouteResult("NameOfErrorRoute");
return;
}
base.OnActionExecuting(filterContext);
}
}
You can then either use this on specific controllers / actions as an attribute:
[HashCodeCheckFilter]
public class HomeController : Controller
{
// GET: Home
[HashCodeCheckFilter]
public ActionResult Index()
{
return View();
}
}
Or you can register it in Application_Start (Or App_Start/FilterConfig) as a global filter that applies to all requests:
GlobalFilters.Filters.Add(new HashCodeCheckFilterAttribute());
If you want to not check if the request is an ajax request you can check the HTTP_X_REQUESTED_WITH header on the request to see if it equals xmlhttprequest.
is there a way that I can check if the HASHCODE is valid in global.asax and redirect the user to error page from there
I would use a HttpHandler which is better suitable for this. (See documentation here and here and step-by-step tutorial here)
Also please tell me if there is a way to check if I can bypass this hashcode for Ajax requests in the application.
You could check if the HttpContext.Current.Request.Headers["x-requested-with"] is XMLHttpRequest. Then it is an AJAX call and you could skip the authentication step. Additionally, you could set a session variable when receiving the hashcode for the first time and check if that hashcode is still valid when the AJAX request is done. Then you will have a better security then just not checking it.
It sounds to me like you should be looking at implementing an IAuthorizationFilter rather than trying to use Global.asax. An implementation of IAuthorizationFilter will run before any request and you can choose what to do if the hashcode in invalid.
Since you're using asp.net MVC I would look at global action filters within your application. You can register an action filter as a global action filter through your global.asax.
GlobalFilters.Filters.Add(new MyActionFilterAttribute());
Within your action filter you can put the code to check for your hash, this will get executed each time any of your actions are called on the controllers.
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(!filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
{
// CHECK HASH HERE
}
base.OnActionExecuting(filterContext);
}
}
Check out these resources for more information
http://weblogs.asp.net/gunnarpeipman/asp-net-mvc-3-global-action-filters
https://msdn.microsoft.com/en-us/library/system.web.mvc.globalfiltercollection(v=vs.98).aspx
I have an ASP.NET MVC web application.
There's a welcome page in my application, and i wish for the user to complete some steps on that page before allowing him to use the application.
I'm trying to accomplish 2 things:
Ensure that the user is always redirected to that page until he completes the required steps. Note: the user is logged in when he is at the welcome page.
Ignore all requests made by that user to any of the controllers, except for a few specific requests to a specific controller.
What is the correct way to do the above?
Thanks.
What i have done is:
Create a class that derives from Controller and add the logic to redirect if not Logged in:
public class CustomController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!LoggedIn) //Here you decide how to check if the user is Logged in
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "YourLogInControllerName",
action = "YourLoginActionName"
}));
}
else
{
base.OnActionExecuting(filterContext);
}
}
}
Then all Controllers derive from this CustomController class.
Sounds like you could use the session for that, or other (more persistent) storage if you must make sure the visitors finish these 'required steps', so you can store it when they've fininshed them.
I created a custom authorise attribute that redirected the use to my login page if they didn't meet the criteria I set. This then allowed me to use [AuthorizeAdminArea] on my base controller which stopped access to all areas. I then used [AllowAnonymous] to allow access to the login area.
Take a look at the SimpleMemshipProvider
Use a Role and only allow access to the other controllers if the user has this Role. Add the user to this Role when they have completed the necessary steps.
See http://msdn.microsoft.com/en-us/library/9ab2fxh0.aspx
I'm trying to find a way to deny any direct access to my action methods. Basically what I want my users to click on links to navigate instead of typing the URL directly into the address bar in their browsers.
Now I know this can be done by checking the urlreferrer in the request object but this is kind of unreliable and weak because the urlreferrer can easily be modified and some of the security suites actually remove it from the request.
So does any of you know of a way to do this in asp.net mvc3?
Below is the code for the NoDirectAccessAttribute method to restrict direct access to any class or action method that applies the NoDirectAccess attribute
using System;
using System.Web.Mvc;
using System.Web.Routing;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class NoDirectAccessAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.UrlReferrer == null ||
filterContext.HttpContext.Request.Url.Host != filterContext.HttpContext.Request.UrlReferrer.Host)
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Home", action = "Index", area = "" }));
}
}
}
Use the action filter as follows for any controller or action method where you don't want user to request it directly
[NoDirectAccess]
public ActionResult IsUsernameUnique()
In the example above, any direct access to the IsUsernameUnique action method will automatically redirect the user to the Home/Index action method.
i am not sure but maybe this can help you
Consider we have a page with this url
www.yoursite.com/home.aspx
To avoid your user to directly browse this page you can rewrite you url like this
www.yoursite.com/fdmf489ruv30/home.aspx
and in this url the part "fdmf489ruv30" is a unique string that you created it on session_start and will destroy it on session_end
Use this code in Global.asax.cs and Call [NoDirectAccess] to all controllers
//Prevent direct URL access: Call [NoDirectAccess] to all controllers to block
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class NoDirectAccessAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.UrlReferrer == null ||
filterContext.HttpContext.Request.Url.Host != filterContext.HttpContext.Request.UrlReferrer.Host)
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Home", action = "Login", area = "" }));
}
}
}
It is impossible to securely ensure that an authorized user CANNOT spoof a valid request.
After all, if the browser can create a "valid request", then so can an authorized user!
However, this is an unusual requirement, and I'm interested to know what's the motivation behind it?
There are, however, a number of ways to make it harder to spoof the request.
Since no methods will be completely secure, you can try to obfuscate and make it tedious for someone to spoof your requests.
As others have suggested, you could create a random "token" per session and require it in the URL (either as a path or as a querystring).
It would be even better to use JavaScript for this. Render a hidden input with this "token". Then, intercept each link's click event, append the "token" value to the URL, then navigate to the URL.
You can enhance the JavaScript to somehow "process" your token before using it, and minify your JavaScript to obfuscate it ... this would definitely deter even the above-average users from tinkering with your URLs.
There are tons of possible solutions, but the "right" solution really depends on what specific behavior you are trying to prevent.
A quick way of doing that is to set a session containing a random number in the action which is redirecting, also pass the random number as a parameter to the other action.
Inside the other action(redirected one), compare the value of the session with the parameter of the action. If values are equal, the user is getting there by pressing button, otherwise, the user gets there by changing the URL. Hope it helps.
question on Stackoverflow
usually, when I look at a ASP.Net MVC application, the Route table gets configured at startup and is not touched ever after.
I have a couple of questions on that but they are closely related to each other:
Is it possible to change the route table at runtime?
How would/should I avoid threading issues?
Is there maybe a better way to provide a dynamic URL? I know that IDs etc. can appear in the URL but can't see how this could be applicable in what I want to achieve.
How can I avoid that, even though I have the default controller/action route defined, that default route doesn't work for a specific combination, e.g. the "Post" action on the "Comments" controller is not available through the default route?
Background: Comment Spammers usually grab the posting URL from the website and then don't bother to go through the website anymore to do their automated spamming. If I regularly modify my post URL to some random one, spammers would have to go back to the site and find the correct post URL to try spamming. If that URL changes constantly I'd think that that could make the spammers' work more tedious, which should usually mean that they give up on the affected URL.
I would consider to implement my own IRouteHandler and put some custom logic in my custom ControllerActionInvoker. How it would work ? The route table wouldn't dynamically change but you could check in your custom ControllerActionInvoker for a random parameter in the route path and invoke or not the corresponding action.
My route :
routes.Add
(
new Route
(
"blog/comment/{*data}",
new RouteValueDictionary(new {controller = "blog", action = "comment", data = ""}),
new MyRouteHandler()
)
);
My I route handler :
class MyRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MyHttpHandler(requestContext);
}
}`
My handler :
class MyHttpHandler : MvcHandler
{
public MyHttpHandler(RequestContext requestContext) : base(requestContext)
{
}
protected override void ProcessRequest(HttpContextBase httpContext)
{
IController controller = new BlogController();
(controller as Controller).ActionInvoker = new MyActionInvoker();
controller.Execute(RequestContext);
} }`
and my action ivoker where the custom logic for handling an action or not should be coded :
class MyActionInvoker : ControllerActionInvoker
{
protected override ActionResult InvokeActionMethod(MethodInfo methodInfo, IDictionary<string, object> parameters)
{
var data = ControllerContext.RouteData.GetRequiredString("data");
// put my custom logic to check whetever I'll handle the action or not. The data could be a parameter in the database for that purpose.
return base.InvokeActionMethod(methodInfo, parameters);
}
}
I don't know it it's the best solution but for now it's the one that comes to my mind.
Considering the actual problem background, the usual approach is to include a dynamically created transaction number. It should be stored in a hidden form field as well as in the server side session dictionary and only be valid for exactly one request.
I think today a lot of frameworks provide such a security mechanism; whereas this attack type is known as Cross-Site-Request-Forgery (csrf).