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?
Related
I have a webshop, user will buy something
When the user visit the my web, their data will be saved temporarly in a variable $user as an array
It will be checked when the user buy something, if their balance is more or equal to the things that they want to buy, but the problem arise when there are some users that try to use two browsers to buy things at the same times, their balance just cut once (it should be twice, since they buy it twice using two browsers)
I know I can just update the $user variable before checking, but I will have to run another query to MySQL, and there is many orders ...
Is there any SQL syntax that can be used to prevent this kind of attack?
for checking their balance and make sure it's correct
Based on your current setup (ie using a variable):
Someone using two browser on the same site trying to use up their balance is going to be fairly rare, with most cases someone trying to game your system.
Just finally check their balance at the point of processing the order and if ok allow it, otherwise don't. For those doing it by accident (which is rare), they'll soon realise the error.
An alternative:
It'd be better to check the real data rather than a variable which isn't reliable and has to be forced to be persistent, and as you know not available in different sessions.
I think a better way would be to use some fast centralised persistent storage like Redis (fairly easy to learn, essentially it's an array stored in memory). You can then store their username (or whatever uniquely IDs them) and while they may have different keys across the two browsers, there will be a common unique ID and you can update their credit value in both (all) sessions by searching for the unique ID.
Then whatever browser that user is logged in to will be updated same as other browsers.
Maybe a better idea:
Unless your application needs it, don't let people log in to different browsers/devices. When they try to login, state "already logged in on another place, want to log that one our and log in here?" etc
I am creating a web app for a business which includes several departments. I am using PHP and MySql to do this, in my database i am storing the privileges as ENUM with options N (No access), R (Read Only) and RW (Read and Write).
Once a user logs in, a php script finds the appropriate privileges and stores those in a session. For example: if a user has RW for Production, then there is a session variable $_SESSION['production'] = RW. Now whenever i need to see if a user is allowed to edit production then i check with the session variable and take appropriate actions.
I have 20 such categories of privileges. This certainly isn't the way store something like this. This will simply increase the load time and hog plenty of memory. Are there any alternatives to store the user rights?
Note: The 20 category list is expected to grow.
I typically use
$_SESSION['privileges']=array(
'someprivilege' => 'r',
'someotherprivilege' => 'rw'
)
to keep the memory requirements in context: 100 privileges, the name consisting of 20 chars, the value of 5 chars, will add up to less than a page (4k). The load time issue is heavily mitgated by PHP using a very efficient bnary serializer on session files.
Why wouldn't you store access rights in database and check them when you need by user id?
You'll need to store only user id in $_SESSION.
This simplifies the whole application.
If you store access rights in session, you need to be always sure they they are always up to date at any single point of time. For example if admin wants to change user access rights, they need to have additional ability to log user out. OR for example you need to update your $_SESSION everytime user tries to access the page, which is overhead and anyway requires checking database every time.
You can create an identity class that encapsulate all your users informations (id, privileges and have in it getters for those properties) and have it instanciated once the user is logged in by getting privileges from DB by using user's id.
then each time you need to find his rights just call getRights(); from your identity class.
You should only pick the permissions when they apply. This will keep your load limited to what is needed.
Alternatively you could store the permissions per user in a serialized form. Then this would be a single retrieve operation (instead of 20 or more). Encapsulate the logic behind that so you can equally easy store this both inside the database and session, and easily manage and store them.
Apart from these general suggestions, I can not see much "load" with your question anyway. You need to do what you need to do. You could reduce the number of permissions if you want less hassle, but that will obviously not be useful as you want that.
And then, what's your problem with sessions? Or more specifically, what do you mean by load?
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.
I have been Googling session variables. As I understand it, I can use them to capture such things as username and other data.
I would like to be able to use the data in order to store the username of the last person to change the record of a particular field. Additionally, I would like to be able to limit what a particular user sees on the site. For instance, if a user generates a work order I would like that user to be limited to seeing only the work orders he/she creates and no one else's.
I'm very new at all of this and I don't have a enough understanding to even write some code you might correct for me. How can I send the data stored in the session variable as $_GET or $_POST? If I have it in that format I can use it in the query (I think).
I have a table called "work_orders" with a field called "updated_by". I would like to be able to store the ID of the last person who updated the record.
I would also like to add privileges to the work orders so that users can only view records created by particular users.
This is a project I undertook in my first year web design class. In the class are some students who think it is funny to see how much damage they can do to the work of others. This is the reason I would like to be able to limit them to accessing only the work orders they generate.
You might want to start by reading or watching a video tutorial that will help you better understand how and when to use session variables. The data that you store in the session won't stick around forever, so you will need to store most information in a database.
Here is what most people do: Once the user signs into the application, you put their user id into the session. This way, you know who they are. The server uses a cookie to make this bit of magic happen and when the user closes their browser, the server will forget who they are and the session data will be lost. So, in this example, the session is just keeping track of who the user is.
To store information about who last edited a field, you will probably want to use a database to store that information. There is no use in storing that information in a session variable.
You seem to be mixing your questions here.
Firstly, where is your database? If it were - for instance - MySQl, then you need to add an extra column to the work order table (and others. Personally, I like to add timestamp column to, for auditing porpoises) .
I prefer POST over GET because 1) it can hold more data and 2) it's not so easy for the user to tamper with.
So, if you have a form with an input field declared
<form>
Name:
(see http://www.w3schools.com/html/html_forms.asp)
You can access $_POST['user_name'] e.g
$sql = 'INSERT INTO work_order_table (user_name, ... <other column names>) VALUES($_POST['user_name'], .. <other values>)
I woudl recommend you to get a good book (cheap enough second had) or online tutorial and work your way through.
Pleas let me know if I missed anything or if anything is not clear. Good luck!
Since you are concerend about school kids screwing with your datbase
1) google for how to make MySql more secure
2) hint this will involve prepared staments
3) use POST, rather than get, so that you have urls like http://192.1.68.1.1/application rather than http://192.1.68.1.1/application?user=smith which tempt users to muck around with the URl "just to see what happens" (probably more of a danger than SQL injection at school level, but ... you never know
4) hit the libbrary for soemthign like O'Reilly's PHP & MySql for Dummies in 24 hours for complete idiot beginners - or find a god online tutorial
5) "I would like to be able to limit that particular user to viewing only the work orders they generated" READ up on WHERE in SQL SELECT
6) change the MySql root password - or even add anew user with root-like access then delete root
7) make sure that no student has acess to the server, lest they look at the PHP and see your MySql user & password
the users registered in a web site will have the possibility to send invitations to the friends. I want to add a daily limit for the number of invitations that a user may send.
Initially I've just added a limit (40) in the php file, but then I thought it would be better to give to the administrators the possibility to change this limit, so I've added this limit in the database. But now every time a user want to send invitations the database will be used. Would this affect the performance?
How would you configure this feature?
TL;DR: just put it in the database. :)
Complete story: It should not be a performance hit. Everything (the user itself, the usernames of the recipient, loads of stuff from your page) will be coming from your database. you shouldn't care.
If you have a REALLY big userbase, and it becomes an issue, I'm sure there are other places to do performance updates (like use memcached for all sorts of stuff). But if you want to "cache" it, I guess you could retrieve it once while loggin in and put it in the session. Use this value to substract and check etc. then ALSO check once against the database (in the background) to make sure there isn't any sort of freakish thing going on for this user. But this can be async, and does not have as big of an impact for the user-experience.
In the rare case the session says it's ok, but the database says it isn't, just send the user an error. The other way around, might need the user to re-login. But it will be rare or even impossible if you implement it correctly :)
It depends a bit on hardware but a 400,000 row table (10,000 * 40) isn't that huge in MySQL standards. I think you'll be fine.
Just make sure that you've built it sensibly and from how you've described it that there's an index on the column that stores the unique invite code.