Using ASP.NET Webforms + VB/C#
I've been tasked to restrict ASP.NET page access to users not in specific roles. And I need to be able to live test my solution (versus unit testing where I could use mocks or fakes). Our site is rather complex so I doubt I'd be able to find all the "gotchas" with just unit testing.
I have something on my development computer I'm pretty sure will work in Production: I have a custom Role Provider hooked into the web.config file. It's being initialized and called when I debug the website, so I'm reasonably sure it's working OK. I have a folder ("Administration") marked only for a specific role. Our roles are defined in our own database and are not tied to Microsoft or Windows roles/permissions.
The problem is: I cannot actually login as a user I wish to debug with. I can "simulate" this with a special development-only start page. That works OK for our menu/navigation items which are built from the roles database, but of course without restricting pages (with a Role Provider or something similar), you can still manually type in a page and it will be served up. This devel-only start page sets the username I pass in as a FormsAuthentation auth cookie.
FormsAuthentication.Initialize()
FormsAuthentication.SignOut()
FormsAuthentication.SetAuthCookie(simUserName, True)
It seems like when I first start debugging (usually after a reboot), the custom Role Provider will get called with the same username I'm simulating, but after a while, that suddenly stops and my local Windows name gets passed instead. (Cookie issue?) After that, it no longer works.
Anyway -- Is there a way I can test roles locally during development or will we just have to put this on Production and hope for the best.
I didn't know what code or settings would be useful at this time, so let me know what you need. Thanks!
I think I've found the solution to my problem. I decided to work with some of the samples in Ch. 7 of Beginning ASP.NET Security by Barry Dorrans (published by Wrox) in the hopes working with authentication and authorization in simple examples might lead to a solution, and it did.
One of the examples of using Forms authentication (pp. 155-157) showed a simple login.aspx page similar to my development startup page. The code I had in my Development startup page (shown above) was incorrect. It should have been:
FormsAuthentication.Authenticate(simUserName, simUserPassword)
FormsAuthentication.RedirectFromLoginPage(simUserName, False)
I also defined my simulated users in my Development web.config:
<authentication mode="Forms">
<forms defaultUrl="default.aspx" loginUrl="mystart.aspx">
<credentials passwordFormat="Clear">
<user name="GeorgeWashington" password="password"/>
<user name="AndrewJackson" password="password"/>
<user name="AbrahamLincoln" password="password"/>
<user name="TeddyRoosevelt" password="password"/>
<user name="JackKennedy" password="password"/>
</credentials>
</forms>
</authentication>
When FormsAuthentication.Authenticate is called and authenticates which ever of the simulated users I choose, it looks like it causes ASP.NET to now always use this username in calls to the Role Provider. At least, this appears to be working when debugging the website.
I've set the project to always call my Development startup page (the login page -- mystart.aspx). This way I always start with a fresh authentication in case I need to work with a different role.
Anyone wanting to use this solution, WARNING: Your Development startup page must NEVER be used for the Production website. Likewise, NEVER use usernames and passwords in a Production web.config. This is ONLY for debugging and testing. Depending how you version control your development and production code, you may have to manually merge certain changes going from development to production in order to avoid sending this debugging code to Production.
I also understand Microsoft wants us to use a Membership Provider in place of the FormsAuthentication class. And in Production code, we should. But for my purposes (ensuring I can interact with the website in differing users/roles while debugging), this appears to be the simplest solution.
EDIT -- one more piece of the puzzle: I was still getting strange method calls in my Custom Role Provider. A few times a method would be called with the username I logged in with on my login page (this is what I expected); most often it was my Windows username which led me to believe Windows authorization was still being used by ASP.NET somewhere. After more research (thanks SO!!!), I found in my applicationhost.config file for my project, Windows authentication was set to True which I guess was causing a conflict with my web.config file. Setting the values in applicationhost.config to:
<location path="MyWebSite">
<system.webServer>
<security>
<authentication>
<anonymousAuthentication enabled="true" />
<windowsAuthentication enabled="false" />
</authentication>
</security>
</system.webServer>
</location>
seems to have solved that issue.
If you have any questions, or need more information, please let me know.
Related
I have a problem with my project Asp.net mvc 1.0, with .net framework 2.0. My application is hosted on a IIS 7.5. My authentication form looks like this:
<authentication mode="Forms">
<forms protection="All" loginUrl="~/Account/LogOn" timeout="60" cookieless="UseUri" />
</authentication>
<httpRuntime executionTimeout="1000" maxRequestLength="600000" />
<sessionState mode="InProc" cookieless="UseUri" timeout="60">
</sessionState>
When a user connects to the webpage, he receives a session id which is stored in the URL. When I connect to my webpage with the default UserAgent (in every browser, Chrome/FF/IE) everything works fine. When I override the browser UserAgent and try to connect with the User agent XXXXXXXX.UP.BROWSER, I receive an infinite redirection loop to address
http://<IP>_redir=1
But when I connect to the default webpage IIS - the user agent doesn't matter and everything loads fine, so it must be a problem with the specified UserAgent and my Application. I tried to find any filters for that XXXXXXXX.UP.BROWSER UserAgent but there aren't any. When I studied application lifecycle I tried to find the differences between good connection and wrong connection and found that functions which are NOT executed are:
Application_AcquireRequestState
Application_PostAcquireRequestState
Application_PreRequestHandlerExecute
Application_PostRequestHandlerExecute
Application_ReleaseRequestState
Application_PostReleaseRequestState
Application_UpdateRequestCache
Application_PostUpdateRequestCache
and another clue I found is that there is no Session in "wrong" connection - Session object is null.
To sum it up: The connection to my application web page with a specified user agent makes an infinite redirection loop, probably because of the lack of the session ID. What could be the problem ?
EDIT: I discovered that User Agent that contains "UP.Browser" is related to mobile. When I changed cookieless to "UseCookies" everything works. Why option "UseUri" doesn't work for mobiles?
EDIT2 : /admin -> my webpage hosted on specified IP address.
Good connection :
Wrong connection:
Sorry, I don't know how to make these images bigger.
http://msdn.microsoft.com/en-us/library/aa479315.aspx
So you're putting two different values into the URI, one for session and one for forms, which would probably create a lengthy URI:
"The principal limitation of this feature is the limited amount of data that can be stored in the URL. This feature is not targeted at common browsers such as IE, since these do support cookies and do not require this feature. The browsers that do not support cookies are the ones found on mobile devices (such as phones), and these browsers typically severely limit the size of the URL they support. So, be careful when you use this feature—try to make sure that the cookieless string generated by your application is small."
My guess is that the key to the infinite redirect loop is this functionality:
"// Step 5: We can't detect if cookies are supported or not. So, send a
// challenge to the client. We do this by sending a cookie, as
// well as setting a query string variable, and then doing a
// redirect back to this page. On the next request, if cookie
// comes back, then Step 3 will report that "cookies are
// supported". On the other hand, if the next request does not
// have any cookies, then Step 4 will report "cookies not
// supported".
SetAutoDetectionCookie();
Redirect(ThisPage + Our_auto_detect_challenge_variable);"
Unfortunately, this sounds like a bit of an architecture rethink, as it's probably going to now matter what the full path to your site is and you may have to drop automatic handling of forms authentication.
As you said the issue is for mobile browsers, I think this issue is limited to the devices(MOBILE) where the cookies are not supported and the Size of the URL increases and mobile browser severely limit that size, as mentioned in the MSDN reference article above.
My solution was to change User Agent containing "UP.Browser" to something else using rewrite rule. Everything works fine ;)
Edit: I found another clue.
In mobile browser - these with user agents containing "UP.Browser", it was necessary to add slash at the of the address.
In conclusion:
Everything works fine for user agents not related with "UP.Browser".
User agents containing "UP.Browser" needed address like:
http://addr/controller/
I don't know why it is necessary. Any ideas?
I’ve got a weird intermittent issue with MVC4 / IIS / Forms Authentication.
I’ve got a pair of sites that pass control to each other using SSO. Most of the time the handover occurs correctly and the user is redirected to the next site as intended. However, in some cases, the user is asked to log in again, even though valid SSO information was sent across. The SSO method is decorated with the [AllowAnonymous] attribute and the web.config also has a location entry granting access to /account/sso to all users.
It appears to occur when the destination site is being hit for the first time - once the app pool is warmed up, the issue disappears.
Some other points:
1 both sites are .net 4, so there should not be any legacy encryption issues.
2. this issue happens quite rarely (<10% of the time) so the code itself should be sound
3. Hosting is IIS 7.5 on win7x64 locally, and azure - happens in both places
4. Seems to be browser independent
<location path="account/sso">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
[Authorize]
public class AccountController : BaseControllerTestable
{
public AccountController()
: base()
{
}
[AllowAnonymous]
public ActionResult SSO(string AuthToken, string Target)
{
//SSO logic here
}
}
Any ideas?
You have an Authorize attribute on your Controller class which means that your SSO method would have AllowAnonymous and Authorize applied to it. In this instance the Authorize attribute looks like it needs to be removed.
What is your BaseControllerTestable? Do you have any authorization attributes there? Your Base class will be instantiated firs before it will get to your other methods on the derived class. So if by any chance you have [Authorize] on the base controller that may be an issue for you.
I think I've finally resolved it (we'll only know for sure once we've had a good while without recurrence given that it was intermittent anyway)
A couple of factors came into play. Firstly I noticed a few static items (css+js files mostly) that were getting caught up in authentication loop even though they should be freely accessible, so I added a location rule in web.config to make sure they were allowed to anonymous users. I also added a route exception to ignore favicon.ico requests for good measure too. This seemed to stop the code from tripping over itself when authenticating for the first time. Finally, the reason the issue was intermittent was due to another bug where if there was any other sessions open (db driven) the issue didn't occur. this explained why the bug only happened early in the morning ie: all the sessions from the previous day had expired.
Running the ASP.NET webforms run the application works fine. When the application is idle for 4 to 5 minutes, it is giving this error:
Validation of viewstate MAC failed. If
this application is hosted by a Web
Farm or cluster, ensure that
configuration specifies
the same validationKey and validation
algorithm. AutoGenerate cannot be used
in a cluster.
How can this be solved?
This free online tool: http://aspnetresources.com/tools/machineKey generates a machineKey element under the system.web element in the web.config file.
Here is an example of what it generates:
<machineKey validationKey="1619AB2FDEE6B943AD5D31DD68B7EBDAB32682A5891481D9403A6A55C4F91A340131CB4F4AD26A686DF5911A6C05CAC89307663656B62BE304EA66605156E9B5" decryptionKey="C9D165260E6A697B2993D45E05BD64386445DE01031B790A60F229F6A2656ECF" validation="SHA1" decryption="AES" />
Once you see this in your web.config, the error itself suddenly makes sense.
The error you are getting says
"ensure that configuration specifies the same
validationKey and validation algorithm".
When you look at this machineKey element, suddenly you can see what it is talking about.
Modifying the pages element under the system.web element may not be necessary with this in place. This avoids the security problems associated with those attributes.
By "hard coding" this value in your web.config, the key that asp.net uses to serialize and deserialize your viewstate stays the same, no matter which server in a server farm picks it up. Your encryption becomes "portable", thus your viewstate becomes "portable".
I'm just guessing also that maybe the very same server (not in a farm) has this problem if for any reason it "forgets" the key it had, due to a reset on any level that wipes it out. That is perhaps why you see this error after an idle period and you try to use a "stale" page.
See http://blogs.msdn.com/tom/archive/2008/03/14/validation-of-viewstate-mac-failed-error.aspx
This isn't your problem but it might help someone else. Make sure you are posting back to the same page. Check the action on your form tag and look at the URL your browser is requesting using Firefox Live HTTP Headers.
I ran into this because I was posting back to a page with the same name but a different path.
Modify your web.config with this element:
<pages validateRequest="false"
enableEventValidation="false"
viewStateEncryptionMode ="Never" />
Any more info required, refer to the ASP.NET Forums topic
We have a web application where we are using global.asax for url rewriting. We use a compiled version of the site on the live server.
As a part of modification request, we had to add some custom native AJAX code where javascript would call a webservice to update the content of the page. For being able to call the webservice with extension .asmx, we modified the url rewriting code to handle asmx requests seperately.
this arrangement works fine on the local machine, but when we publish the site and deploy it on the live server, the new code doesnt seem to get included. It still skips the condition to check the ".asmx" extension, and throws a page not found exception considering the webservice name as a page name.
We have tried looking all over and googled for such things as well.. but no avail..
any pointers on what might be going wrong.. ?
Assuming your URL rewriting is good (isn't that normally implemented as a HttpModule?) I'd check to make sure that there's an ISAPI mapping in IIS on production that sends .asmx requests to ASP.NET.
If you think your changes to the global.asax haven't been rejitted then you can always stop the application pool, go find your web applications compiled bits in c:\windows\microsoft.net\framework[version]\temporary asp.net files... and delete the jitted version. I've seen ASP.NET miss when it comes to Jitting changes before.
I'd also consider running http fiddler (IE) or tamper data (FireFox extension) on one of the pages that makes calls to the web service. It will tell you exactly how the page is calling the web service and you can validate that the called URL is correct.
There is machine.config file where you can add HttpModules. I also think that you can do that through web.config.
One reason I can think of is that in the Web.config, you might have configured the routing module in the system.web section but not in system.webServer (or at least forgot something in there).
I the similar problem before and the solution was to remove the module & add it again in the system.webServer section of the Web.config like this:
<system.webServer>
<modules>
<remove name="UrlRoutingModule" />
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, e="RoleManager" type="System.Web.Security.RoleManagerModule"/>
</modules>
</system.webServer>
It might be a different module or handler but the idea is basically the same. It's important to "remove" the module first.
I am using ASP.NET C#.
How do I implement URL re-writing procedure that is similar to StackOverflow.com?
http://stackoverflow.com/questions/358630/how-to-search-date-in-sql
Also, what is the meaning of values such as "358630" in the URL? Is this the question ID (the basis for which they use to fetch the data from the table)? Whatever it is, in my application I am identifying records using an "ID" field. This field is an identity column in an SQL table. Right now, my URLs are like the following:
http://myweb.com/showdetails.aspx?id=9872
But I'd like them to appear like:
http://myweb.com/showdetails/9872/my_question_title
Or:
http://myweb.com/9872/my_question_title
Or whatever the best way, which will taste good to search bots.
My application is hosted on Go Daddy's shared hosting service, and I feel that no customized ASP.NET "HTTP module" or no customized DLL for URL re-writing is working on their server. I tried many samples but no luck yet!
I found that Stack Overflow is hosted on Go Daddy (shared hosting?). Maybe Stack Overflow's method will work for me.
SO is using ASP.NET MVC. You really need to read in details how MVC URL rewriting works, but the gist of it is that the 'questions' part in the URL is the name of the Controller class (which roughly corresponds to the 'showdetails' in your URL) and the number is a ID parameter for the default action on that Controller (same as the parameter 'id' in your URL).
Since MVC isn't an option you can try redirecting the 404s. This will work in ASP.NET 1.1 and above: Redirect 404s and 405s to your own handler using either IIS config or web.config, parse out the request in the handler and redirect to the appropriate resource.
<configuration>
<system.web>
<customErrors mode="On" defaultRedirect="error.html">
<error statusCode="404" redirect="newHandler.aspx"/>
</customErrors>
</system.web>
</configuration>
Before the advent of System.Web.Routing, the common practice was to use UrlRewriter.NET. Worked well enough, but could bite you when configuring IIS. I'm not sure if there are any simple ways of using the new Routing classes in ASP.NET (i.e., drop it in and go vs. refactoring code).
please explain the meaning of values
such as "358630" in the URL
That is (presumably) the ID for the question in the database. In the MVC model
myurl.com/questions/358630
is analogous to
myurl.com/questions.aspx?id=358630
The question title on the end of the URL is actually being ignored by the app. It's generally "tacked on" for search engine optimization and human readability purposes. In fact, you can change the title of this question in the URL and notice the page still loads just fine.
The new System.Web.Routing dll is part of ASP.NET 3.5 SP1, and is bin deployable on ASP.NET 3.5, so you could use the features of that on a classic ASP.NET WebForms site.
You'll probably want to take note of Phil Haack's comments in his post on using MVC on IIS 6 as you'll probably need to include the .aspx extension in your routed urls
http://www.mysite.com/controler.aspx/action/id
You might also want to check out Questions Tagged SEO.
The ignored question name at the end of the url is often called a "Slug", and is used for SEO purposes to include the page title in the url.