ASP.NET Active Directory Nested Authorization Issue - asp.net

I'm working on an internal ASP.NET application that uses an Active Directory distribution list for managing who has access to the web site.
However, due to the fact that this distribution list could contain both users and groups, I had to develop a solution for checking to see if the current user is able to access this site (e.g. They could be in a group that is a part of this distribution list). The default Windows authentication mode does not support this type of hierarchical structure.
My question is how can I ensure that every resource in this web site can only be accessed by those who are in this distribution list? I am currently using a custom attribute applied to every page that checks the user's credentials and redirects to a 'No Access' page if they are not a member of the DL. However, I'm thinking that there must be a better way to do this that doesn't require me to use the attribute on every page which is created for this site?
Any help is appreciated!

The simplest fix to avoid duplication without changing the underlying authentication scheme - Instead of using it on every page, you could do hook into the Session_Start event and store the authentication value there, and check this value on an appropriate event of your master page if you have one. (again this is least effort and an answer directed at your direct question)

Update (Response to Comment)
To manage permissions for a group use the following xml block. Note that this will do what you mentioned in your comment on the other answer: this will block image files, etc... too.
<authorization>
<allow roles="domain\group"/>
<deny users="*"/>
</authorization>
Original
The best way is to stick to the native options: Why not use the Membership Provider? The ASP.Net membership provider can handle all of this for you. You can specify which groups can access which pages/directories using web.config files no sweat.
Check out these links for further guidance on implementing the Active Directory membership provider:
http://msdn.microsoft.com/en-us/library/system.web.security.activedirectorymembershipprovider.aspx
http://blogs.msdn.com/b/gduthie/archive/2005/08/17/452905.aspx
This XML shows how you can configure your web.config, once you are using the membership provider, so that it allows/denies permission to files and folders (I got this from http://support.microsoft.com/kb/316871):
<configuration>
<system.web>
<authentication mode="Forms" >
<forms loginUrl="login.aspx" name=".ASPNETAUTH" protection="None" path="/" timeout="20" >
</forms>
</authentication>
<!-- This section denies access to all files in this application except for those that you have not explicitly specified by using another setting. -->
<authorization>
<deny users="?" />
</authorization>
</system.web>
<!-- This section gives the unauthenticated user access to the Default1.aspx page only. It is located in the same folder as this configuration file. -->
<location path="default1.aspx">
<system.web>
<authorization>
<allow users ="*" />
</authorization>
</system.web>
</location>
<!-- This section gives the unauthenticated user access to all of the files that are stored in the Subdir1 folder. -->
<location path="subdir1">
<system.web>
<authorization>
<allow users ="*" />
</authorization>
</system.web>
</location>
</configuration>

I ended up rolling my own security class for checking to see if the currently logged in Active Directory user has access.
I used the GroupPrincipal.GetMembers function in the System.DirectoryServices.AccountManagement namespace. This overloadedd method which takes a boolean value can be used to search for users recursively (satisfying my groups-within-groups issue).
The security class is a Singleton, and the list allowed active directory users is stored inside the Singleton to keep this access check fast. I chose a Singleton to ensure that there was only 1 copy of this list on the server. I stored the list of allowed users as a SortedDictionary, which increased look-up speed greatly.
When a user who does not exist tries to access the site, the original user lookup will come back negative. At this point, the security class refreshes the users list, saving the timestamp of this refresh to the list of allowed users. The method endures that this refresh is done at most once every 10 minutes to prevent users from hammering the site (and keeping the site responsive for other users).

Related

IIS 7.5 and making anonymous authentication/forms authentication play nicely together

I've got an ASP.NET MVC 4 application that I run under the site level of an IIS web site.
So the dir structure looks like this:
\IIS
\Site
\bin
\Content
\Views
The MVC 4 app uses Forms Authentication via Username and Password, but I have a requirement to lock down the full site and turn off anonymous authentication at the IIS level.
The goal of this requirement is to allow users only to land on a home page and logon page. The problem is if I turn off anonymous authentication then users can't even get to home or login.
Another thing we want to prevent a user from being able to go to /Content/Scripts/MyScript.js in their browser.
I'm using bundling so those file are there and don't get used by me besides when I bundle things up.
Is this even possible since IIS and MVC 4 auth are at completely different level? If it is possible what options do I have?
Chris Pratts answer is correct. You can successfully turn of anonymous authentication and let MVC4 handle all of that for you.
Make sure in your web.config you have the following
<modules runAllManagedModulesForAllRequests="true"></modules>
In your system.webserver section.
Another thing you can do is make use of the locations tags in IIS to prevent user access to different parts of the site.
For example, you could put this in your web.config
<location>
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location>
This ensures that only authenticated users can access the site. You can then further refine this.
<location path="External">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
Basically, now any request to /External will be allowed for all users (regardless of authentication). You will probably want to put all your scripts in here that you need unauthenticated users to access.
If there was a specific directory you didn't want anyone to access, you could do something like
<location path="/Content/Scripts">
<system.web>
<authorization>
<deny users="*" />
</authorization>
</system.web>
</location>
Now any access to that location will be prevented by default in IIS. Give that a try, it should satisfy your requirement to have the scripts available for bundling, but not accessible if someone browses directly to it.
I only halfway got what I wanted, but here is what I ended up doing. I have anonymous authentication enabled at the site level and used Forms authentication for specific controllers. This was how I originally had it so nothing changed here.
Since I am using bundles the users never really need to look at the .js so I used Request Filtering by file extension so block any .js and even .css I don't want exposed.
This works because the bundling doesn't make http requests to those files and the bundles themselves don't have the normal JavaScript and CSS file extensions.
You don't handle this at the IIS-level. You simply allow Anonymous Auth and then add [Authorize] to every controller. Then only on your home and login actions add the attribute [AllowAnonymous].
As to the second part of your question, you can't really stop this. MVC bundles on the fly, so it needs the actual files to be there. If they're never referenced, though, they're black holes: the user would have no way of knowing what file to request, so it's kind of security by obscurity.

ASP.NET Roles and Membership

I am quite new to ASP.NET technologies
<configuration>
<system.web>
<authorization>
<allow roles="Agency,Admin"/>
<deny users="*"/>
</authorization>
</system.web>
</configuration>
I have the above web.config for a folder, there is a requirement to give an elevated priviledge to some users in Agency role to access a page called AddOrganisation.aspx.
To solve this, I think I can add the following markup to the web.config but this will be static
<location path="AddOrganization.aspx">
<system.web>
<authorization>
<allow users="wale, etc, etc"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
How can I enable adding users programmatically instead of updating the web.config for each change?
I will be grateful for your responses. Thank you
You should not make changes the web config at runtime, this will restart your application, every time you add a user. MSDN says this
Configuration Changes Cause a Restart of the Application Domain
Changes to configuration settings in Web.config files indirectly cause
the application domain to restart. This behavior occurs by design. You
can optionally use the configSource attribute to reference external
configuration files that do not cause a restart when a change is made.
For more information, see configSource in General Attributes Inherited
by Section Elements.
Instead you should give those users a different role, so that only those users can access the
"AddOrganisation.aspx" page.
Or else you can also do another thing if you dont want to create another role for these users. You keep on adding these users to a table and whenever a request is made to the page you can check if the users name is present in the table or not and then allow/deny the user.

How to test asp.net location folder authorization programmatically

I have an location element in my web.config like so:
<location path="Admin">
<system.web>
<authorization>
<allow roles="Domain\Development"/>
<deny users="*" />
</authorization>
</system.web>
</location>
This works to only allow members of the development group access to this folder.
I was wondering if there is a way to simply test if a user has access to this folder?
One scenario is creating menu items. I'd simply like to hide or not render links to pages in this folder if the user does not have the proper rights.
Is there a way to do this in code. I don't want to have to hard code a check for membership in Domain\Development rather I'd like to use asp.net to tell me if this current user has access.
This would be nice if the rules get more complicated etc. Also having this in one place enforces DRY (Don't Repeat Yourself).
Through a related question I found the answer. Call UrlAuthorizationModule.CheckUrlAccessForPrincipal. (Documentation is here.) You can give it a path and a user (such as Page.User for the current user), and it will tell you if the user can access that path.
I like this for exactly the reason you mentioned: You can put your access logic in one place.

Need to show pages without logging in (asp.net)

I am using
<authentication mode="Forms" >
<forms loginUrl="login.aspx"
name=".ASPXFORMSAUTH" />
</authentication>
Every thing works fine except that, there are some pages like About Us, Contact Us, Privacy Policy etc, which do not need to login to view them.
In my case i need to login to view all pages. I want these common pages to be viewable without having to log on.
I have tested my application on local IIS as well as on deployment server, but same problem occurs.
Please help!
Thanks for sharing your valuable time.
You need to create exceptions to your security policy:
<!-- files in the "Public" folder don't require authorization -->
<location path="Public">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
Alternately, you can make page-specific exceptions:
<location path="AboutUs.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
Appearantly you want some pages to be available without logging in. The way to go about this is to set permission on subdirectories instead of the website root, and put these pages in the web root (usually they are in teh root)
If thats too much work, put your pages in a directory and allow anonymous users to access it.
There could be a large number of possible answers to such an open question. We will need more specifics to answer. Here are just a few places to look:
Have you checked your web.config file to see if anonymous authentication is off?
Have you checked the web.config to see if you are denying anonymous users access to your root directory?
Have you checked IIS to see if anonymous authentication is off?
Have you checked the pages' source code files to see if you are doing manual denial of service to anonymous users?

Add authentication to subfolders without creating a web application

We have an existing publicly accessible web application with user controls, data access libraries, graphics, etc. We want to create a new secure section of the site that accesses some of the already existing resources.
Initially we created the new section of the site as a virtual directory which (we hoped) would allow us to access the parent site's resources. We added the appropriate location information to the base web.config (authentication and authorization) but we continue to see the following error "Parser Error Message: It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level. This error can be caused by a virtual directory not being configured as an application in IIS."
In response to that error we created the directory as a new application. This allows us to authenticate properly but has the drawback of not being able to access any of the resources in the parent directory (since it's outside the application scope).
Is there any way to secure the new section of the site while at the same time utilize the already existing resources?
In your web.config file in the root of your site, if you add:
<location path="relativePathToDir">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>
This is working for me using FormsAuthentication, the user gets redirected to the default login page if not authenticated
I typed up a summary since many were facing the same situation regarding subfolder authentication.
Subfolder Authorization
ASP.NET can only have a single
authentication mode for one
application.
The different
applications CANNOT share resource
among them.
Scenario
Let's say the home page should not prompt login dialog. It should let users pass through without whatever login is. However, in the same applicatiopn, in a different folder presumably, another page needs to check user permission against database table using user network login ID. By default IE treats all web site with only hostname a Intranet. By its default intranet setting, it will not prompt the login dialog and pass user login and password to the application if Windows Authentication is used. However, the tricky party is, if the application has an actual domain, IE will think it is an Internet site, and it will prompt the login and password if Windows Authentication is used.
The only way to not to promopt login dialog for Internet site using Windows Authentication, is to also turn on the anonymous authentication in IIS. However, you will lose the ability to capture the login info because the Anonymous has precedence over the Windows Authentication. The good news is there is a way to resolve that issue. If an application subfolder needs to capture the login information, you will need to overwrite the parent authorization in Location element in web.config.
1 In IIS, configure Authentication as follows:
Enable Anonymous Authentication,
Enable Windows Authentication
2 Add the followings in Web.Config.
<authentication mode="Windows" />
<authorization>
<allow users="*" />
</authorization>
<!-- secured is the relative subfolder name. deny anonymous user, so only the authenticated login will pass through -->
<location path="secured" allowOverride="true">
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location>
Remove the application, then add this to the top-level web.config:
<configuration>
<system.web>
<!-- applies application wide -->
</system.web>
<location path="securedirectory" allowOverride="false">
<system.web>
<!-- applies only to the path specified -->
</system.web>
</location>
</configuration>
MSDN Reference

Resources