Lyoid Lopes
Lyoid Lopes

Reputation: 11

How to Prevent Direct access to View in Asp.net Core

I want to prevent an view from directly being accessed. I have a view that displays Email Confirmed, when a confirmation link in the users email is clicked. But, the problem is the Url of that view can be directly accessed, I just wan it to be accessed when the link in email is clicked.

I have tried this option, but keep getting error as the UrlReferer is not available in Asp.net core. this code will not work in asp.net core, can someone help me please.

How to access these values in ASp.net Core 2.x :

filterContext.HttpContext.Request.UrlReferrer
filterContext.HttpContext.Request.Url.Host
filterContext.HttpContext.Request.UrlReferrer.Host

Code I found on Stack-overflow - but it needs System.Web, which is not available in asp.net core.


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 = "" })); 
        }
    }
}

Recommended way to use the custom Attribute

[NoDirectAccess]
public ActionResult MyActionMethod()

Upvotes: 1

Views: 5420

Answers (2)

Phantom2018
Phantom2018

Reputation: 559

In general it is not a good idea to depend on the "referer" properties on requests. Browser implementations could be different & as a result you may receive inconsistent values.

If a URL is "public", you cannot prevent a user from typing that on a browser. What you could do however, is add validation to the "Get" request - for example, requiring some additional information on the URL - in your case, say the User id and a validation "code". This is how asp.net identity generates a URL for email confirmation. It includes the user id & a confirmation code. Without these values as part of the URL, it is possible to invalidate the request and redirect to another page (rather than the Email confirmed page).

For example, in Razor pages, the following is an example of how you could validate the request. (The point is, it requires additional input from the user rather than the plain URL..) (You could do something similar in MVC as well.)

       public async Task<IActionResult> OnGetAsync(string userId, string code)
        {
            if (userId == null || code == null)
            {
                return RedirectToPage("/Index");
            }

            var user = await _userManager.FindByIdAsync(userId);
            if (user == null)
            {
                throw new ApplicationException($"Unable to load user with ID '{userId}'.");
            }

            var result = await _userManager.ConfirmEmailAsync(user, code);
            if (!result.Succeeded)
            {
                throw new ApplicationException($"Error confirming email for user with ID '{userId}':");
            }

            return Page();
        }

The URL for the above code to validate would be something similar to this:

http://[baseurl]/Account/ConfirmEmail?userId=ca5416dd-c93a-49a5-8fac-a9986dc91ed7&code=CfDJ8DWvHoaRg6JAv0rZ75jyEVx

Upvotes: 2

itminus
itminus

Reputation: 25360

  1. If you do need UrlReferer, create a filter as below :
    public class NoDirectAccessAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var canAcess= false;

            // check the refer
            var referer= filterContext.HttpContext.Request.Headers["Referer"].ToString(); 
            if(!string.IsNullOrEmpty(referer)){
                var rUri = new System.UriBuilder(referer).Uri;
                var req = filterContext.HttpContext.Request;
                if(req.Host.Host==rUri.Host && req.Host.Port == rUri.Port && req.Scheme == rUri.Scheme ){
                    canAcess=true;
                }
            }

            // ... check other requirements

            if(!canAcess){
                filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Home", action = "Index", area = "" })); 
            }
        }
    }

and now you can decorate your action method with a [NoDirectAccess] :

        [NoDirectAccess]
        public IActionResult Privacy()
        {
            return View();
        }
  1. It is not suggested to use Referer to authorize request because it's quite easy to fake a HTTP Header of UrlReferer.

IMO, it is much better to store the EmailConfirmed=true somewhere when user clicks the confirmation link. And then you could check whether EmailConfirmed equals true when validating.

    public class NoDirectAccessAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var canAcess= false;

            // check the `EmailConfirmed` state

            // ... check other requirements

            if(!canAcess){
                filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Home", action = "Index", area = "" })); 
            }
        }
    }

Upvotes: 3

Related Questions