Assuming I have a valid session and an authenticated user, what are some ways to go about implementing user authorization in an application with a PHP/MySQL backend, and a heavy JavaScript front-end?
Most of the implementation examples I can find seem too focused on user authentication and the authorization just sort of happens. For instance, an if statement checking if the type of user an admin. This seems way too implemented to me.
In an implementation like mine, there is no way of knowing what "page" the user was on when they initiated the request. So, a method of only serving certain content for certain users, determined by PHP, is too broad for what I need to do.
Ideally each entity has a sort of access control list based either on the user explicitly or what group or type the user is/in.
I went to a local bookstore and spent an afternoon looking through all they had on PHP, MySQL and JavaScript. Surprisingly, most of the books had virtually nothing on user authorization. That scares the hell out of me! This has to be solved by anyone building a large web application that uses AJAX, I just can't seem to find something to get me started.
I would appreciate any and all feedback, experiences, tips, etc. (Any books on this subject?)
PHP security seems stuck in the dark ages of single password gives a token for a single user for a class of particular pages. You seem to be wanting to get a lot more fine-grained in your app, maybe even allowing access to specific pieces of resources depending on that login token. Your thought of access control lists is absolutely correct, and yes, you've discovered the dark secret: no one really published how to design or write an ACL mechanism. That said, it has been done.
First, are you familiar with unix file permissions? The're the -rwxr-xr-x things you see in an ls -l on the command line. Unix has chosen a very simplified approach to ACLs. Each person logged in has a User ID (UID) and one or more Group IDs (GID) (whoami, groups). The Unix file permissions allow three operations, Read, Write, and Execute which can be on or off. With 2^^9 states, these permissions easily fit in an integer, and Unix can then attach that
integer to the file directly in the file system. When a user attempts to access a file, permissions are compared from strict to permissive, matching the most permissive privileges allowed. So, users get the first set of permissions, groups get the second, and anyone gets the third. Thus, an executable is usually 755: only the owner can change it, but anyone can read and use it.
Second, LDAP is the Lightweight Directory Access Protocol, a system designed to give multiple network users access to resources. OpenLDAP is a common Linux implementation, and Microsoft's Active Directory on Windows Server speaks LDAP (with a lot of extensions). LDAP has a much more robust system of ACLs. A general configuration is access to [resources] by [who] [type of access granted] [control] or access to dn="uid=matt,ou=Users,dc=example,dc=com" by * none to limit all access to to Matt's user information. For a much more complete discussion, I would highly recommend Mastering LDAP, specifically chapter 4 on security. (This is where I get a bit out of my direct knowledge.) I am under the impression that LDAP stores this information in a separate database table, but I don't know that and can't find documentation one way or another. I am keeping an eye out for a possible schema for that.
Short stop to summarize: ACLs take a concept of a user token with possible groups above the user level, a collection of objects to secure in some way, and several consistent possible operations on those pieces- 3 dimensions of information. Unix stores two of those dimensions with the thing to be secured directly. OpenLDAP stores those three dimensions separately, in some way we don't quite know, but that I suspect is a linked tree structure.
Given that, let's take a look at how we could design an ACL system for a RESTful web application. For assumptions, we will break your application into discrete addressable units- each thing that needs to be secured will be accessible via a URI (http://example.com/users, http://example.com/page_pieces/ticker). Our users will be a simple UID/GIDs token- a user can be part of a several groups. Finally, our available operations will be based on the HTTP requests- GET, POST, PUT, DELETE, etc. We now need a system that efficiently handles a 3-dimensional array of data. Our schema should be pretty obvious: (uri, userid, groupid, operations). We deliberately denormalize the operations column into a string list of GET,POST,... so we only need one table. There is no primary key, since we will never really be looking up by ID.
Queries will be done in two steps: SELECT * FROM acl WHERE uri=#uri, userid=#userid which will return 0 or 1 rows. If it returns 1 row, we're done and can grep permisssion to see if the operation is in the list (use * to indicate all perms). If we got 0 rows, run a second query SELECT * FROM acl WHERE uri=#uri, userid='*', groupid in (#groupid) which will again return 0 or some rows. If it returns some, loop through and look at perms. If it returns 0, do one last query SELECT * FROM acl WHERE uri=#uri, userid='*', groupid='*' which will finally return 0 or 1 row. If it returns 1, look at perms. If it returns 0, take the default action.
We can set permissions in several ways:
INSERT INTO acl VALUES (#uri, #userid, '', 'GET,POST') allows a single user GET or POST access
INSERT INTO acl VALUES (#uri, '*', 'admin,contributors', 'GET,PUT,POST,DELETE')
INSERT INTO acl VALUES (#uri, '*', '*', '') denies all access.
A couple things to note:
All URIs must be expressed exactly; this solution has no way to set
default permissions at a higher level and have them trickle down
(left as exercise to the Questioner).
Uniqueness of uri/uid/gid pairs should happen at some point. The app can handle it, or in MySQL you can do ALTER TABLE acl ADD UNIQUE INDEX (uri, userid, groupid) (look up documentation for similar constraints in other DBMSes).
It seems that you are looking for something called Access Control List aka ACL (which is dead according to Zed Shaw, great video).
It's pretty hard to give a you a solution without knowing what kind of backend you have, but you might check out how other are doing that.
For something specific to the lithium framework (PHP), see: Lithium Access Control
This is what I understand:
You need to build an access control list for your users? do you?
[correct me if I'm wrong]
I suggest you to create a DB table in which you can store the User ID (or username) and what kind of access it has on your Web Application. Then you can check the table to know if the requested URL/resource is accessible to that user. That's all.
Related
I need to implement access control in my application. I need to control which columns of which row can be edited by who.
Examples of a rule may be:
User can edit only her own rows (and also insert rown only for her) of TableA, which is linked to her through TableB (TableB contains foreign keys of both User and TableA).
User can edit just some parts of her profile, while admin can update other parts.
Instead of asking a DB to give my application all the information needed to evaluate the possibility of change in the application code, I am currently thinking about trigger based approach for inserts, updates and deletes. In this scenario a triggers, which would fail when operation is not permitted, would be hooked to controlled tables.
I will tell Postgres which user is working right now using local configuration.
Then I simply run the operation while expecting the possibility of failure.
Assuming I am able to evaluate all the permissions inside the Postgres DB, I see a few positives:
I would not need to ask the DB before the operation if it can be done.
I would not litter the application with permissions evaluation.
I can run updates from different parts of application even without using proper models while maintaining the security.
My questions are:
Can this become a bottleneck when scaling up? (It is a pretty traditional web-based PHP app, so I am expecting about a 100-times more selects than updates. But this would shift evaluation from web server which could be more easily replicated then DB server.)
Is there some better well-known design practice to implement this kind of permissions I have in mind.
there is a possibility that after scaling up your DB will be full of triggers which would be difficult to edit or change something. E.g. if you add extra column, you should edit all procedures related to table.
If I've got your question, you're trying to control web application users.
I think better way is to implement restrictions on a PHP side. For instance to create role, which contain array of possible to edit columns. And if this is 'admin', his/her list would contain all columns.
Then dynamically create the form according to role configuration, in order to let user see, what he/she can edit.
I am currently working with a medium-sized team developing a custom content management system for a large client. The CMS is written using PHP and follows the MVC pattern (custom). It is a modular system, for which plugins can be added to the system by us or other developers at a later stage.
The system will contain user-based permissions, and a series of generic roles that have predefined permissions. It is required that a super-admin user can also modify permissions on a user basis (for example John Doe might be defined as a regular user, but has the possibility of modifying content).
Opinion is currently divided about the best way for us to store and handle these permissions. Half of the dev team are suggesting to add a new DB table that will store key/value pairs and user IDs for each user, with boolean values stored in each record. The table structure would be something like this:
user_ID: the ID of the user
perm_name: the name of the permission
perm_value: a boolean value dictating whether the user can carry out this action
The proposal is that if the value associated with a particular permission is set to 0, or does not exist in the table, the user does not have the required permission.
The other half of the dev team is favouring storing the permissions in a single field as a JSON-encoded string within the users table. So for example, we would store the following JSON for John Doe):
{
'modifyProducts': 1,
'addProducts': 1,
'addPages': 0
}
We would then be able to use json_decode() within the User class to extract the permissions, for example:
$this->permissions = json_decode($dbval);
I am personally leaning towards the latter option for two main reasons:
It is scalable
It does not require us to modify the database if we need a new permissions.
In short, what is the best approach for such an application?
I think the best solution in this case would be to use NoSQL database, such as MongoDB - this way you can still keep the scalability and take advantage of the JSON structure.
On the other hand, depending on your user table you could take possible advantage of column type indexing and optimize your requests for querying and reading, if of course you're working with normalized database.
I personally would store JSON within a relational DB only when I want to directly display the info and not use it for any querying. Just like you've said yourself - there's always the possibility of ending up with huge and growing JSON string and this would most probably cause troubles at some point.
I've been working on a web application for a company that assists them with quoting, managing inventory, and running jobs. We believe the app will be useful to other companies in the industry, but there's no way I want to roll out separate instances of the app, so we're making it multi-user (or multi-company might be a better term, as each company has multiple users).
It's built in Codeigniter (wish I had've done it in Rails, too late now though), and I've tried to follow the skinny-controller fat-model approach. I just want to make sure I do the authorisation side of things properly. When a user logs in I'd store the companyID along with the userID in the session. I'm thinking that every table that the user interfaces with should have an additional companyID field (tables accessed indirectly via relationships probably wouldn't need to store the companyID too, tell me if I'm wrong though). Retrieving data seems pretty straight forward, just have an additional where clause in AR to add the company ID to the select, eg $this->db->where('companyID', $companyID). I'm ok with this.
However, what I'd like to know is how to ensure users can only modify data within their own company (in case they send say, a delete request to a random quoteID, using firebug or a similar tool). One way I thought of is to add the same where clause above to every update and delete method in the models as well. This would technically work, but I just wanted to know whether it's the correct way to go about doing it, or if anyone had any other ideas.
Another option would be to check to see if the user's company owned the record prior to modification, but that seems like a double-up on database requests, and I don't really know if there's any benefit to doing it this way.
I'm surprised I couldn't find an answer to this question, I must be searching for the wrong terms :p. But I would appreciate any answers on this topic.
Thanks in advance,
Christian
I'd say you're going about this the correct way. Keeping all of the items in the same tables will allow you to run global statistics as well as localized statistics - so I think this is the better way to go.
I would also say that it would be best to add the where clause you mention to each query (whether it's a get, update, delete. However, I'm not sure you'd want to manually go in and do that for all of your queries. I would suggest you overwrite those methods in your models to add the relevant where clauses. That way, when you call $this->model->get(), you will automatically get the where->($companyID, $userID) clause added to the query.
From the looks of things it looks like this might be a more API type system (as otherwise this is simply a normal user authentication system).
Simple Authentication
Anyway, the best bet I can see for an API is to have two tables, companies and users
in the companies table have an companyID, and password. in the users table link each user to a company.
Then when a user makes a request have them send through the companyID and password with every request.
oauth
The next option, slightly harder to implement, and means that the other end must also setup Oauth authentication is oauth.
But, in my opinion is much nicer overall to use and is a bit more secure.
One way to do it would be with table prefixes. However, if you have a lot of tables already, duplicating them will obviously grow the size of the db rapidly. If you don't have many tables, this should scale. You can set the prefix based on user credentials. See the prefixes section of this page: http://codeigniter.com/user_guide/database/queries.html for more on working with them.
Another option is to not roll out separate instances of the application, but use separate databases. Here is a post on CI forum discussing multiple db's: http://codeigniter.com/forums/viewthread/145901/ Here again you can select the proper db based on user credentials.
The only other option I see is the one you proposed where you add an identifier to the data designating ownership. This should work, but seems kinda scary.
I have built a web application for one user, but now I would like to offer it to many users (it's an application for photographer(s)).
Multiple databases problems
I first did this by creating an application for each user, but this has many problems, like:
Giving access to a new user can't be automated (or is very difficult) since I have to create a subdomain, a database, initial tables, copy code to a new location, etc. This is tedious to do by hand!
I can't as easily create reports and statistics of usage, like how many projects do my users have, how many photos, etc.
Single database problems
But having just one database for each users creates it's own problems in code:
Now I have to change the DB schema to accommodate extra users, like the projects table having a user_id column (the same goes for some other tables like settings, etc.).
I have to look at almost each line of code that accesses the database and edit the SQL for selecting and inserting, so that I sava data for that specific user, at the same time doing joins so that I check permissions (select ... from projects inner join project_users ... where user_id = ?).
If I forget to do that at one spot in the code it means security breach or another unpleasant thing (consider showing user's projects by just doing select * from projects like I used to do - it will show all users' projects).
Backup: backup is harder because there's more data for the whole database and if a user says: "hey, I made a mistake today, can you revert the DB to yesterday", I can't as easily do that.
A solution?
I have read multiple questions on stackoverflow and have decided that I should go the "single database" route. But I'd like to get rid of the problems, if it's possible.
So I was thinking if there was a way to segment my database somehow so that I don't get these nasty (sometimes invisible) bugs?
I can reprogram the DB access layer if needed, but I'm using SQLs and not OO getter and setter methods.
Any help would be greatly appreciated.
I don't think there's a silver bullet on this one - though there are some things you can do.
Firstly, you could have your new design use a different MySQL user, and deny that user "select" rights on tables that should only be accessed through joins with the "users" table. You can then create a view which joins the two tables together, and use that whenever you run "select" queries. This way, if you forget a query, it will fail spectacularly, instead of silently. You can of course also limit insert, update and delete in this way - though that's a lot harder with a view.
Edit
So, if your application currently connects as "web_user", you could revoke select access on the projects table from that user. Instead, you'd create a view "projects_for_users", and grant "select" permissions on that view to a new user - "photographer", perhaps. The new user should also not have select access to "projects".
You could then re-write the application's data access step by step, and you'd be sure that you'd caught every instance where your app selects projects, because it would explode when trying to retrieve data - neither of your users would have "select" permissions on the projects table.
As a little side bonus - the select permission is also required for updates with a where clause, so you'd also be able to find instances where the application updates the project table without having been rewritten.
Secondly, you want to think about the provisioning process - how will you grant access to the system to new users? Who does this? Again, by separating the database user who can insert records into "users", you can avoid stupid bugs where page in your system does more than you think it does. With this kind of system, there are usually several steps that make up the provisioning process. Make sure you separate out the privileges for those tasks from the regular user privileges.
Edit
Provisioning is the word for setting up a service for a new user (I think it comes from the telephony world, where phone companies will talk about provisioning a new service on an existing phone line). It usually includes a whole bunch of business processes - and each step in the process must succeed for the next one to start. So, in your app, you may need to set up a new user account, validate their email address, set up storage space etc. Each of those steps needs to be considered as a step in the process, not just a single task.
Finally, while you're doing this, you may as well think about different levels of privilege. Will your system merit different types of user? Photographers, who can upload work, reviewers who can't? If that's a possible feature extension, you may want to build support for that now, even if the only type of user you support on go-live is photographer.
Well, time to face some hard facts -- I think. The "single database problem" that you describe, is not a problem, but a normal (usual) design. Quite often, one is simply a special case of many.
For some reason you have designed a web-app for one user -- not many of those around.
So, time to re-design.
Designing a fairly complicated site with a lot of ajax running on a single page. I have reached the point where some user's need to have specific permission to do things and some need to be stopped from the action. I have set up user roles in my database and all is working fine, but I wonder if there is an easier/safer method for me to store each permission.
Currently, when a user logs in their specific permissions are grabbed from the db and loaded into a session array. To check if the user has permission, I simply check to see if the permission is contained in the array. This seems sluggish, and almost like I am missing a better solution.
Also, sessions can apparently be edited by the user... is there a safer method?
I have thought running a query for each check, but that could greatly increase the load time for a simple ajax request.
I am open to any and all ideas. Thanks.
First and foremost, the user cannot edit Session variables. The only thing that is saved on the user's machine is a Session ID. That ID is then used by the server to grab key/value pairs that are stored ONLY on the server. From a client's standpoint, it is impossible to change values on a whim.
Second, I would not worry too heavily on a database connection. Avoid repeating yourself, but don't worry too much about the first connection.
Finally, my favorite way to do multiple permissions without creating roles is to use binary math. Some people like this, some people don't, but I find it useful.
To use this method, imaging that we define the following values:
CAN_EDIT_SOMETHING = 1 // Powers of 2
CAN_SEE_SOMETHING_ELSE = 2
CAN_DO_ADMIN_STUFF = 4
... = 8
To give people multiple permissions, use binary OR
PERMISSIONS = CAN_EDIT_SOMETHING | CAN_DO_ADMIN_STUFF
To illustrate how this works, we can look at the bits:
0b0001
OR 0b0100
---------
0b0101
To check if someone has a permission, use binary AND
if( PERMISSIONS & CAN_EDIT_SOMETHING != 0 ) {
}
To see how this works, we look at the bits again
0b0101
AND 0b0001
----------
0b0001 // Not equal to 0. They must have that permission!
The final benefit of this method is that it allows you to combine multiple permissions easily into "meta-permissions"
// If both EDIT_SOMETHING and ADMIN_STUFF are tasks that an admin
// can perform, we can combine them easily
//
IS_FULL_ADMIN = CAN_EDIT_SOMETHING | CAN_DO_ADMIN_STUFF
// We can then use this value exactly as we do any other permission
//
PERMISSIONS = IS_FULL_ADMIN | CAN_SEE_SOMETHING ELSE
Use it if you want, but it is a nice trick to have in your arsenal.
Seems OK to me! You could look at some software to enhance your session chache peformance.
Querying the DB every time is not as bad as it sounds! Firstly you probably need to connect to the DB anyway, secondly if you queried the users permisions when they signed in then the chances are that all the relevent rows are sitting in the buffer and no IO is required, thirdly a query for a single permision for a single user is going to be a lot lighter than a query for all permisions for a user.
Your explanation of the model seems a bit confused. Permission is the product of the subject authorization and the object authorization. Do you really store these products for every combination of subject and object? That's a very inefficient solution and very hard to manage.
Also, sessions can apparently be edited by the user
WTF?????!!!!
Session data should only ever be changed by the methods you define in your code - if users are able to modify any part of the session data in any way they like then this is the first problem you need to address - until you do, it will be virtually impossible to rely on any part of your authentication/authorization method unless you move authentication completely out of the domain of your application code (BTW: this is not the right way to fix the problem).
Certainly searching a very large array (not sure of the actual breakpoint - but in the region of n=1000 - but there are lots of variables affecting this) can be significantly slower than fetching the results from a database.
Its hard to say what you're doing wrong without understanding how your current system works. Is it one of these?