I have an app with multiple subdomains, subone.parent.com, subtwo.parent.com.
I have a logon page at parent.com/login. When a user logs in I redirect them to the proper domain based on which one they are a member of. This works fine.
FormsAuthenticationTicket ticket = new FormsAuth...
string encTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
cookie.Domain = subone.parent.com
Response.Cookies.Add(cookie)
This properly authenticates the user for subone.parent.com and not subtwo.parent.com. However I would like to do the following.
If the user goes back to parent.com, I would like to know that they are logged in and redirect them back to subone.parent.com.
Is there a best practice for accomplishing this? Or do I have to set another cookie for parent.com?
I'm working in asp.net mvc if it matters.
THanks!
You can share cookies across domains like you are trying to do, but its not straight forward, example here.
Another options is to set the cookie to be ".parent.com" rather than specifying the sub-domain explicitly and use the cookie store the details of the sub-domain. Then you can access the cookie from any of your sub-domains (and parent assuming its www.parent.com).
If your using MVC, you can pretty easily create a custom filter and add to the www.parent.com controllers to check for the existence of the cookie, and if so redirect to the sub domain the cookie specifies. Further details of filters here.
I would set the cookie for the explicit domain as you have there because that maintains any security information within that specific domain's cookie. You can also add a non-encrypted cookie at the *.parent.com level that holds information about which domains have been authenticated. There is no real way to tie this together though without using maybe timestamps and having a logical connection between the applications (ie - sub2 has a session timeout of 20 minutes so if the domain + valid timestamp occur in the parent cookie it would be valid, however this is business logic).
I'm not sure the reasoning behind the disconnect between domains, but you might actually prefer to have a single cookie that has encrypted text behind encrypted text. EG:
1) Sub1 logs in, sets the parent.com cookie as valid. Sends a piece of user data to an authentication web service.
2) The authentication service recognizes sub1 as the sender, encrypts the user data, and adds it to a custom cookie object.
3) The custom cookie object constructs a composite string on a unique split character (or sequence) and makes it available to the service method.
4) The service, using the forms encryption, encrypts the entire ticket and sends it back to the original login.
That way each server would be able to unencrypt the global ticket, but each piece of data would be encrypted using a common algorithm but a server based salt. So if sub2 attempts to read the cookie data from sub1, it gets the encrypted version rather than raw data.
you could share the same session on all subdomains. That is the code we use to accomplish that :-)
void MasterPage_Unload(object sender, EventArgs e)
{
///ASP.NET uses one cookie per subdomain/domain,
///we need one cookie for _all_ subdomains.
if (Context.Response.Cookies["ASP.NET_SessionId"] == null)
return;
var sessionCookie = new HttpCookie("ASP.NET_SessionId", Context.Session.SessionID);
sessionCookie.Domain = ".yourdomain.com" ;
Context.Response.SetCookie(sessionCookie);
}
inside the Page_Load method is:
Unload += MasterPage_Unload;
it works great :-)
robert
Related
I can actually see the verification token key generated by MVC3 framework in plain text when making a request to the server without ssl.
This key is stored in a cookie called: _RequestVerificationToken_Lw__
In mixed security environment it is actually possible to see this token in plain text sent to the server on the initial request to the non ssl site. This token is also static for the duration of the user's session. Then what's the use of having this token when it can easily be stolen by an attacker, because the cookie gets thrown around in plain text.
Shouldn't this cookie be marked as secure and never to be sent across in plain text? Or at the very least be regenerated on every request such that the secure information doesn't leak out of the ssl channel?
I'm talking about this block in MVC 3 AntiForgeryWorker class
private string GetAntiForgeryTokenAndSetCookie(HttpContextBase httpContext, string salt, string domain, string path)
{
string forgeryTokenName = AntiForgeryData.GetAntiForgeryTokenName(httpContext.Request.ApplicationPath);
AntiForgeryData token = (AntiForgeryData) null;
HttpCookie httpCookie = httpContext.Request.Cookies[forgeryTokenName];
if (httpCookie != null)
{
try
{
token = this.Serializer.Deserialize(httpCookie.Value);
}
catch (HttpAntiForgeryException ex)
{
}
}
if (token == null)
{
token = AntiForgeryData.NewToken();
string str = this.Serializer.Serialize(token);
HttpCookie cookie = new HttpCookie(forgeryTokenName, str)
{
HttpOnly = true,
Domain = domain
};
if (!string.IsNullOrEmpty(path))
cookie.Path = path;
httpContext.Response.Cookies.Set(cookie); //Ma, Why isn't this marked as "SECURE"
}
return this.Serializer.Serialize(new AntiForgeryData(token)
{
Salt = salt,
Username = AntiForgeryData.GetUsername(httpContext.User)
});
}
That's quite the inflammatory question title you have there.
The built-in MVC anti-forgery functionality is as secure as the application is configured to be. All cookies written to Response.Cookies will be automatically marked with the "secure" modifier if <httpCookies requireSSL="true" /> is set in Web.config (see MSDN docs). MVC's anti-forgery cookie also gets this behavior if this switch is set.
Combine this with other functionality like setting the HSTS header in your responses, and you're essentially providing a guarantee that the browser will never send sensitive data over plaintext channels.
Additionally, the anti-forgery system does allow storing custom data in the tokens, and you can receive a callback to verify the custom data when the token is validated. See AntiForgeryConfig.AdditionalDataProvider for more information.
With protection against CSRF attacks, an optimal solution is to always use SSL. Without SSL, yes, the nonce--as it is called--is vulnerable to a MITM attack. When using cookies to store the nonce, the cookie must be marked HTTP-only. This prevents JavaScript from reading the cookie. You should also render the nonce as an <input type="hidden" value="nonce"> tag within all <form>s in addition to a cookie.
Anyone with access to the browser itself would be able to read the nonce, and the only way to prevent a replay attack is to have nonces expire the first time after they are validated for the first time by the server. This approach can cause a terrible user experience when the user uses the back button and resubmits a request with the same nonce, however. Because you're using ASP.NET MVC's built-in anti-CSRF protection mechanism, it may not be easy to change its behavior to only allow a nonce to be used once. (EDIT: Thanks to Levi below for informing me that ASP.NET MVC actually makes this quite simple)
If you want better control over generating and validating the nonces then I suggest rolling your own implementation, as I did with my JuniorRoute framework. In fact, feel free to take a look at JuniorRoute's source code to see how I implemented it. It's too much code for a Stack Overflow post.
My Take
a) The form submission is deemed not forged based on comparison of
__RequestVerificationToken cookie &
__RequestVerificationToken form field.
The 2 values are some kind of symmetrically match and hence not same.
b) Cookie can never be marked default must-use-secure-channel by the framework because some applications do not use https.
c) The __RequestVerificationToken implementation is protection against CSRF & cannot help valid user from snooping into process memory:p.
In my website i have implemented custom session values. In which, on log on i set the session value to some object. This object is used to extract user specific data from db.
now the problem is If user logs in with : test1.somesite.com and logs off and again logs in with: test2.somesite.com that user is still receiving the data from object specific to test1.somesite.com.
the point is whichever site user first logs in with the second time if he logs in with another subdomain he is always getting the data from previous sub domain login.
on log out from specific domain i cleared all the sessions(tried everything): by putting HttpContext.session["UserDetail"] = null;, HttpContext.Session.Abandon() and also HttpContext.Session.Clear();
but nothing seems to work
and i also don't have much idea how session variables are treated across subdomains.
I mean if i initialize a session with one value by visiting test1.somesite.com will that value also be visible if on the same computer and on same browser i also open test2.somesite.com.
any help please
Sounds like a cookie issue. Try clearing out the session cookie. Something along the lines of:
if (!User.Identity.IsAuthenticated)
{
if (Request.Cookies["ASP.NET_SessionId"] != null)
{
Response.Cookies["ASP.NET_SessionId"].Expires = DateTime.Now.AddDays(-1);
}
Session.Abandon();
}
What you need to do is set your asp.net session id cookie to write to a wild card domain.
Response.Cookies("ASP.NET_SessionId").Domain = "yourdomain.com" // or "*.yourdomain.com"
So that the cookie can float to your subdomains.
I have multiple asp.net sites. When a user logs unto one of the sites, I want to store a cookie telling me that a user has logged on. When the user later visits one of the other sites I have, I would like to read the cookie from that site.
AFAIK you neither can read cookies from or write cookies to other sites, so what could a workaround be?
Perhaps making a redirect to http://www.othersite.com/SaveCookie.aspx ?
Give me some ideas :-)
One of our clients has exactly the same requirement (logging into multiple sites on different domains), complicated by the fact that one of the sites requires that the user is logged in to a classic ASP application, a .NET 1.1 application and a .NET 3.5 application running on different hardware, but under the same domain...
We've basically implemented a system of round-robin style redirects, where each domain logs the user in, then bounces them on to the next domain until they return to the original domain at which point they are redirected to their original request.
So (pages and domains changed to protect the innocent):
User requests www.example1.com/page1.aspx
A cookie is set that tells us the user was attempting to access page1.aspx, and the user is sent to the www.example1.com/login.aspx
The user logs in, and is then redirected to www.example2.com/processlogin.aspx?token=EncryptedToken
ProcessLogin.aspx checks for a cookie telling it where to direct the user, if it can't find one, it decrypts the token, logs the user in on example2.com, and then redirects them to www.example1.com/processlogin.aspx?token=EncryptedToken (or example3.com - repeat as required)
As in 4, ProcessLogin.aspx checks for the cookie, finds it, deletes it and redirects the user to /page1.aspx.
If the user later on visits a page on www.example2.com, before the authentication ticket timeout, they will still be logged in on that site as well.
Edit to respond to comment
That depends on how you are making the "request to the other pages". If you make the request from your code behind, what you're doing is effectively setting the cookie on the server, rather than on the users browser.
Cookies need to be issued by the server to the client browser, and that is done in the headers of the page response - so you need to direct the users browser to a page on the other site to issue the cookie from that domain.
You could generate a request to the other page in an IFrame, or try and do it in a self closing pop-up window - but that has other issues like pop-up blockers, flickering windows, etc.
After some investigation we found that a round-robin set of redirects like this was the simplest and most reliable solution.
A very basic code setup:
An .aspx page, containing a Login control, with a method "OnLoggedIn" attached to the LoggedIn event of the control:
void OnLoggedIn(object sender, EventArgs e){
string returnUrl = Request.QueryString["returnUrl"];
// Create new cookie, store value, and add to cookie collection
HttpCookie myCookie = new HttpCookie("WhereTo");
myCookie["ReturnUrl"] = ReturnUrl;
Response.Cookies.Add(myCookie);
// Redirect user to roundtrip login processor on next domain.
// Work out domain as required.
string redirect = GetNextDomain();
// Add encoded user token
redirect += "?token=" + EncodeUserToken();
// Redirect the user, and end further processing on this thread
Response.Redirect(redirect, true);
}
Then on both servers you have ProcessLogin.aspx, that has something like this in it:
protected void Page_Load(object sender, EventArgs e){
// Look for redirect cookie
if (Request.Cookies["WhereTo"]["ReturnUrl"] != null){
// Save value from cookie
string redirect = Request.Cookies["WhereTo"]["ReturnUrl"];
// Delete original cookie by creating an empty one, and setting it
// to expire yesterday, and add it to the response.
HttpCookie myCookie = new HttpCookie("WhereTo");
myCookie.Expires = DateTime.Now.AddDays(-1d);
Response.Cookies.Add(myCookie);
// Redirect the user, and stop processing
Response.Redirect(redirect, true);
}
// Still here, so log in and redirect
string encryptedToken = Request.QueryString["token"];
if (!string.IsNullOrEmpty(encryptedToken)){
// Decrypt token, and log user in
// This will vary depending on your authentication mechanism
PerformLogin(encryptedToken);
}
// Redirect user to roundtrip login processor on next domain.
// Work out domain as required.
string redirect = GetNextDomain();
// Add encoded user token - no need to recalculate, it will be the same
redirect += "?token=" + encryptedToken;
// Redirect the user, and end further processing on this thread
Response.Redirect(redirect, true);
}
You're looking for a Single Sign-On (SSO) solution.
If it's possible for you to host your sites at different subdomains below the same domain, you can save cookies that are shared for the whole domain, e.g.:
"site1.yourdomain.com" and
"site2.yourdomain.com"
can both read cookies saved to the domain "yourdomain.com"
Another alternative is to tell the other site about the login via a request to it, as in your redirect suggestion. You could do this in several ways, e.g. by loading the page in an iframe, sending the data directly from one server to another, and so on. None of these are particularly elegant, though, and in the case of login, as Tomas Lycken says, you should really be going for a proper SSO implementation.
I'd like to use ASP.NET MVC's views as mail template engine. For that, I am calling one controller action from another controller action using a System.ComponentModel.Component.WebClient, parse the returned web page and send it via e-mail.
In this scenario, is it possible to forward the current user's login credentials (I am using FormsAuthentication) to the controller action requested by the WebClient? User passwords are encrypted, so I can't just create a new NetworkCredentials instance with his user name and password.
Yes, you can just copy the .ASPXAUTH cookie from your current Request object to the WebClient
EDIT: I haven't actually tried this myself, so maybe the .ASPXAUTH cookie is removed from the Request object for security reasons.
But since you have access to the machine key, you can create your own cookies on the fly. Here's the code that should do it (I can't find the project where I actually did that)
var ticket = new FormsAuthenticationTicket(User.Identity.Name, true, 5);
string aspxAuthCookieValue = FormsAuthentication.Encrypt(ticket);
This code creates a forms authentication cookie for your current user name and with an expiration time of 5 minutes.
Instead of performing a http request, aren't you looking for something like "rendering a view to a string"
We have an application that does single sign-on using a centralized authentication server (CAS). We'd like to do single sign-out, such that if the user logs out of one application (say a front-end portal), the user is automatically signed out of all applications using the same single sign-on ticket.
The expectation would be that each application would register a sign-out hook (URL) with the CAS at the time of logon to that application. When the CAS receives the sign out request from one of the applications, it invokes the sign-out hook for all the application sharing the SSO ticket.
My question is this: is there a way to abandon an InProc session from a different session? I presume, since the HTTP request will be coming from the CAS server, that it will get its own session, but it is the session of the user that I want to terminate. I have pretty good idea of how to do this using a separate session state server, but I'd like to know if it is possible using InProc session state.
Haha, well... It looks like you can. I was wondering myself if there was any way to do this, turns out, there is.
When you use InProc, the InProcSessionStateStore (internal class) persist the session state in an internal (non public) cache. You can access this cache through reflection and remove the session state manually.
using System;
using System.Reflection;
using System.Web;
object obj = typeof(HttpRuntime).GetProperty("CacheInternal",
BindingFlags.NonPublic | BindingFlags.Static)
.GetValue(null, null);
if (obj != null)
{
MethodInfo remove = obj.GetType()
.GetMethod("Remove", BindingFlags.NonPublic | BindingFlags.Instance,
Type.DefaultBinder, new Type[] { typeof(string) }, null);
object proc = remove.Invoke(obj, new object[] { "j" + state.SessionID });
}
The end result is, that the next request will take on the same SessionID, but the HttpSessionState will be empty. You'll still get the Session_Start and Session_End events.
After doing a bit of digging around and considering the answers provided so far I've come up with an alternative that lets me continue to use InProc session. Basically, it consists of extending the HttpModule that already handles single sign-on to detected CAS sign outs and redirect the browser to the application sign out page.
Outline:
Sign-On:
For each new single sign-on request, create a new SSO cookie and encode a unique value in it to identify the session (not the session id, so it isn't leaked).
Construct the the sign-out callback url, encoded with the identifier, and register it with the CAS server.
Sign-Out:
When a sign-out request is received from the CAS server, decode the identifier and store it in an application-wide cache. This needs to be pinned in the cache at least long enough for the session to expire naturally.
For each request, look for the SSO cookie and check its value against the cached, signed-out session identifiers. If there is a hit, remove the SSO cookie and redirect the browser to the application's sign-out url.
For each sign-out, check to see if there is an SSO cookie, if so, forward the sign-out request to the CAS. In any event, abandon the user's session, and sign them out of the application.
Page_Load:
Check for the presence of the SSO cookie. If there isn't one, redirect to the sign out page.
No can do.
http://forums.asp.net/p/416094/416094.aspx#416094
With InProc SessionState, you won't be able to access the data... With StateServer, you still will have a sticky scenario trying to access the correct API to remove the session.
You will most likely want to use a database backed state solution like the pre-packaged SqlServer state provider or a third party solution like DOTSS: http://codeplex.com/dotss
With the database backed solution, you will be able to lookup the state record in a table by session id and mark it as completed. These techniques will vary based on the provider you choose.