Sessions are based on the session identifier, a string that uniquely identifies a browser session via a cookie1 set on the client when session_start()
is called. Since they are based on cookies, you should treat them as user input and you should never absolutely trust user input. Cookies can be stolen via XSS, and PHP cookies are not encrypted by default.
Sessions are stored in the %TEMP%
directory, whichever that may be for your system. The save location can be changed from the session.save_path directive or by the session_save_path during runtime, but on a typical hosting environment2, it's generally considered easier to gain access to the filesystem than a database.
The various security concerns are addressed on the manual:
The session module cannot guarantee that the information you store in a session is only viewed by the user who created the session.
...
Assess the importance of the data carried by your sessions and deploy additional protections -- this usually comes at a price, reduced convenience for the user.
...
The session module cannot guarantee that the information you store in a session is only viewed by the user who created the session.
Session fixation is the commonly quoted buzzword, you can find a little more info on the Session Fixation Vulnerability in Web-based Applications article that's referenced on the manual.
The session_set_save_handler() function allows you to transparently choose where sessions are saved:
session_set_save_handler() sets the user-level session storage functions which are used for storing and retrieving data associated with a session. This is most useful when a storage method other than those supplied by PHP sessions is preferred. i.e. Storing the session data in a local database.
After you build and set your handler, you can continue using sessions as usual. There are quite a few examples on the comments to the manual page on storing sessions to a database. Storing sessions in a database has an important disadvantage: if for any reason the database goes awol, then you've lost everything you've built around sessions too.
1 You can propagate the session id via a URL parameter, but that's more trouble than it's worth.
2 By typical I mean cheap shared hosting.
I'm afraid adding a Web Service layer is probably the correct solution to your problem.
Separating the client from the underlying database implementation will probably help you in the long run too.
Adding a web service layer doesn't necessarily have to hurt performance...
Indeed, with an appropriate API, a web service can actually improve performance, by batching together multiple database queries within the data center LAN, rather than requiring multiple round trips over the WAN.
And of course a web service layer can often be scaled horizontally, and add appropriate caching to your database queries, perhaps even a change notification mechanism.
A server layer adds security that you cannot possibly ensure with apps running on a remote client. Anything that runs on a client can be "hacked" and should not really be considered in any way trusted. You should only really put presentation logic in the client, and host anything important on hardware you have complete control of.
I don't know about your apps, but my web apps are naturally split into several layers, with the presentation code separated from the persistence layer by at least one level of business logic that keeps the two apart. I find this makes it much easier to reason about my app, and so much faster to add or modify functionality. If the layers are separated anyway, it is relatively easy to keep the presentation layer in the client, and the rest on a server under my control.
So while you can solve your problems without introducing a "web service" layer, by the time you have written all the stored procedures (or equivalent) necessary to fill in the holes in the standard database security implementation, you would probably be better off writing a server-side application that you can write proper unit tests for.
Best Answer
What you've come up against is one of the fundamental problems of client-server architecture. In order for the client machine to access the database, you need to have credentials for that database on the client machine. As accepted answer in to the that question provided by Robert Harvey explains, there's no way to completely prevent the user of the application from getting those credentials.
This is one problem that is made easier by moving to a N-tier architecture. Other than that, I see two main approaches you can take:
Create database credentials for each user. Then there is no need to store the DB credentials on the client machine. Logging into the DB could then take the place of managing user passwords in a table. You would then need to manage the rights within the DB for each user to prevent them from being able to access or change data outside of their authority.
Encrypt the database password using the clients credentials. This is extremely error-prone (especially around credential updates (both user and DB). Ideally you would find a well tested pre-built solution for this but I doubt you'll find one because such a solution still leaves you with the reality that someone with user credentials (legitimately or otherwise) can intercept the database credentials. You will need to consider what access those database credentials provide and what someone with them could do to your database.
It might not be easy but the preferred approach to improve security here is to move to a more contemporary architecture.