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.
Related
I'm going to get so many "okay grandpa" comments for this one.
I've read a dozen articles and every SO question I could find on this subject.
I must have been away too long or missed something completely, because I swear that user authentication used to be very simple. I seem to recall built-in methods and a session on the server simply knowing who the user was via a cookie or similar, with the ability to store information "in session". I don't recall even setting up authentication in years past, it was just built-in to new applications.
Instead, the most succinct guide I could find is very involved. I think I need a token authorization/authentication setup because there may be consumers (like apps) who don't have a typical cookie pattern these days. In my head, the token works like a cookie except it's manually held on the user end and passed via header with each request?
To its credit, the guide worked, at least for logging in and correctly utilizing the simple Authorize attribute in controllers. However, User.Identity.Name is always empty, even when User.Identity.IsAuthenticated is true, which is perplexing.
How I think auth is working:
User request hits API with username/password
Service checks the combination, and returns an encrypted JWT to the user
The user sends the JWT back with every request
The server decrypts this JWT to identify the user - this is probably where I'm wrong
So here is where my question comes in:
I need more data about the user, like access to the entire UserModel with every request, but I don't want to go to the database to find it every time. This is where I think there should just be a session object in memory, but that doesn't appear to be the case with token authentication.
TL;DR:
Where do I put user-specific, short-term ("session") information for consumption in future requests where a user is identified with a JWT in the Authorization header instead of a cookie?
Session state isn't right, because it's hard-wired to a cookie
HttpContext.Items aren't right, because it's just for the one request
Cache storage isn't right, because it's not user/session specific. I could potentially create a session-like user-keyed storage here but that seems way, way over-engineered for this.
Basically anything where I'm passing all the data (not just a user identifier) to the client then relying on the client to pass it back seems wrong? But feel free to correct me.
The server decrypts this JWT to identify the user This is probably
where I'm wrong
The JWT token is not encrypted, its signed so you can't alter it. You can open it if you look at jwt.io for example.
Where do I put user-specific, short-term ("session") information for
consumption in future requests where a user is identified with a JWT
in the Authorization header instead of a cookie?
You put it in the principle claims of the token. In the guide you linked it wrote:
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.NameId, user.UserName)
};
So you add whatever you want to the claims to store it on the token and later you can access this data via:
var claim = _contextAccessor.HttpContext.User?.Claims.FirstOrDefault(d =>
d.Type == ClaimTypes.NameIdentifier);
You also can't use any of these other examples that you listed like HttpContext.Items because those are not signed. If the token is altered in any way the system identifies this and returns a 401
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.
What I have
I'm making a web service using C#.
In order to authenticate users, they have to send their name plus their encrypted password, in order to check if exists in a database.
Then, If it's found, I create a string token, which is a 10 char string randomly generated in order to send it the next times while the session is alive, avoiding to have to send the original credentials anymore.
What is my problem
Using this approach, my problem appears due to the service lifetime.
It's known that web services are not initialized each time a request arrives, but nor is infinite. So there will be a moment, when it'll be destroyed and initialized again.
At this point, my token list would be erased, as well as all the alive connections with it, as this is its function.
So I'm stuck at this point. I'm not sure about how to proceed, maybe I'm just fooling around and there's a simpler way to authenticate users? Or maybe you've and idea about how to don't loose all these alive sessions without having to write them at a DB.
Thank you in advance
Update:
My goal
I aim to create a personal Web Service, just build for me and some friends. Not inside a company nor anything like this. Not in the same LAN neither.
I want to add a bit of security to this service, so I wanted to add authentication to the WS, mainly in order to avoid people pretending to be another and this kind of stuff. So I created User+Password system.
Then, in order to avoid to send them both in each WS Request, I started to write the "token" approach described above.
Notice that I'm using token word because it's similarity with token systems for these cases, but it's a completely created from 0 system, nothing proffesional, so do not assume anything complex about it if I've not said that.
How my system works (or try to)
User -> Auth (user, pass_encrypted) -> WS -> DB (exist? OK)
WS -> token (randomly generated, 10char string) -> User
After that, at each WS request, User sends the token instead of credentials.
After receiving it, WS looks for the token at a List<structureToken>, so it obtains the user which is doing the call, and (for example) the access level, in order to know if the user has rights to run this call.
Your current problem is that you want same list to be persisted through restarts and not persisted to any physical media at the same time. You have to pick one of the choices and live with it: not persisted - just ignore the fact you can have list in memory and make sure token can be validated by itself, if persisting - pick storage and save you list of random numbers.
Since you are building simple system without actual need to have proven verifiable security you can get some ideas from existing systems (like STS and the way it creates token). Basically STS signs information about user (indeed after validation) and than encrypts it with public key of receiving party. So particular server that supposed to get the token can decrypt it (as it has private key part), everyone else may still use it but have to treat as non-verifiable black box token.
Simplest version of this would be no encryption of information, just basic signing. Proper signing requires private/public pair (so external party can validate signature), but since in your case both parties are the same service - you can just SHA256. To prevent external callers to fake you signature you need to have some private information included in hash to "salt" value before hashing. Random number hardcoded into server code (or read from settings) would be good enough. You may also want to include expiration as part of signed value.
So your "token" could look like:
Name,expiration,Base64 of SHA256 of {Name + expiration + secret value}
Bob-2015-06-30-A23BDEDDC56
Since your server code have "secret value" you can always re-compute hash to verify if it is indeed the correct token.
Notes:
do not use it for any real services. Use an existing authentication and make sure to review all security comments related to proper usage of it.
this approach gives you chance to learn a some other concepts - i.e. key update (when your "secret value" need to change, or in real systems signing/encryption certs).
Currently, I'm using the FOrmsAuthentication.SetAuthCookie to store an Id so I can use it on the next page that is "Authorized" (using custom authorizeattribute controller). But I'm currently thinking of making a custom cookie using httpcookie so I can store more data, or easily maintainable data. Was wondering if having the kind of cookie will I be able to authorize the current user to access the "Authorized" controllers? If so how do I go about it.
Hope that made sense.
Please let me know your thoughts.
Just put your extra stuff in a different cookie. And if forms auth says the user isn't authenticated, don't read the other cookies. No need to overload the purpose of the auth cookie (and non trivial to do so securely)
There is a UserData property of the FormsAuthenticationTicket. It is a string so you will have to be able to serialize/deserialize any complex data.
Good security design says dont store this information in a cookie - figure out another way (server side). Recently (octoberish) the ASP.Net POET vulnerability taught us that forms auth tickets could be forged because the machinekey could be determined and hence data encrypted as it would be on the server. I know - not exactly what you asked but I think it's important to not store sensitive data on the client side.
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.