I have a PHP application that relies extensively on sessions. We are now considering building an API for our users. Our initial thoughts are that users will need to authenticate against the api with their email address, password and an API key (unique for each user).
However, as the current application (including the models) relies on user sessions extensively, I am not sure on the best approach.
Assuming that an API request is correctly authenticated, would it be acceptable to:
Start the session for the API call once user is authenticated
Run the models and return json/xml to the user
Kill the session
This means that the session gets instantiated for each API call, and then immediately flushed. Is this OK? Or should we be considering other alternatives?
In my experience of creating APIs, I have found it best that sessions only last for one request and to recreate the session information in each execution cycle.
This does obviously introduce an overhead if your session instantiation is significant, however if you're just checking credentials against a database it should be OK. Plus, you should be able to cache any of the heavy lifting in something like APC or memcache based on a user identifier rather than session reducing the work required to recreate a session while ensuring authentication verified in each request.
Related
I want to authenticate socket.io clients based on client session data created by Laravel.
What I have thought of is :
A - emitting username and email from client to server;
B - storing the data my socket.io server needs in Redis in php after user logs in and then reading it in Node.js based on session cookie id.
I will probably have to store sessionId -> "email, name" in Redis if I prefer this approach.;
C - using Redis session driver in Laravel, decoding cookies set by Laravel, accessing Laravel session values from Node.js, unserializing and decoding them;
Approach A is obviously very insecure and could be used only to prove concept.
Approach C seems better since I do not have to duplicate or manage session data but only to decode it. This way however couples my application to the implementation details of Laravel managed sessions and thus doesnt seem to be appropriate.
Approach B looks more promising and it is simpler to implement.
However using approach B means I have to manage some session data myself in order for socket.io to be able to read it. Doing so may make Laravel session data and session data I store in Redis mutually inconsistent and this will happen at some point in the time. In some extreme case for example an expired session id could be reused and some socket.io client will be authenticated incorrectly as another user. I cant think of more trivial case at this moment but because of this incosistency I assume its possible that there is such case and both security and UX could be compromised.
What is a more elegant, robust and secure way to achieve user authentication based on Laravel session data in socket.io application? If there are no drastically better approaches and I assume approach B is best what could I do to improve consistency between session data I manage using Redis and Laravel session data.
The whole point as far as I can summarize is actually accessing Laravel session data outside Laravel and php and identifying clients by sessionId, email and username.
I suggest you to use JSON Web Token authentication.
JSON Web Token (JWt) is a relatively new token format used in space-constrained environments such as HTTP Authorization headers. JWT is architected as a method for transferring security claims based between parties. More detailed info about JWT
The easiest way to do this with Laravel is by a using a package like this one. Or you can implement it by yourserlf.
Using JWT auth you will be able to access the user from the token. For example:
$user = JWTAuth::parseToken()->toUser();
For detailed information about how to use 'jwt-auth' take a look here.
As already noted by Alexandros you should use JWT.
Authenticate JWTs with socket.io is a breeze.
You could use socketio-auth and jsonwebtoken as described very well in this article to authenticate your users. Furthermore you could use dotenv to read the secret-key for the signed tokens from the Laravel .env-file.
For me it's working fine, although you have to think of invalidation of the tokens. In Laravel jwt-auth takes care of this by using a blacklist. So in your node server you have to handle this by yourself. Or keep the liftetime short.
I had found a good solution for this about a year ago. I decided to make it a module, its really easy to use. helps you get the cookie without hard coding it. helps you get that session Id and retrieve it from mysql and redis
https://www.npmjs.com/package/node-laravel-session
this way you can share all of your session. you can also utilize the session in order to sign up a user to certain rooms
A user logs in using default Laravel authentication, which puts an encrypted cookie in the browser, and saves the session in the database.
The user moves to a classic asp page, where I check the cookie value, get the hash, and call the laravel app back passing the session id hash.
Then I use that in laravel to see if there's an active session for that id, and if so I return true, so the user can be logged in, in classic asp.
On each page request in the classic app, I check the last_updated_time in the db and update it on each page. All logging in and out is done in laravel, and classic relies on the database to see if a session is active.
I would also call a public url to get sessions variables and add session variables using laravel, since it's all encrypted and using classic asp for this would be hard.
The only risk I see is session highjacking, but I don't think it's a higher risk than usual.
Is it important to lockdown the laravel URL I call to check if it's a valid session?
Am I missing a security hole here?
Is this method secure?
From what you've stated you probably haven't opened up any security holes. The session cookie is not itself encrypted on the users machine, but you are making sure it is encrypted between their machines and yours, as well as between each of your machines. You should make sure you've set the Secure Flag to help prevent the cookie being accidentally sent over traditional unencrypted transport (HTTP), but as stated, this doesn't effect storing the cookie itself.
That being said, you are essentially hijacking your own users sessions. While a hole might not be introduced now, you are potentially weakening the overall system, which could lead to hole in the future.
Is there a better way to do it?
This might well be a dumb question, but are you sure you need the session? If you're juggling credentials between servers, it sounds more like you want to use Access Tokens and scrap the session.
Using Access Tokens is similar to using sessions, but you need to make your services stateless. This means your no longer storing information about the logged in user any specific machine so you'll need to pull anything you need from the database every time they hit a server requiring that information.
This is a good thing in the long run as it's much easier to scale your services when you don't need to worry so much about where the session is and what's inside it.
OAuth 2.0 is widely used standard (Facebook, Twitter, Google), and was specifically designed to be easy to use. The RFC is complex, but there's a log of good guides out there don't worry.
The one slight down side (if you can call it that) to OAuth 2, is that it MUST happen over an encrypted connection. If your use case can not guarantee encryption over SSL or (preferably) TLS, then you should use OAuth 1.0 (WITH revision A) instead.
This is due to the fact that OAuth 2.0 exposes it's "secret" token in requests, where as OAuth 1.0 only ever uses it to provide a signature hash. If you take this route it's advisable to use someone else's library as the hash is very, specific.
Further Improvement
(Note: This section added after the answer was accepted)
One system I've been exploring recently is Json Web Tokens. These store information about the user to save each machine repeatedly looking it up in a database. Because the token is hashed with a secret, you can be sure that, so long as your secret isn't exposed, a valid token represents a successfully logged in user, without having to touch the database.
You should avoid putting anything too personal in the tokens if possible. If you must store private or secret information in the token, you can encrypt it, or you can use a reverse caching proxy to exchange the JWT for a traditional security token. This may initially seem to defeat the purpose, but it means some of your services may not need database access at all.
I'm no security expert but I don't see an issue with this. The packaged Laravel database session handler works the same way. The cookie contains a hash that references a record in the database. The session data is base64 encoded but that's neither here nor there. I think you could actually avoid rolling your own and just use Laravel's DatabaseSessionHandler.
Illuminate/Session/DatabaseSessionHandler
... I just read a little deeper into your question and noticed the part about the public URL to set and retrieve session data. I think this is a really bad idea. Mostly because it will provide an open door to the end user allowing them to read and write session data. This can only end badly.
Like I said above, the data is only base64 encoded so I believe you'll be able to parse, read and write that to your hearts content within asp.
Edit
Ok... I think this is the last edit. The data is php serialized and then base64 encoded. This question looks like it may help you to that end. If it doesn't and an API endpoint is the only way, find some way to block the end user from accessing it.
Aside from session-hijacking, no. This is the standard way applications interact on a internal basis. Of course there might be a better way to get at the data if you choose a different type of session store other than your database, Memcached for instance.
There are couple of things that can be done.
Make the channel HTTPS. It will make almost impossible to sniff on your transport layer.
Rather than making interactions with your cookie, you could use a JWT to get this task done. Which will help you to use the existing functionality in your system while connecting with ASP system as well. You can write a small REST web service which allows ASP to connect. You could use this lib. You can refer this article which will give you an idea how it should be done.
Please let me know if you need more information.
I'm starting to develop a simple PHP RESTful API. After reading some tutorials, one of the features about REST is that the
"... statelessness is key. Essentially, what this means is that the
necessary state to handle the request is contained within the request
itself, whether as part of the URI, query-string parameters, body, or
headers"
Therefore, it means that my PHP server won't need to have $_SESSION ? What kind of approach do you recommend ? Using a token (valid for a short limit of time) in the URL doesn't seem a bit unsecure?
For example www.myapi.com/1233asdd123/get_user/12.
Many thanks.
If you're a web developer of any sort, you'll have heard this sentence probably 1,000 times: "HTTP is a stateless protocol". This means that every session works with a token exchanged between the server and the client.
When you use PHP's built-in sessions, the server is actually doing exactly that, even if you don't realize it: it generates a session_id and passes it to the client. The client passes the session_id token back normally on a cookie; PHP allows including the session token also on the URL, as a GET parameter, but I personally recommend disabling that feature (disabled by default on PHP 5.3+).
In your case, yes, you won't be using PHP's sessions.
You create a table in your database storing all session tokens and the associated session.
Tokens should have a short lifespan (for example, 30 minutes) and should be refreshed frequently. Refreshes are important not only to extend the life of the session (every refresh gives you an extra 30 minutes or so), but also help fighting against thefts of the session key. In some REST servers we created, the session token lives for 30 minutes and users are given a new token on the first request made after 10 minutes the session started. When clients are sent a new token, the old one is invalidated immediately.
You could pass the token to the server in any way, but adding it as a GET parameter is not an ideal solution for two reasons: 1. GET parameters are often written in the access logs of the servers and 2. users often copy/paste URLs and share them, and that can expose their session token.
For API servers, the best approach is to include the session token in one of the headers of the HTTP request. For example, you could set your Authorization header: Authorization: SessionToken 123123123 where 123123123 is your token and SessionToken is a string to tell the server to use your authorization method (you're free to choose your own name, as long as it's not one of the default methods like Basic; be consistent, though!).
Security on API servers is normally obtained by using SSL. Basically, if you have an API server, you must protect it with HTTPS (SSL).
There are methods to achieve security also without using SSL, but they require signing each request and are really complicate to implement and to use - and the overhead they add is probably bigger than the one of SSL.
A very common practice is to use a key value inside the URL:
www.myapi.com?key=ABCD1234
It is not less/more secure than POSTing the string. SSL encryption ensures that the whole payload cannot be intercepted by a man-in-the-middle.
More info :
You also mentioned a temporary access (session token). It is common in systems to log in using credentials (like the solution above) and obtain a temporary session token in the response. The session token is then used instead of the login details to query the service. It reduces the exposition of credentials in case of interception, and if someone manages to steal the session token, it will be working only for a few minutes. Good to have although not a necessity.
I've been making a standardized JSON API for my company's website; I'd like to have a way of authenticating users to use the API while keeping the API as stateless as possible. My idea was the following:
The user logs in, the web service authenticates the user, and generates a random string that gets passed to the client, along with an expiration date. This is stored in a cookie, as well as an entry in the database.
For every API request, the web service checks the cookie string against the database entry. If it authenticates, the web service will generate a new string, replace the old string with the new one in the database and the cookie, then send back the requested information.
If the client sends a request and the cookie does not match the database entry, the string in the database is set to NULL and the client has to log in again and start the process from step 1.
If a request is sent after the expiration date, the string in the database is set to NULL and the user must log in again.
I want to cause as few disruptions as possible with my company's current setup as we slowly transition to newer technology. Is this method something that is commonly done? What kind of security issues would I be running into if I do it this way? Should I be using a different method?
Yes, this is a common scenario. What you are describing is a session cookie and is widely used.
You might want to read into Session Fixation techniques and ways to mitigate those.
But using a session is not really stateless. If you can provide keys (shared secrets) to your API consumers, you could also consider message signing to authenticate requests. Make sure you are using (H)MAC. Also make sure that you guard yourself from Replay Attacks.
In the case of an authentication token the client sends the credentials, receives a token and uses this in all subsequent calls. The server needs to save the token in order to validate the requests.
With for example PHP sessions the server returns a session UID which the client sends in every request. The server needs to save the session.
In both cases the server needs to save a state so why is the former considered stateless?
Semantics. A session is generally implemented by assigning each user a unique ID and storing that ID in a client-side cookie. The auth token would be EXACTLY the same thing - a unique per-user ID of some sort. Cookies are sent back on every request automatically by the browser, and the auth token CAN be sent back on every request, but generally should be sent only on the requests that actually require authentication.
The difference has to do with how those tokens are treated on the server. The session ID is used to load up a corresponding session from storage (e.g. a file, a db record, whatever), and that session data will be persisted between requests.
An auth token doesn't have any associated session file. It's more just a "I'm allowed to be here, and here's the proof".
There's no reason a session ID can't be used to implement the authentication system. Even a simple $_SESSION['logged_in'] = true would turn the session into an authentication system.
If the expectation is that the client will always send an authentication token for every request, the server actually doesn't need to save state. It has everything it needs in the message to determine how to evaluate the request.
Certain server architectures (I'm thinking Java servlets, specifically) are required to return a session cookie, but they aren't required to use it when it's passed back to them on the next request. In my stateless servlet application, a different cookie representing the JSESSIONID is returned for every response. So, in this case, it's just background noise.
Most sessions are stateful for two reasons:
the identifier passed by the client can't be parsed into a meaningful set of credentials without having had previous server interaction (they are usually just a random value assigned by the server)
the identifier has an implicit lifespan that isn't discoverable within the identifier
First of all, what you described in this question is a session management, not token management.
SessionIds are generated by business system itself. The workflow is same as your question.
While tokens are usually generated and managed by an independent system, not the business system. When client sent subsequent calls to the business server after it had got a token already, the business server had to validate the token from the token system. So when we talk about the business system, we say it is stateless.
Additionally, in my point of view, token is not born to handle authentication, it is designed to keep important information secure.
Reference:
PCI DSS tokenization
Redhat token management system