Hello fellow developers,
I'm a little new to working with cookies in ASP.NET and I apologize if this is a basic question. So I have the following code in my web.config that I was kind of playing around with to get an understanding of cookies.
<httpCookies httpOnlyCookies="true" requireSSL="true"/>
Now here is my question. I created a cookie in two ways (which I needed to secure).
One way I secured it was with this code-
protected void btnSubmit_Click(object sender, EventArgs e)
{
HttpCookie cookie = new HttpCookie("UserInfo");
cookie.Secure = true; // secure the cookie
cookie["username"] = txtEmail.Text;
if (txtEmail.Text != "")
{
Response.Cookies.Add(cookie);
}
Response.Redirect("WebForm2.aspx");
}
Now when I used this code to create it, I got the UserInfo cookie to be secured in this way was well.
protected void btnSubmit_Click(object sender, EventArgs e)
{
Response.Cookies["UserInfo"]["userName"] = txtEmail.Text;
}
Now here is my question. Why does using "Response.Cookies" default to using the settings in the web.config? How come when I create a cookie using HttpCookie I have to secure it by setting it to true in the CS code? My best guess was that since I'm creating an instance of HttpCookie, that is why but I wanted further direction on this.
Many Thanks!
Why does using "Response.Cookies" default to using the settings in the
web.config?
The simple/direct answer is this is by design.
Assuming the cookie doesn't existing when you call
Response.Cookies["UserInfo"]["userName"] = txtEmail.Text;
The cookie is created for you and assigned the value but is given the defaults that you specified in:
<httpCookies httpOnlyCookies="true" requireSSL="true"/>
However, if you were to instantiated as you already pointed out and add it to the collection, it will use those manually set values, which in the case of a new HttpCookie, the Secure property default value is false;
As specified in the Cookies Collection docs
You can get up close and personal with the code for HttpCookieCollection as well to see more of the "why".
Related
On authentication, the email of the user is retrieved and stored as session data like below
protected void Page_Load(object sender, EventArgs e)
{
string email = Request.Form["email"];
HttpContext.Current.Session["email"] = email;
Server.Transfer("Home.aspx");
}
The email is successfully stored but when I navigate to a new page and try to retrieve the session value like below. I see the session has been lost
var email = (string)Session["email"] ?? "";
I already enabled session state in web.config
<pages enableSessionState="true">
<sessionState timeout="20" mode="InProc">
Setting rquireSSL to false in web.config fixed the issue.
<httpCookies requireSSL="false" />
In asp.net application when use server.transfer its not work,because instead of use server.transfer use Response.Redirect("Home.aspx")
That's why causing issue not getting session value in home page.
Also check this link hope will usefull for this
Server.Transfer causing Session exception
We previously had cookieless session enabled on our application. We have disabled this and gone to session cookies however we are having a problem. Users who had the session ID in their URL as a bookmark are still able to navigate to the site with the session id in the url. I have set it to not regenerate expired sessions but it is still allowing it anyways. It also ends up creating a session cookie in addition and then we are getting random session loss. I've come up with a few wonky workarounds like stripping it out using a URL rewrite and stripping it out via javascript but this seems bad. Is there anything built in that I am missing that can help with this? Not that it should matter for this but I will add we are using state server.
For anyone else looking for a solution that won't require users to update their bookmarks I was able to use the following in my Global.asax Application_BeginRequest:
void Application_BeginRequest(object sender, EventArgs e) {
if (CookielessValuesExist()) {
Response.Redirect(Request.Url.OriginalString, true);
}
}
private bool CookielessValuesExist() {
string cookieless = Request.Params["HTTP_ASPFILTERSESSIONID"];
if (string.IsNullOrWhiteSpace(cookieless)) {
return false;
}
return true;
}
A URL rewrite is a good solution to this.
However if you would like users to update their bookmarks, (so you can eventually retire the URL rewrite) you might consider having your URL rewrite send them to a page telling them so:
Oops! That link doesn't work.
And then giving them the usual options to log in etc.
I have an ASP.NET site that must use Forms Authentication and not Windows Authentication to access a ActiveDirectoryMembershipProvider. The site must use forms because they need a designed input form instead of the browser authentication popup that Windows authentication uses.
The site needs to impersonate the user logged in via Active Directory to access user specific files.
However, the WindowsIdentity.GetCurrent() is not the same as the HttpContext.Current.User.Identity although my web.config contains:
<authentication mode="Forms">
<forms loginUrl="login.aspx" timeout="480"/>
</authentication>
<identity impersonate="true" />
I cannot use LoginUser() and the WindowsIdentity.Impersonate() because I need to impersonate as the AD user to get their specific permissions, and I don't know the user's password because Forms takes care of logging in.
Is it possible maybe from the login.aspx.cs, to take the System.Web.UI.WebControls.Login.Password, then save the LoginUser() token in a session variable for WindowsIdentity.Impersonate() later? Or maybe a much more secure method of Impersonating the right way?
I'm confused why Forms authentication can't automatically <identity impersonate="true" />
I've read this http://msdn.microsoft.com/en-us/library/ms998351.aspx but it uses Windows Authentication.
Impersonating a user using Forms Authentication can be done. The following code does work.
The Visual Studio Magazine article referred to by Robert is an excellent resource. There are a some issues with the example code in the article, so I've included some working code below.
Note: If you are using Visual Studio, make sure to launch it "Run as Administrator" to avoid problems with UAC blocking impersonation.
// in your login page (hook up to OnAuthenticate event)
protected void LoginControl_Authenticate(object sender, AuthenticateEventArgs e)
{
int token;
// replace "YOURDOMAIN" with your actual domain name
e.Authenticated = LogonUser(LoginUser.UserName,"YOURDOMAIN",LoginUser.Password,8,0,out token);
if (e.Authenticated) {
Session.Add("principal", new WindowsPrincipal(new WindowsIdentity(new IntPtr(token))));
}
}
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, out int TokenHandle);
// in global.asax.cs
void Application_PreRequestHandlerExecute(object send, EventArgs e)
{
if (Thread.CurrentPrincipal.Identity.IsAuthenticated == true && HttpContext.Current.Session != null) {
WindowsPrincipal windowsPrincipal = (WindowsPrincipal)Session["principal"];
Session["principal"] = (GenericPrincipal)Thread.CurrentPrincipal;
Thread.CurrentPrincipal = windowsPrincipal;
HttpContext.Current.User = windowsPrincipal;
HttpContext.Current.Items["identity"] = ((WindowsIdentity)windowsPrincipal.Identity).Impersonate();
}
}
// in global.asax.cs
void Application_PostRequestHandlerExecute(object send, EventArgs e)
{
if (HttpContext.Current.Session != null && Session["principal"] as GenericPrincipal != null) {
GenericPrincipal genericPrincipal = (GenericPrincipal)Session["principal"];
Session["principal"] = (WindowsPrincipal)Thread.CurrentPrincipal;
Thread.CurrentPrincipal = genericPrincipal;
HttpContext.Current.User = genericPrincipal;
((WindowsImpersonationContext)HttpContext.Current.Items["identity"]).Undo();
}
}
// test that impersonation is working (add this and an Asp:Label to a test page)
protected void Page_Load(object sender, EventArgs e)
{
try {
// replace YOURSERVER and YOURDB with your actual server and database names
string connstring = "data source=YOURSERVER;initial catalog=YOURDB;integrated security=True";
using (SqlConnection conn = new SqlConnection(connstring)) {
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT SUSER_NAME()", conn);
using (SqlDataReader rdr = cmd.ExecuteReader()) {
rdr.Read();
Label1.Text = "SUSER_NAME() = " + rdr.GetString(0);
}
}
}
catch {
}
}
Update:
You should also handle Application_EndRequest, because calls like Response.End() will bypass Application_PostRequestHandlerExecute.
Another issue is that the WindowsIdentity may get garbage collected, so you should create a new WindowsIdentity and WindowsPrincipal from the logon token on every request.
Update2:
I'm not sure why this is getting downvoted, because it works. I've added the pinvoke signature and some test code. Again, launch Visual Studio using "Run as Administrator". Google how to do that if you don't know how.
If your users are using IE then you can turn on integrated security for the website and your users will be authenticated silently (no login dialog, no login page). Your impersonation will then work. If you need to target other browsers then this may not work (the user will probably be presented with a login dialog).
Your current impersonation will never work because your users are logging in using an account other than their domain account. You can't expect the site to impersonate a user which hasn't supplied his credentials. That would go against basic security principals.
You may find this useful:
how to create a login screen that
allows Admin users to log in as
another user in the user database
EDIT
On reading your question more closely, I am not sure if that approach would work with your scenario though; when you login using Forms Authentication and Impersonate Active Directory user
We got the same problem recently, the customer wanted their users can log in by AD account and then this credential must be used to access Analysis Service as well as all other databases. They wanted it that way because they implemented an auditing system and all access must be done by current logged in account.
We tried Forms authentication and Win32 LogonUser() API for impersonating part, it worked but it also asks us for user's password as plain text. Later, we decided to utilized Windows authentication, it saves us lot of time (no more AD authentication, impersonate manually). Of course, there was also no fancy login page.
For just in case, and a bit late, i found something that works for me and it's really simple but of course is only for testing purposes...
Just set a cookie with your username.
//Login button. You can give whatever input to the form
protected void Login_Click(object sender, EventArgs e)
{
FormsAuthentication.SetAuthCookie("your_username", createPersistentCookie: true);
Response.Redirect("~/");
}
Any comments accepted...
I would like to auto-redirect to login page when session time outs.
In web.config file, i have the following code
<configuration>
<system.web>
<sessionState mode="InProc" timeout="1"/>
</system.web>
</configuration>
In Global.asax file-
protected void Session_End(object sender, EventArgs e)
{
Response.Redirect("LoginPage.aspx");
}
But after time-out, i am receiving the following error:
HttpException was unhandled by user code.
Response is not available in this context.
Any clue to solve this issue?
Session_End is called when the session ends - normally 20 minutes after the last request (for example if browser is inactive or closed).
Since there is no request there is also no response.
I would recommend to do redirection in Application_AcquireRequestState if there is no active session. Remember to avoid loops by checking current url.
Edit: I'm no fan of .Nets built in authentication, Example goes in Global.asax:
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
try
{
string lcReqPath = Request.Path.ToLower();
// Session is not stable in AcquireRequestState - Use Current.Session instead.
System.Web.SessionState.HttpSessionState curSession = HttpContext.Current.Session;
// If we do not have a OK Logon (remember Session["LogonOK"] = null; on logout, and set to true on logon.)
// and we are not already on loginpage, redirect.
// note: on missing pages curSession is null, Test this without 'curSession == null || ' and catch exception.
if (lcReqPath != "/loginpage.aspx" &&
(curSession == null || curSession["LogonOK"] == null))
{
// Redirect nicely
Context.Server.ClearError();
Context.Response.AddHeader("Location", "/LoginPage.aspx");
Context.Response.TrySkipIisCustomErrors = true;
Context.Response.StatusCode = (int) System.Net.HttpStatusCode.Redirect;
// End now end the current request so we dont leak.
Context.Response.Output.Close();
Context.Response.End();
return;
}
}
catch (Exception)
{
// todo: handle exceptions nicely!
}
}
If you are using something like FormsAuthentication for maintaining the security of your application, then this part (that part that you are trying to do) will be done for you. If FormsAuthentication discovers that a user's session has expired it will redirect him or her back to you login page.
Second, don't rely too much on Session_End because it will never trigger if you change session provider from InProc to SQLServer or other out of process provider.
You can use session property IsNewSession to detect whether it is session timeout or not.
The ASP.NET HttpSessionState class has IsNewSession() method that returns true if a new session was created for this request. The key to detecting a session timeout is to also look for the ASP.NET_SessionId cookie in the request.
Definitely I too agree that we should put the below code in some so called a custom BasePage, which is used by all pages, to implement this effectively.
override protected void OnInit(EventArgs e)
{
base.OnInit(e);
if (Context.Session != null)
{
if (Session.IsNewSession)
{
string CookieHeader = Request.Headers["Cookie"];
if((CookieHeader!=null) && (CookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
{
// redirect to any page you need to
Response.Redirect("sessionTimeout.aspx");
}
}
}
}
check this link for more explanations if you want to put the above code in a base page class .
You should use Application_AcquireRequestState
You'll find that Application_AuthenticateRequest no longer has a session context (or never had one).
In my research into this I cam across this post which solved it for me.
Thanks go to Waqas Raja.
asp.net: where to put code to redirect users without a session to the homepage?
I think you are getting "Response is not available in this context" because the user is not making a request to the server, and therefor you cannot provide it with a response. Try Server.Transfer instead.
The easiest way what I feel is to use Meta information and get the trick working. Consider we have a page WebPage.aspx add the below code in the the WebPage.aspx.cs file.
private void Page_Load(object sender, System.EventArgs e){
Response.AddHeader("Refresh",Convert.ToString((Session.Timeout * 60) + 5));
if(Session[“IsUserValid”].ToString()==””)
Server.Transfer(“Relogin.aspx”);
}
In the above code, The WebPage.aspx is refreshed after 5 seconds once the Session is expired. And in the page load the session is validated, as the session is no more valid. The page is redirected to the Re-Login page. Every post-back to the server will refresh the session and the same will be updated in the Meta information of the WebPage.aspx.
you can simply do the following in web.config
<configuration>
<system.web>
<sessionState mode="InProc" timeout="1" loginurl="destinationurl"/>
</system.web>
</configuration>
Since, we can't redirect from Session_End as no response/redirect is present there.By using this you will be redirected to destinationurl when session will timeout.Hope this helps.
aware that there are a lot of questions relating to Forms Authentication and the persistence of cookies, but having spent most of a day delving around, I'm still having difficulties.
My Problem
I am working on a web app (VS2010 but webapp is f/w 3.5) which restricts access to certain parts of the app to authenticated users (whereas other parts are open). My problem is that my authentication cookies do not appear to be persisting after I close the browser.
My Approach
I have written a simple login.aspx page which is configured in web.config as follows:
<authentication mode="Forms">
...and the individual pages' behaviour are declared like so:
<location path="ClientManageAccount.aspx">
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location>
...which works fine in every respect EXCEPT for these cookie shenanigans...
I create the authentication cookie manually once I have authenticated my user's login & password against the database (which works fine) in the login.aspx page. If the user selects the 'keep me logged in' checkbox, the cookie is generated using this method:
private void GenerateAuthenticationCookie(int expiryInMinutes, Guid userGuid)
{
DateTime cookieExpiration = DateTime.Now.AddMinutes(expiryInMinutes); // change to months for production
var authenticationTicket =
new FormsAuthenticationTicket(
2,
userGuid.ToString(),
DateTime.Now,
cookieExpiration,
true,
string.Empty,
FormsAuthentication.FormsCookiePath);
// ticket must be encrypted
string encryptedTicket = FormsAuthentication.Encrypt(authenticationTicket);
// create cookie to contain encrypted auth ticket
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
authCookie.Expires = authenticationTicket.Expiration;
authCookie.Path = FormsAuthentication.FormsCookiePath;
// clear out existing cookie for good measure (probably overkill) then add
HttpContext.Current.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
HttpContext.Current.Response.Cookies.Add(authCookie);
}
The objective here is that I store a user Guid in the auth cookie, which I will then use to restore a user object into session (this is also in the login.aspx page, and my thinking is that I'd like to pluck the user guid from the auth cookie that I have created, and use it to stuff the corresponding user record into session and redirect to the requested page):
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
TryAutoLogin();
}
}
private void TryAutoLogin()
{
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get(FormsAuthentication.FormsCookieName);
if (cookie != null)
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
if (ticket != null)
{
if (ticket.Name.Length > 0)
{
try
{
Guid userGuid = new Guid(ticket.Name);
KUser user = UserFunctions.GetUserFromUserGuid(userGuid);
if (user != null) Session["User"] = user;
FormsAuthentication.RedirectFromLoginPage(userGuid.ToString(), true);
}
catch (Exception anyException)
{
// don't do anything for now - do something smart later :-) }
}
}
}
}
Finally, here is the code for the login button on my login.aspx page:
protected void Submit_OnClick(object sender, EventArgs e)
{
long userId = 0;
UserAuthenticationStatus status;
status = (UserAuthenticationStatus)UserFunctions.UserAuthenticates(EmailAddress.Text, Password.Text, ref userId);
switch (status)
{
case UserAuthenticationStatus.Authenticated:
//email address and password match, account ok, so log this user in
KUser user = UserFunctions.GetUser(userId);
Session["User"] = user;
if (ChkRememberMe.Checked)
{
GenerateAuthenticationCookie(15, user.UserGuid); // 15 minutes
FormsAuthentication.RedirectFromLoginPage(user.UserGuid.ToString(), true);
}
else
{
FormsAuthentication.RedirectFromLoginPage(user.UserGuid.ToString(), false);
}
break;
case UserAuthenticationStatus.AuthButLocked:
// email/pwd match but account is locked so do something
ShowLockedAccountMessage();
break;
case UserAuthenticationStatus.EmailFoundIncorrectPassword:
case UserAuthenticationStatus.EmailNotFound:
case UserAuthenticationStatus.Unknown:
// either the email wasn't found, or the password was incorrect or there was some other problem
// present message stating this and offer chance to register
ShowFailedLoginMessage();
break;
default:
ShowUnavailableMessage();
break;
}
}
As you can see, there's nothing particularly complex going on, but despite the fact that the authCookie which is created in GenerateAuthenticationCookie(..) being created correctly (as far as I can tell) it does not persist.
One thing I have noticed is that if I place some code into the Application_AuthenticateRequest method in global.asax.cs, such as:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get(FormsAuthentication.FormsCookieName);
if (cookie != null)
{
int x = 4; // just a dummy line so that I can set a breakpoint
}
}
...that breakpoint is sometimes hit following a new browser session, although it stops being hit once I navigate away from the startup page (in this case a dummy start.aspx page used purely for dev & testing).
So, apologies for the long question, but I'm truly suffering here.
Things I have checked/tried
Ensuring that the code is being executed - YES
Browser settings - i.e. no deletion of cookies on exit - CONFIRMED NO DELETION
Trying different timeouts - e.g. equal or different to the web.config timeout, doesn't seem to matter...
...and of course at least twenty or thirty different previous questions, but to no avail.
System/Dev Environment
Windows 7 64-bit, VS2010 (but proj is a 3.5), SQL Server 2008 Express.
On my dev server, this problem remains so I'm not sure it's necessarily environmental - that machine is a WS2008R2 box running SQL 2008R2 - and the same problem remains.
Does anyone, anywhere, have any ideas for things I can try here? I have a hunch I could get this working by intercepting the initial Application_AuthenticateRequest hit in global.asax.cs and stuffing something into session state to mark as 'authenticated' (to avoid an expensive authentication attempt every time that method is called, which turns out to be several times per page.
Thanks in advance,
John
OK, having spent all that time writing that, I had a moment of clarity and realised that (a) I didn't need to be doing any of that checking on the Page_Load() as (if this were working properly) the login.aspx page wouldn't be called at all, and (b) I ought to have been able to get to the cookie from the Session_Start - which is where I relocated the TryAutoLogin code.
This in itself was a step forward, but despite retrieving the cookie and therefore the user guid from it, I found that by I was still getting punted back to the login.aspx page.
It was at this point I recalled the fact that I have a parent master page and two child master pages - one which I set for non-authentication pages (e.g. homepage) and one for those pages requiring authentication. I vaguely recalled a problem with session timeouts and had placed the following code in the OnInit override:
if (Session["User"] == null)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
Response.End();
}
...which in itself wasn't so bad (and avoided a nasty bug on timeouts) but also on the start.aspx page, I found this gem:
Session.Clear();
...in the Page_Load!
So, what was happening was that I was inadvertently clearing the session into which I had placed my newly recovered user record. Which meant that the authorisation master page's OnInit override was then detecting the absence of the user object and - ta dah! - signing the user out, which in turn removes the authorisation cookie...
So, a bit of wiring and some sleuthing later, and I can put this one to bed.
Thanks for reading (even if I did figure it out on my own)... :)