I have an application in ASP.NET MVC that we bought that has multiples possible routes and that might get even bigger because we can dynamically add pluggins. We're talking hundreds of different pages and controllers. We modified/added the ones that we needed (mostly reskinning). And now we have to make sure that only the pages that we modified are accessible to the user. Someone that knows the application that we bought could go to routes that we don't want him to see.
So in the end, my question is :
Is there a way to deny access to all the routes except the ones that I want.
Since the application is HUGE. I can't/don't want to go to individual controller and add a redirect or a Data annotation. Also, I dont want to list every url I deny because that would also be crazy long and would not work if we add a plugin later on. I have like 300 url to deny, 20 to allow..
I'm thinking something like this in the webconfig ??
deny *
permit mycontroller/action1
permit mycontroller/action2
I should also add, that this application does not use any kind of authentication/authorization. I want any user to be able to use the application.
We're hosting it in Azure wep app, would that be a solution ?
If anyone is stuck with the same problem, here's how I it.
It's all done in the web.config of the application:
You start by denying everything with this
<authorization>
<deny users="?" />
</authorization>
<authentication mode="None" />
Then, you can whitelist the URLs that you want like this:
<location path="mycontroller/action1">
<system.web>
<authorization>
<allow users="?" />
</authorization>
</system.web>
<location path="mycontroller/action2">
<system.web>
<authorization>
<allow users="?" />
</authorization>
</system.web>
So every URL dans does not have a location tag is considered not valid and will raise a 401 exception. You can then do what you want from there.
Hope this helps someone :)
use the [Authorize] attribute at the controller level and then [AllowAnonymous] for the action
[Authorize]
Public class mycontroller : Controller
{
Public ActionResult action1 ()
{
Return View();
}
[AllowAnonymous]
Public ActionResult action2 ()
{
Return View ();
}
}
Related
I have this site hosted in a couple of places.
http://web83.jet.studiocoast.com.au/Account/Register
http://flowerpictures.tesselaars.com/Account/Register
Both with the same code.
In a weird way http://flowerpictures.tesselaars.com/Account/Register redirect it to login.
The register action is as simple as
//
// GET: /Account/Register
public ActionResult Register()
{
return View();
}
Not sure if this might help but the register view uses
#Membership.MinRequiredPasswordLength
What works in one location doesn't in another. Both sit on the same server.
Looks like you're restricting anonymous users from accessing this action. This'll happen when you've set the Authorize attribute, at the class level - thus cascading accessibility down to your action methods.
Remove the Authorize attribute that's at the class level, and only add it to applicable action methods inside the class.
It'd also be good to look at the web.config and check the Authorize section where the accessibility is configured for anonymous and authenticated users:
<configuration>
<system.web>
<authentication mode="Forms"/>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
<location path="/account/register">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
</configuration>
I'm using ASP.NET Authorization to deny users access to my site before logging in, but this is also blocking the Register.cshtml page.
How do I sort out my authorizations to allow this page through?
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
<location path="Content">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
<location path="Register.cshtml">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
IMHO, you should not use web.config to control the authentication of your application instead use Authorize attribute.
Add this in your Global.asax file under RegisterGlobalFilters method
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
or you can decorate also your controller with [Authorize]
[Authorize]
public class HomeController : Controller
{
...
}
For action which require Anonymous access use AllowAnonymous attribute
[AllowAnonymous]
public ActionResult Register() {
// This action can be accessed by unauthorized users
return View("Register");
}
As per Reference,
You cannot use routing or web.config files to secure your MVC application. The only supported way to secure your MVC application is to apply the Authorize attribute to each controller and use the new AllowAnonymous attribute on the login and register actions. Making security decisions based on the current area is a Very Bad Thing and will open your application to vulnerabilities.
This is happening because you are denying everyone from application by using
<authorization>
<deny users="?" />
</authorization>
Above code will override all permission given to the folder
Good idea would be Deny user folderwise and keep Register/Login/Help/Contact pages at root level.
I have a MVC4 web app with the following controller
[Authorize]
public class AccountController : BaseController
{
[AllowAnonymous]
public ActionResult SignInRegister(LoginModel loginModel, string returnUrl)
{
//some implementation
}
//other secured actions
}
This is working as expected when running locally, but as soon as I deploy it to the Free Azure Website I get a 401 error code with the message: You do not have permission to view this directory or page.
Removing the [Authorize] attribute and redeploying works as expected, adding it again and redeploying brings back the problem.
I even tried the fully qualified class names: System.Web.Mvc.Authorize and System.Web.Mvc.AllowAnonymous with the same results.
The app is using .NET 4.5 and the Azure Website is also configured to use 4.5.
UPDATE:
The BaseController has an action that returns the Header as partial view which was not decorated with [AllowAnonymous]. Locally it resulted in the page being displayed without the header, but on Azure Websites the response was cut off and only returned with the error message mentioned above. I had not realized the header was missing until I purposely looked into it.
Now the question begs to be asked: why is Azure Websites overriding the response?
The BaseController has an action that returns the Header as partial view which was not decorated with [AllowAnonymous]. Locally it resulted in the page being displayed without the header, but on Azure Websites the response was cut off and only returned with the error message mentioned above. I had not realized the header was missing until I purposely looked into it.
Now the question begs to be asked: why is Azure Websites overriding the response?
I had the exact same problem and like Jonas' update says, you need to look out for Actions that return Partial Views AND have the [Authorize] attribute.
What you need to do is to remove the [Authorize] attribute and then if your action needs the user to be authenticated to render properly, have your code handle the unauthorized case.
Example is if your page displays the currently logged in user's name via a Partial. Have your action display an empty string or something else if the currently logged in user is not available.
Check your web.config if you have
<authorization>
<deny users="?" />
</authorization>
its override [AllowAnonymous]
Add to web.config section:
<location path="YourController/AnonymousMethod">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
to allow anonymous access for AnonymousMethod
I am working in my first MVC project and i am having troubles with authentication.
I have a login page that correctly validates the user by my active directory. But, even authenticated not all users are authorized to access the system, so I use a section in web.config to check if user have permissions. Something like:
<authorization>
<allow users="john,mary,paul,bill,jane,anna" />
<deny users="*" />
</authorization>
It works fine and the user always is redirected to login if doesn't have permission.
BUT, when I check if user is Authenticated, the result is always true. And, in login page, I want to check if I must show a message to logged AND authorized users. Something like:
#if (User.Identity.IsAuthenticated && User.Identity.IsAuthorized)
{
#Html.Partial("_Menu");
}
So... How I do it?
Authentication an Authorization are 2 different concepts. Authentication means you know who the person is. Authorization means they have specific permissions.
If you want to check if they are authorized to perform some action and provide them a button or link to perform that action (or access some data, whatever it may be), then you'll have to check if they have the permissions using other means. Some more specifics about the setup you have would help answer the question better.
To Authenticate, check on your web.config if you have this tag:
<authentication mode="Forms">//It cannot be "None"
<forms loginUrl="~/MyLoginURL" timeout="2880" />
</authentication>
And in your LoginMethod, that authenticate the user, make sure you called any .NET method that authenticate the user on its Identity, example:
using System.Web.Security;
FormsAuthentication.SetAuthCookie(login, boolean to cookie) // Or others FormsAuthentication methods that authenticate.
There is an AuthorizeAttribute in System.Web.Mvc, that can be used with your actions or controllers, like:
[Authorize]
public ActionResult Index()
{ ... }
or
[Authorize]
public class HomeController
{ ... }
This AuthorizeAttribute will check if the current user is Authorized. You can create your own attribute by just inheriting the AuthorizeAttribute and override OnAuthorization and AuthorizeCore
Once you perform a successful login (FormsAuthentication.SetAuthCookie), your user get authenticated so until you dont explicitly logoff (FormsAuthentication.SignOut) that's the correct behaviour, as said authentication and authorization are two different things.
I guess you want to show a menu only to users that have been authenticated through AD, in this case you can just restrict your resources with:
#if (User.Identity.IsAuthenticated)
{
#Html.Partial("_Menu");
}
If authorized, a user is also authenticated so no need to do the double check. You can then use the [Authorize] attribute, which relies on User.Identity.IsAuthorized.
Authorization comes handy when you can use Roles to group your users. Check if your ADusers belong to any group, if so you could do something like:
<authorization>
<allow users="*" allow role="yourdomain\yourgroup" />
<deny users="*" />
</authorization>
Using a technique I found in one of the recent ASP.NET MVC books, I have an action method on a controller that returns a partial view for an ajax request and a full view actionresult for a normal get request -- it checks the IsAjaxRequest property of the Request object to determine which type of actionresult to return. The partial view returned by the action method returns the HTML to display a table of records from a database. The controller that contains the action method is tagged with the Authorize attribute so that only logged in users can call the controller's methods. I am using forms authentication with a 30-minute timeout and sliding expiration.
The problem arises after the user's 30-minute timeout has been reached. Because the controller is marked with the Authorize attribute, a call to the action method after the expiration timeout redirects the user to the login page. However, because this is an ajax call, the html for my login page is returned and rendered in the middle of the page that should contain the HTML table of records that would normally be returned by the action method in the partial view. The ajax call is not really failing, just returning the html for the wrong page.
Has anyone encountered and dealt with this problem? I am trying to avoid having to move all of my server side code that handles ajax calls into a separate controller that does not require an authenticated user, but that seems like my only alternative at this point. Even that won't produce the behavior I would expect, because it will allow the user to continue using the web page even after the 30 minute timeout has been reached -- it will not redirect to the login page.
Thanks for any advice.
Edit
The solution below with the custom AuthorizeAttribute would seem to have me heading in the right direction, but I cannot even get to this code. It appears that the code in the custom AuthorizeAttribute is never reached after the expiration timeout is reached. It seems like forms authentication is causing a redirect to the login page long before the attribute code. The custom AuthorizeAttribute is the only one on my controller. I also have the following web.config values (the timeout value is set extremely low to trigger a timeout for testing):
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="1" slidingExpiration="true" defaultUrl="~/ErrorReport/Index" requireSSL="true" protection="All"/>
</authentication>
<authorization>
<deny users="?"/>
<allow users="*"/>
</authorization>
<location path="Content">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
<location path="Scripts">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
Are the authorization web.config elements getting in the way? Should I not be using them with ASP.NET MVC?
I actually have a blog post queued up about this that I'll add to the comment when it has been posted, but in the time being here's what's happening.
At a high level, when the ASP.NET runtime sees a 401 response code it will automatically convert it to a redirect and send the user to the login page. What you want to do is bypass that 401.
In MVC, when you're using the AuthorizeAttribute, it checks to see if the user is authorized and, if not, returns an HttpUnauthorizedResult. This basically forces the runtime to redirect the user to the login page. What you want to do is override this behavior.
To do this, extend the AuthorizeAttribute as such:
public class AuthorizeWithAjaxAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.Result is HttpUnauthorizedResult && filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.StatusCode = 200;
filterContext.Result = /* Some result recognized by the client */
}
}
}
When you make an AJAX request and the data of the response is equal to what you return as the filter context's result, simply redirect the user to the login page via javascript.
Yes, this problem is fairly common. It also has a simple solution - in the part of the Javascript which is handling the AJAX response, check for a timeout before using the response. If the timeout has occurred, redirect the user to the login page.
Alternately, if the server-side script is itself checking for a session timeout (which appears to be your case), it becomes even simpler. Do not return an entire login page, instead just return a flag like 'need_login'. Further, simply write the Javascript handler to check if the returned value is 'need_login' and load the login page if true.