I’ve inherited a rather convoluted project. The original designer created a “cookie” that appears to be server side rather than client based (though I could be very wrong on that part). He is using it for what he called “Least Privileges, Single Sign On”. I have the following code in all of the Web Service Proxies he set up:
[WebServiceBinding(Name = "ISecurityManager", Namespace = "urn:riv:apis:security:forms:ver1")]
public partial class SecurityManager : SoapHttpClientProtocol, ISecurityManager
{
public SecurityManager()
{
//Url = CookieManager.WebServiceUrl(String.Empty, ref CookieContainer);
// I’d like to replace the following code with a call like this...
CookieContainer = new System.Net.CookieContainer();
string urlSetting = ConfigurationManager.AppSettings["SecurityManager"];
if (urlSetting != null)
Url = urlSetting;
else
Trace.TraceWarning("No URL was found in application configuration file");
string cookieName = FormsAuthentication.FormsCookieName;
string cookiePath = FormsAuthentication.FormsCookiePath;
string cookieDomain = Properties.Settings.Default.CookieDomain;
HttpCookie authCookie = HttpContext.Current.Request.Cookies[cookieName];
if (null != authCookie)
CookieContainer.Add(new Uri(urlSetting), new System.Net.Cookie(cookieName, authCookie.Value, cookiePath, cookieDomain));
}
….
I also have this code pretty much everywhere:
string cookieName = FormsAuthentication.FormsCookieName;
string SecurityContext.ApplicationName = HttpContext.Current.Request.Cookies[cookieName].Path;
string SecurityContext.UserName = HttpContext.Current.User.Identity.Name;
if (!string.IsNullOrEmpty(SecurityContext.UserName))
….
In all instances, when it goes to get the authCookie, it comes up null or the SecurityContext.UserName is blank. I’m not a cookie guru and a lot of this guy’s code is obfuscated – and zero documentation.
Can anyone make heads or tails out of the intent of the code blocks?
TIA
FormsAuthentication for a web service method? Storing authentication credentials in a cookie? There are so many things wrong with this story. (Note: heavy obfuscation of code should be taken as a sign.)
The intent of the code blocks, as it appears, is using the cookie framework for user identification during a method call. It assumes the user has already been authenticated and that the authentication cookie is present in all requests.
EDIT: a bit more information on "server-side cookies" -- the references you see to System.Net.Cookie and such are .Net Framework classes for handling cookies. Cookies are client-side pieces of data that reside either in-memory for the client (usually a web browser), and/or saved as text files somewhere on the local file system of the client. Most web applications that set client-side cookies assume they are dealing with a web browser, as all the major browser providers support cookies.
When a web browser is used to make a request to a URL, lots of information is sent in the background that is hidden from the user: IP address, the type of browser and OS, etc. Included in this list are cookies for that given URL domain (there are HTTP rules that browsers agree to). The code you're looking at are specific .Net Framework classes for dealing with those cookie values in a structured way.
Most applications that consume web services are completely stateless -- no cookies, no sessions, nothing. While it's possible that a client to a web service may implement cookie support, assuming or requiring cookie support for a web service is folly.
In the code scenario you've debugged to detect null values, most likely the calling application does not support cookies, effectively rendering the entire code block invalid. This is broken-by-design.
I cannot find a sensible way of improving this code block that doesn't involve a teardown of the entire structure. Given your suggested level of familiarity, spend a little time on web security 101. Get familiar with the concepts of authentication, sessions, (and cookies, too.) You'll know you're ready to proceed as soon as you realize that security is something you don't invent yourself.
Well obviously he's attempting to create a security/authorization provider that apparently doesn't work well or at all. I recommend you look at Enterprise Library for this functionality. Security Application Block QuickStart. Then look at caching the security token.
Related
I have been trying to find an explanation, but are coming rather short, as to how exactly identity works with mvc core. I have found numerous guides on how to implement it and I have and it all works great, but I want to understand it under the hood at the very least on a high level.
My current understanding is that when a user passes their credentials, identity library will issue a token and that token will be written to the browser's cookies. Now all further requests will have the user identified based on that browser cookie. However, I can't seem to find explanations past that. Why is this safe, why can't I steal that cookie and use it as my own? I know there is more to this and potentially I have understood the above part wrong.
In any case, I'm either looking for a high-level explanation on how this works or a good reference that can delve more into details.
how exactly identity works with mvc core.
As #Chris Pratt says, what you're talking about is the security subsystem. Since you're talking about cookie, I'll take the authentication scheme of cookie as an example.
The built-in security could be mainly found in 4 projects:
HttpAbstractions: core interfaces and classes, such as authentication scheme, authentication handler, authentication ticket and so on.
Security: authentication middleware, cookie authentication, JWT Bearer authentication, OAuth2.0 authentication(Google/Facebook/Microsoft/...) and so on.
Identity : a scaffold project named as "Identity" that helps to manage user/roles/claims/etc.
DataProtection : Data Protection APIs for protecting and unprotecting data. You can treat it as an API to encrypt and decrypt.
The entry point to understand how authentication works is the AuthenticationMiddleware. This middleware will try to authenticate every request if possible:
public async Task Invoke(HttpContext context)
{
// ...
// Give any IAuthenticationRequestHandler schemes a chance to handle the request
var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;
if (handler != null && await handler.HandleRequestAsync())
{
return;
}
}
// Use the default scheme to authenticate request
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
context.User = result.Principal;
}
}
await _next(context);
}
Usually, this middleware runs before other middlewares/mvc thus you can intercept requests as you need.
When you want to access an url protected by [Authorize] without login, it will ask you to sign in through some scheme. You can configure your services to use different schemes as you like, such as Jwt Bearer, cookies, and so on.
If you're using the cookie scheme,
CookieAuthenticationHandler will do the heavy lifting :
Signin: will issue a new cookie when you think you have validated the user principal.
Authenticate: validate the cookie sent by client
Signout : delete the cookie
Note all these are done by Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler, i.e. a handler defined in aspnet/Security, not the aspnet/Identity library.
why can't I steal that cookie and use it as my own?
Of course you can steal someone's cookie and use it as your own. Actually, if Alice's cookie is stolen by Bob (let's say through XSS or sniffering), Bob will be treated as Alice. ASP.NET Core (and other technologies such as PHP/Python/Java) cannot prevent this and there're quite a lot to do to prevent the stealing :
The website should use HTTPS rather than HTTP
encode characters like <,>,<img onclick='javascript:' and so on to prevent XSS
...
Also, you don't need to steal someone's cookie sometimes. By CSRF, you simply "borrow" his cookie.
Why is this safe
Typically, even if it's possible to steal someone's cookie or borrow someone's cookie in theory, it will only happen when you're developing your app in a wrong way or deploy them in an insecure way.
Another thing is that you could hardly fake a cookie on client side.
A common use case for WebAPI would be to have shell views rendered by MVC controllers, which contain javascript that then hit your API to access data.
But let's say you have some expensive API operations and you don't want people remotely accessing those endpoints -- you only want your MVC views, delivered by your application, to access them. How could you go about protecting them?
In this case Request.IsLocal doesn't work, because javascript is invoking it from the client's browser on their machine. Even if it did work, you need to dig to get the real HttpContext in order to find this property -- and that solution wouldn't work in self-hosted WebAPI.
For API endpoints that require a valid IPrincipal, you could protect them with the [Authorize] attribute. But what about API endpoints that you want your app to be able to access for anonymous users?
I have tried a solution and will post it separately as an answer, because I'm not sure if it's the best (or even a good) approach.
If your MVC site uses authentication, you could enable forms authentication for your Web API methods. You could write a custom [Authorize] attribute that will check for the presence of a forms authentication cookie which will be sent from the AJAX call and if present construct the principal.
Another possible solution is to protect your API with tokens which is a more RESTful style. The idea here is that when a user authenticates on your MVC website you could generate and pass a token to the view which will be used when sending the AJAX request to the Web API which in turn will verify the validity of the token and its signature.
If on the other hand your site doesn't use authentication, then things will get very complicated because you have no way of knowing whether the request comes from a trusted client since you are using javascript to call your API methods.
Before you go harping about "what have you tried", here is what I have tried. It works. Just not sure if there is a better way.
Create an MVC action filter and add it as a global filter during Application_Start.
Create an Http (WebAPI) action filter and use it on actions that should reject remote requests.
The global MVC filter does this:
Looks for a specific cookie in the request. If the cookie is there, its value is decrypted. The decrypted value should be a string representation of a DateTime, so use DateTime.TryParse to get it out. If the value is correctly parsed to a DateTime, and that DateTime is less than a day old, STOP HERE and do nothing else.
If the cookie is not there, or cannot be decrypted / parsed, or is older than a day, write a new cookie to the browser. Use the current DateTime.UtcNow.ToString() as the value, encrypt it, and write it with HttpOnly = false.
The WebAPI filter does this:
Looks for a specific cookie in the request. If the cookie is there, decrypt its value and try to parse it out as a DateTime.
If the value is a valid DateTime and is less than 2 days old, STOP HERE and do nothing else.
Otherwise, throw a 403 Forbidden exception.
A couple of notes about my current implementation of this. First of all, I use AES encryption with a shared secret and a salt. The shared secret is stored as an appSetting in web.config. For the salt, I enabled anonymous identification and used Request.AnonymousID as the salt. I'm not entirely fond of the salt because it's tricker to get at in a WebAPI controller, but not impossible as long as it is not self-hosted.
I'm now building a App that use asp.net webAPI on the server side,I got a problem about the validation:
I want to provide my webAPI for multi-platform,just like browser's javascript,or windows phone ,and so on,so I decide to implicit the validation with HTTP-BASIC,"(forgive my poor English),the problem is ,In the past time.
I always take some User's Information in SESSION,but we know that webAPI with RESTful-style is Session-stateless,so how to store the User's information:
And I get some idea,I hope you can help me to make the right choice,thx a lot
1.
put the information into the browser's cookie except the user's password and other important infos. everytime I make the http-request ,i take the cookies.and on the server-side,I can query the user's infomation.and make other steps.(the sequence will not work on moblie platform,cuz cookies only in browsers)
2.user HTTP-BASIC validation,and everytime the server get the httpRequest,it get the username and password in the HTTP-Headers,and server-side also can query the user's information.
Most REST APIs I've seen handle authentication one of two ways:
HTTP Headers, be it basic auth, or some custom headers to pass credentials. This would be your option 2. This is only really good if you're running over HTTPS, since the credentials will be in clear text in the headers.
Using a pair of tokens, one as an identifier (somewhat like a user name) and one shared secret between the client and the server (somewhat like a password). A hash is then made of the identifier, parts of the request parameters, and the secret. This hash and the identifier is then sent along with the request. The server, knowing the secret, then computes the hash using the same method, and ensures they match (Amazon Web Services uses this method, along with anything using OAuth).
More web APIs seem to be migrating to the second method here, as it is resistant to tampering and replay attacks, unlike basic auth. It is, of course, more complex.
RFC 5849 Section 3.4 for OAuth, while dry reading, goes through the process used for creating the hash, and probably would be a good starting point for implementing, if you desire. A basic implementation in C# is provided on the OAuth Google Code site, and might be a better choice to start with.
Background:
This is really a general best-practices question, but some background about the specific situation might be helpful:
We are developing a "connected" application for the iPhone. It will communicate with the backend application via REST services. In order to not have to prompt the user for a username and password every time they launch the application, we will expose a "Login" service that validates their username and password on initial launch and returns an authentication token that can be used for future web service requests for real data. The token may have an expiration time after which we'll ask them to re-authenticate with their username/password.
The Question:
What are the best practices for generating this sort of token to be used for authentication?
For example, we could...
Hash (SHA-256, etc) a random string and store it in the database for the given user along with an expiration date. Do a simple lookup of the token on subsequent requests to make sure it matches.
Encrypte the user id and some additional information (timestamp, etc) with a secret key. Decrypt the token on subsequent requests to make sure it was issued by us.
This feels like it must be a solved problem.
Based on the feedback from the other answers to this question, additional research, and offline discussions, here is what we ended up doing...
It was pointed out pretty quickly that the interaction model here is essentially exactly the same as the model used by Forms Authentication in ASP.NET when a "remember me" checkbox is checked. It's just not a web browser making the HTTP requests. Our "ticket" is equivilant to the cookie that Forms Authentication sets. Forms Authentication uses essentially an "encrypt some data with a secret key" approach by default.
In our login web service, we use this code to create a ticket:
string[] userData = new string[4];
// fill the userData array with the information we need for subsequent requests
userData[0] = ...; // data we need
userData[1] = ...; // other data, etc
// create a Forms Auth ticket with the username and the user data.
FormsAuthenticationTicket formsTicket = new FormsAuthenticationTicket(
1,
username,
DateTime.Now,
DateTime.Now.AddMinutes(DefaultTimeout),
true,
string.Join(UserDataDelimiter, userData)
);
// encrypt the ticket
string encryptedTicket = FormsAuthentication.Encrypt(formsTicket);
Then we have an operation behavior attribute for the WCF services that adds an IParameterInspector that checks for a valid ticket in the HTTP headers for the request. Developers put this operation behavior attribute on operations that require authentication. Here is how that code parses the ticket:
// get the Forms Auth ticket object back from the encrypted Ticket
FormsAuthenticationTicket formsTicket = FormsAuthentication.Decrypt(encryptedTicket);
// split the user data back apart
string[] userData = formsTicket.UserData.Split(new string[] { UserDataDelimiter }, StringSplitOptions.None);
// verify that the username in the ticket matches the username that was sent with the request
if (formsTicket.Name == expectedUsername)
{
// ticket is valid
...
}
Building your own authentication system is always a "worst practice". That's the kind of thing best left to professionals who specialize in authentication systems.
If you're bent on building your own "expiring ticket from a login service" architecture rather than re-using an existing one, it's probably a good idea to at least familiarize yourself with the issues that drove the design of similar systems, like Kerberos. A gentle introduction is here:
http://web.mit.edu/kerberos/dialogue.html
It would also be a good idea to take a look at what security holes have been found in Kerberos (and similar systems) over the last 20 years and make sure you don't replicate them. Kerberos was built by security experts and carefully reviewed for decades, and still serious algorithmic flaws are being found in it, like this one:
http://web.mit.edu/kerberos/www/advisories/MITKRB5-SA-2003-004-krb4.txt
It's a lot better to learn from their mistakes than your own.
Amazon.com uses a HMAC SHA-1 message token to authenticate and authorize requests. They use this for a fairly large commercial service, so I'd be liable to trust their engineering decisions. Google publishes the OpenSocial API which is somewhat similar. Based on Google and Amazon.com using similar and openly published approaches to securing web requests, I suspect these are probably good ways to go.
Either of the two answers you've provided will suffice. You may find frameworks out there that do this for you, but the truth is it's not that hard to build. (Every company I've worked for has rolled their own.) The choice of database-stored tokens versus encrypted data "cookies" is an architectural decision -- do you want to incur a database lookup on every page view, or would you rather chew up CPU with cookie decryption? In most applications, using encrypted cookies provides a performance win at scale (if that's a concern). Otherwise it's just a matter of taste.
Since you're using WCF, you have a variety of options if using CFNetwork -- for instance NTLM or Digest Authentication:
http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/Concepts/Concepts.html#//apple_ref/doc/uid/TP30001132-CH4-SW7
I know this doesn't answer your specific question, but I have also been faced with this problem (iPhone - Tomcat) and decided to use the authentication services on the web server as much as possible. There's no significant penalty for including the authentication information with each request in most cases. A quick Google turns up lots of blog posts about WCF and RESTful services (and some related questions on StackOverflow).
Hope this helps!
You should implement:
OAuth2 Implicit Grant - for third-party applications https://www.rfc-editor.org/rfc/rfc6749#section-1.3.2
OAuth2 Resource Owner Password Credentials — for your own mobile application https://www.rfc-editor.org/rfc/rfc6749#section-1.3.3
which are exactly the workflows, from OAuth2, that you are looking for. Do not reinvent the wheel.
This simply sounds like a session identifier with a long expiration time. The same principles used for this in web applications could apply here.
Rather than encoding information, session identifiers are randomly chosen from a very large space (128 bits). The server keeps a record associating the session identifier with the user and other desired information such as expiration time. The client presents the session identifier over a secure channel with each request.
Security relies on the unpredictability of the session identifiers. Generate them with a cryptographic RNG, from a very large space.
I have two webapplication, one is a simple authenticationsite which can authenticate the logged in user and redirects him then to another site.
Therefore I have to pass ther userId (GUID) to the second application. Currently this is done via the URL but i would like to hide this id.
Has anybody an idea how to do this properly?
[EDIT]: I can't use the Session because of the ApplicationBoundaries (2 different Servers)
This sounds like a tricky situation.
There are however several options you can use but it all depends on what your application does.
Let's call WebApp1 your authenticate site, and WebApp2 your distination site once authenticated.
Can WebApp2 not call WebApp1 behind the scenes? (Services)
THe problem with passing this Guid between applications is it's going through clear text, and considering it's a user id, if anyone manages to intercept this they will have access to WebApp2 for life. Whether you pass it in a querystring or form variable, it's still vulnerable.
If you can't use WebApp2 to query WebApp1, you should consider WebApp1 creating a temporary Guid that expires. That would be much safer long term, but as it's clear text is still susceptible to attack. The 2 web apps will also need access to the same data store.
Ultimately, i think the AUthentication Site should be a service which WebApp2 can consume.
Users should login through WebApp2, which will call WebApp1 securely for authentication.
WebApp2 can then manage it's own session.
If you can't use cookies because it's cross domain then encrypt it, with a nonce.
Setup a shared secret/key between the two servers; send the encrypted GUID and nonce combination to the second server. Unencrypt, check the nonce hasn't already been used (to stop reply attacks), then use the unencrypted GUID.
If you want to be extra tricky have a web service on app1 where it can check the nonce was actually issued (at this point you're heading towards WSTrust and a single sign-on solution, which generally solve what you're trying to do)
Even with cookies, as they're easily edited/faked, you should have some form of checking.
You have two ASP.NET web applications, and one application does nothing but authenticate a user?
this sounds like a job for....
Web Services!
Create a new web service on the authentication app (They are the .asmx extension), and add a single method that takes in the user and password etc, and returns authentication info.
Then import the WSDL on your 2nd app, and call the 1st app like it was a method. It will simplify your code, and fix your issue.
An Example:
AuthenticateUserService.asmx goes on the Authentication app:
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class AuthenticateUserService : System.Web.Services.WebService
{
[WebMethod]
public bool AuthenticateUser(string username, string passhash)
{
// Fake authentication for the example
return (username == "jon" && passhash == "SomeHashedValueOfFoobar");
}
}
Once this is setup, fire up your main app, and right click the project and click "Add Web Reference".
Enter the url to the asmx on the authentication app, and Visual Studio will discover it and create a proxy class.
Once that is done, we can call that method like it was a local method in our main app:
protected void Page_Load(object sender, EventArgs e)
{
// Now we can easily authenticate user in our code
AuthenticateUserService authenticationProxy =
new AuthenticateUserService();
bool isUserAuthenticated =
authenticationProxy.AuthenticateUser("jon", SomeHashMethod("foobar"));
}
So, what does this really do?
It eliminates the client from the authentication process.
Your current process:
Client Enters credentials to AppA
AppA redirects the client to AppB
AppB redirects the client back to AppA if the credentials match.
Is replaced with a server side SOAP call between AppA and AppB. Now its like this:
Client enters credentials in AppA
AppA asks AppB if they are good
AppA serves proper content to the client.
Pass the GUID through a session, best way.
http://www.w3schools.com/ASP/asp_sessions.asp
OR, since it's 2 different servers, pass the information by POST method:
http://www.w3schools.com/aspnet/aspnet_forms.asp
The other possibility is to store the session state in a database on the local server, and remotely access that database from the other server to see if the user has successfully logged in and within session timelimit.
With that in mind, you can do the entire authentication remotely as well. Remotely connect to the local database from the remote server and check the login credentials from there...that way you will be able to store the session and/or cookie on the remote server.
I would recommend AGAINST the hidden field proposition, as it completely counteracts what you are trying to do! You are trying to hide the GUID in the URL but posting the same information in your HTML code! This is not the way to do it.
Best choice is the database option, or if not possible, then use HTTP POST.
Use session variables or HTTP POST instead of HTTP GET.
Instead of passing it via a query string you should create a hidden form field with its value and then post to your 2nd page, which can then grab the posted value and it will be hidden from the user.
If the servers have a common domain name, you can use a cookie.
EDIT: Cookies will just hide the ID visually, it is still accessible. Same with hidden fields or using POST rather than GET. So if the ID is confidental and you want to avoid to send it over the network unencrypted, you need a different approach.
A solution could be to encrypt the ID on the auth server with a key which is shared by the servers. Another solution could be to generate a random GUID on the auth server, and then let the auth server directly inform the other server (over SSL) which ID the GUID corresponds to.
go for session mangement or use a HTTP Post as said in the above post.