views:

187

answers:

3

Original Question:

i read that for RESTful websites. it is not good to use $_SESSION. Why is it not good? how then do i properly authenticate users without looking up database all the time to check for the user's roles?


I read that it is not good to use $_SESSION.

http://www.recessframework.org/page/towards-restful-php-5-basic-tips

I am creating a WEBSITE, not web service in PHP. and i am trying to make it more RESTful. at least in spirit.

right now i am rewriting all the action to use Form tags POST and add in a hidden value called _method which would be "delete" for deleting action and "put" for updating action.

however, i am not sure why it is recommended NOT to use $_SESSION. i would like to know why and what can i do to improve.

To allow easy authorization checking, what i did was to after logging in the user, the username is stored in the $_SESSION.

Everytime the user navigates to a page, the page would check if the username is stored inside $_SESSION and then based on the $_SESSION retrieves all the info including privileges from the database and then evaluates the authorization to access the page based on the info retrieved.

Is the way I am implementing bad? not RESTful? how do i improve performance and security?

Thank you.

+1  A: 

Sessions as they are normally implemented are not RESTful because they store only a small token on the client side, while the actual "state" of the client (logged in, contents of shopping cart, etc) are stored on the server. REST requires the web to follow it's original design of "statelessness" meaning that each request is just the same as any other -- the server doesn't have to keep track of anything.

If all the data is stored in a cookie -- actual data, not just a token -- that can be in keeping with REST. I tend to have the server return all the state to the client as a block of encrypted text, which is decrypted by the server on the next call to confirm the user is logged in, what is in the shopping cart, etc.

Devin Ceartas
+1  A: 

The very same link you posted offers a solution:

In practice this means storing authentication information in a cookie with a timestamp and a checksum.

But personally I don't agree that a checksum is enough. I prefer the authentication cookie to contain a timestamp, the original login IP (to prevent cookie hijacking) and to be fully encrypted with a site key.

Remus Rusanu
Users can roam between IP addresses. Consider a college student taking her laptop from her dorm to the coffee shop, or any home user whose ISP resets IP addresses every day.
Justice
True, but it's reasonable to ask someone to re-authenticate under those circumstances I would think.
Devin Ceartas
@Devin It's also possible for large organizations to have multiple web proxies under different IP addresses, and outgoing requests are load-balanced to any one of them. Hence the same user may have a different IP on every request. It's rare, but not too rare. Also, on many sites (including this very site here), I wouldn't want to re-authenticate all the time, even just once a day, because my modem switched IPs.
deceze
It boils down to risk assessment. Some will deem it necessary, some don't. Proxies can add headers tell what IP they proxy for like `HTTP_FORWARDED_FOR` or `HTTP_X_CLUSTER_CLIENT_IP`.
Remus Rusanu
+6  A: 

As written in the article, this "rule" is utter nonsense. The author is damning $_SESSION, but is perfectly okay with cookies storing authentication information. He goes on to say:

In the event you need more than a cookie’s worth of data fall back to storing it in a central database with the authentication still in the cookie.

What's the difference between storing data in a session with a token in the cookie, and storing data in a database with authentication in the cookie? There is no difference, other than that by using a token, you're not transmitting authentication data, possibly in the clear, with every request.

There is no difference between transmitting additional information with each request and transmitting a token that represents additional data, but needs to be resolved through a session on the server. It's just a matter of security and practicality.

The argument is often that a server should be "stateless". Since RESTfulness pertains to the HTTP protocol though, "statelessness" does not mean "the server doesn't store any state". Statelessness from a protocol's perspective means that I can make any number of requests in arbitrary order, and I receive the same resource for the same request.

GET  /index.html
POST /someaction
GET  /index.html  -> should return the same *resource* as before

Contrast that with a real state-keeping protocol such as FTP:

LS       -> gets list of files in current directory
CD /dir  -> changes directory, i.e. changes state
LS       -> same command gets list of files for a different directory

That's the real difference between a RESTful protocol and a state-keeping protocol. Whether the server stores any data pertaining to the user or not is totally an implementation detail of the server and has nothing to do with RESTfulness. If the server returns the same resource as response to the exact same request, regardless of what other kinds of requests were made in between, it's stateless and thereby RESTful.

This doesn't have anything to do with authentication or storing additional data and also does not preclude that requests or URLs may eventually expire.

If URLs/requests do expire, there's a special way to handle that using HTTP: responding with appropriate status codes. If a user sends a request with an expired token/login, the server is not supposed to answer with a login screen at the requested URL. That would violate RESTfulness.

not RESTful:

GET /restricted/page

200 OK

Please log in here:
Name: _____
Password: _____

----------------------

POST /restricted/page
[name, password]

200 OK

Content of restricted page.

----------------------

GET /restricted/page

200 OK

Content of restricted page.

RESTful:

GET /restricted/page

401 Unauthorized

or

GET /restricted/page

307 Temporary Redirect
Location: /login

----------------------

GET /login

200 OK

----------------------

POST /login
[name, password]

307 Temporary Redirect
Location: /restricted/page

----------------------

GET /restricted/page

200 OK

This does not "replace" the resource /restricted/page as the bad example would do, keeping the server RESTful. It does appropriately signal to the client that the request is valid, just not right now. Note that the term resource is always used, not response. It's okay for the server to respond differently, but it's not okay to offer a different resource (content*) at the same URL. If that was the case, the client would also need to keep track of the current state of a session (like FTP) to be able to tell what's going on. Statelessness is much more about the client being stateless than the server. It doesn't preclude the server from keeping track of what the client is doing.


*) Note that content is not equivalent to resource. It's okay for the content of a resource to change and update.

Further note that there are valid reasons against using $_SESSION, most notably scalability across several servers. Keeping a server RESTful is not a valid reason though. If you need any sort of state, like expiring logins or a shopping basket, you need to keep track of that information somewhere. A server session is as valid a place as a cookie, and is in many cases the better choice. Whether that session is implemented using $_SESSION or a database or pen and paper is an implementation detail.

deceze
Erm okie, then what should I do?
keisimone
@keisimone Just avoid making a stateful application, i.e. where `GET /a` and `GET /a` return different *resources*. You'd have to go out of your way to do that though, so you probably don't have anything to worry about. Continue to use `$_SESSION` in a way that makes sense, storing authentication information is perfectly okay.
deceze
@keisimone Added an example of what not to do in a RESTful application.
deceze
currently what we have done is when user tries to access restricted page and is NOT logged in, we will redirect the user to login page url + a referral param. something like login.php?return=restrictedpage.php?param1=a so is that in line with what you said? sorry i sound like an idiot. i am trying to learn here and i feel bad that i dont "get it" after reading your words which you clearly have put a lot of efforts into.
keisimone
@keisimone Yes, that's fine and RESTful. :) Make sure you're using the right HTTP status code (maybe you are already) and you're all set. http://en.wikipedia.org/wiki/Http_status_codes
deceze
You should show the correct caching headers on the restricted resource to emphasize that normal HTTP caching rules do not apply because the resource is not publicly cacheable. Better would be correct use of HTTP authentication because the Authenticate header already limits caching to private.
Jan Algermissen
@deceze Make sure you're using the right HTTP status code (maybe you are already) and you're all set. so when i redirect unlogged in users from restrictedpage.php to login page, i should use 401. after successful login, the login page will auto redirect to restrictedpage.php with 200. Am I right? sorry for asking such detailed question.
keisimone
@keisimone For any redirection you should use a status code in the 3xx block, usually 302 or 307. 401 just means "Sorry no", without redirection, but offers different options to authenticate. Read the aforelinked Wikipedia article, the status codes are all very well explained in there. 200 is really only for "Here is the page you requested, no further action needed."
deceze
@deceze thanks for the clarification. cause i thought the idea is that the restrictedpage does not allow access hence i thought the right choice would be 401.
keisimone