User permission and software security - php

I am developing an application in php codeigniter. Now I am worrried abt the permission.
I need page wise permission, page may be add records page, edit page, delete page and print report etc. There will be many users as well, and applicaiton will grow with passage of time.
If I implement ACL that will better for me or not
what can be ideal for me any suggestion.

First, let's clear up some terms: I personally use the security term for things like preventing SQL injection, XSS attacks, where we have to validate input, filter/sanitize values, take care of the dynamically generated SQL commands, take care of properly escaping output (for JSON or HTML text or HTML attributes), etc. This is not about what you are asking, if I understood well.
The access control or permissions system is where you give or deny access to a function for a user. It can be secure or not. I understand that to deny a user which does not have permission the access to a function may sound like "security", but I wouldn't use this specific word in this context, to avoid confusion.
Now, the answer:
I strongly recommend you create a few base controller classes to your needs. Read the following blog post carefully (it is short and useful): http://philsturgeon.co.uk/blog/2010/02/CodeIgniter-base-Classes-Keeping-it-DRY
A code to check if the user is properly authenticated (logged in) is essential. If the user is not logged in, redirect to home page or login page.
For fine-grained control, you could create your ACL in the database using the users table, plus an actions table, plus an acl table...
The users table would contain the users data (id, name, login, password, etc)
The actions table would contain the id field and at least one more field containing what suits best for your application: it can be only the controller class name (the first part of the URL, for example: "products"), granting access to the whole "products" controller or not. Or you may want to include both the controller class AND the method name (the first and second parts of the URL, for example: "products/add" and "products/delete"), and so on.
To decide about the actions table is the most decisive step. Think very well about it, balance your needs (your "true" needs)... I developed a system where each and every action has its entry. It is good, but it needs work to be maintained.
A very useful column for the actions table is a human-readable description of the action.
The acl then would be nothing more than a column for the user id and another column for the action id.
A "master" grant/deny access field in the users table is useful too, in case you want to temporarily deny access from a specific user, without having to delete all his permissions and maybe having to restore it later.
With the database tables and your "controller/method" or "actions" strategy well defined, you can easily code in your base controller class a function which checks if the user have permission to execute the requested action.
This is the basic. In my system, I have the users administration interface, where I can grant/deny the actions for each user (I use an ExtJS tree with checkboxes). One of these actions is the own user management. I have gone one step further, where the user who can access the user management may "delegate" (grant/deny) to other users only the actions he himself has access to.
The system has several modules, and functions. The interface does not show anything the user does not have access. So, I have users who can see only a single or a couple of modules, and they don't even imagine the existence of the other modules.
It requires more work to manage all this, but the result worths.
I also log each granted access, so it is possible to track who did what, and when. This log feature is very very easy to add, since you have this base controller "master function" allowing or disallowing the users to perform the actions.
I hope I have helped. I've just shared a bit of what worked (and works) for me...

Related

Is it safe to use variable session data?

I have a (hopefully) quick question regarding sessions. Whilst I have used sessions extensively, I have not used them in a situation whereby the values change depending on a users actions.
After logging in to my application, a user can select a company area, which has many levels of pages and folders. All of these pages will need this 'company_id'. At the moment I send the company_id via GET, but as I get deeper into the application this is becoming increasingly hard to maintain, with various other data being stored in the URL.
Therefore, when a user selects their company, I could set their company_id in $_SESSION array. However, when a user changes company, I would then need to change $_SESSION['company_id'] to the new value.
Is this a good use of sessions? I could potentially clean up my urls by using session data rather than always using GET, but I am unsure if this is a recommended way of using sessions.
Thanks in advance
This is a bad implementation of the HTTP design philosophy. All HTTP requests should be self contained, RESTful. All information needed to get a specific page should be present in the request itself (URL, headers and body), not dependent on hidden state.
Super trivial example: you can't copy a URL to someplace or someone else and have them see the same page. The content of the page is dependent on session state, which has been laboriously set through the visit history of several previous pages. To return to this same page, you need to retrace the same steps, recreating some hidden server-side state to arrive at the same page.
This gets even more complex and messier if you take into account that a visitor may want to open pages requiring different states in two or more simultaneous tabs/windows.
All this isn't to say that it can't work, only that it's hideously complex and will break the usual expected behaviour of browsers, unless you really bend over backwards to somehow prevent that.
If the many levels of pages and levels are per-company, you can put the company_id in a specific include file - this part of the site being dedicated to a given company.
However if they're shared by multiple companies, and this is probably what you want, this is potentially misleading, or even dangerous depending on the user actions, since the user may jump to a given page (link...) and access a page with unexpected data linked to a company which ID is provided by the session or cookie.
You could dynamically build the links on a page, based on IDs, to ensure consistency during the navigation from that page. Any direct "jump" to another part of the site will not carry the ID with it (and the page may offer to select a company).
Depending on your web server and if you have control over it you could build the URL having "company ID" as an element of the URL path, not the GET parameters
Eg
http://example.com/invoicing/company382/listprices.php
using a rewrite (web server configuration) to change the URL to be actually used to
http://example.com/invoicing/listprices.php?compid=company382
(URL not visible to the user) that informs of the company ID via the GET parameters.

What is the proper way to limit access to many pages upon user basis?

Restrict access. I have a table that, to simplify has 3 columns. user, pass, access.
I have 4 types of pages... One requires 1, aka super admin. Another requires 2, aka general admin... the other 3, for mod. The last, any USER may access.
I can write the code, but how should I do it?
I came up with 2 options on my own: Function that goes into my library or include file that I include and if they shouldn't be able to access, it redirects to "restricted".
Are there other options? What's the most common way to do this? The most efficient to re-use code.
My only real piece of advice: don't write your own ACL library.. there are plenty of them out there. Take a look at https://packagist.org/search/?q=acl and see if there's one that looks like it would fit your use case. I've been looking at https://github.com/alexshelkov/SimpleAcl and it may be a good one to start with (I haven't used it myself though).
There are two ways to go about what you're trying to achieve. The way I usually go, is to hide links based on access level. If a use isn't permitted to view specific content, create a function to hide the link and only show it to users with the right permissions.
If a user types the URL of a restricted page, then they should be redirected to a default page, maybe the home page, or a page that explains why they can't go "here". You only need to test the privilege, and have one default target page. Any "secure" page should simply test the current access level against the page's level, and redirect if not equal or greater.
The alternate way is to create a default "access denied" page that loads when sufficient privileges aren't available to the user.
I always go with the first method. It's simple enough to implement, and reduces many headaches, and, most importantly, user frustration. It's more of a user interface question than a programming question.
Ask yourself this, how annoying would amazon.com be if there were admin links on the site that didn't go anywhere?

How can I restrict certain content in CakePHP?

I'm pretty new to using CakePHP but I'm already finding incredibly useful for rapidly developing web apps.
However, I was wondering if there is a way to restrict access to certain objects in a non-standard way. For example, if I was to create a single CMS system allows users to create a "site", how can I assert that the users (multiples) have access to that particular site?
I could check this in my site controller but would I need to check this for every single controller on my site - for example, I would need to check that the current Page, News, Contacts, Files etc being edited belongs to the site ID and the user has access to edit it?
ie, thecms.com/pages/edit/123 (how can I be sure user 9 can edit page 123 which belongs to site 2)
I'm assuming this is outside of what ACL can offer as they're entity specific. Is there any easy way to do this?
I assume that you already know about CakePHP's Auth and ACL component, which can provide ways for restricting content. But it's also true what you say: that CakePHP's ACL is 'entity-specific' and not the best option for a 'per-record' basis (e.g. user 3 shouldn't access article 7). So I propose this code; try it and let me know:
Within the app/app_controller.php file:
<?php
class AppController extends Controller {
function checkPermission($aro, $aco, $loggedUserRole = 'User') {
if ($loggedUserRole != 'Admin') {
$permission = ($aro == $aco);
if (!$permission) {
$this->Session->setFlash('You cannot access that.');
$this->redirect('/somewhere');
}
}
}
}
Then, within the action that you want to restrict, put:
$this->checkPermission($this->Auth->user('id'), $someId, $this->Auth->user('role'));
So, the checkPermission() function does the following:
When called, you pass an id for the user you want to authorize, an id of the thing the user is trying to access, and also a role (so it is assumed that there is a column of 'Role' within the users table; also, $this->Auth->user('id') means the user session data automatically stored by the Auth component). The checkPermission() method then checks if the passed role is not an admin (supposing there will be admins who will have access to all), and then checks equality of both the $aro and $aco parameters. If not, then it redirects somewhere with a message of 'forbidden'.
Now, the $aco parameter can be a variety of things. For example, suppose you are making a function for editing a user's account, which takes an argument of $userId. So you compare the logged-in user's id (from $this->Auth->user('id')) with the passed $userId. If not equal, it means the logged-in user should not be editing that user record.
Another example:
You have an action for deleting an article, which accepts an $articleId argument. You can fetch the article from the database and then pass the article's user_id value as the $aco, before deleting the article. Again, the logged-in user would be kicked out if such user_id is not him.
I hope this solution serves you well.

Best way to track the stages of a form across different controllers - $_GET or routing

I am in a bit of a dilemma about how best to handle the following situation. I have a long registration process on a site, where there are around 10 form sections to fill in. Some of these forms relate specifically to the user and their own personal data, while most of them relate to the user's pets - my current set up handles user specific forms in a User_Controller (e.g via methods like user/profile, user/household etc), and similarly the pet related forms are handled in a Pet_Controller (e.g pet/health). Whether or not all of these methods should be combined into a single Registration_Controller, I'm not sure - I'm open to any advice on that.
Anyway, my main issue is that I want to generate a progress bar which shows how far along in the registration process each user is. As the urls in each form section can potentially be mapping to different controllers, I'm trying to find a clean way to extract which stage a person is at in the overall process. I could just use the query string to pass a stage parameter with each request, e.g user/profile?stage=1. Another way to do it potentially is to use routing - e.g the urls for each section of the form could be set up to be registration/stage/1, registration/stage/2 - then i could just map these urls to the appropriate controller/method behind the scenes.
If this makes any sense at all, does anyone have any advice for me?
I think creating a SignupController is a fine idea. The initial user registration is a distinct process, and ought to be separate from general profile management tasks.
If you've been a good developer and keeping your controllers thin and your models fat, you ought to be able to avoid any code duplication. If you find yourself duplicating, it's probably a good idea to think about refactoring.
As a concrete example, consider your user's email address. Let's say you're pretty strict, and any time a user changes their email address, they have to do a little confirmation-email dance. During signup, you'll want the user to return to the signup process after they click their confirmation link. When an existing user is changing their email address, you'll want them to land somewhere else (like their profile). It's likely that you'll want different content in the body of the confirmation email is each case. Trying to make /user/profile handle both cases is going to start creating a bunch of complexity where the action needs to figure out context and behave accordingly.
The better solution is to decide that signup is it's own mode of interaction, distinct from general profile management. Therefore, it gets its own controller, which shares model and view resources with other controllers.
That's my take, anyway.

Fast, easy, and secure method to perform DB actions with GET

Sort of a methods/best practices question here that I am sure has been addressed, yet I can't find a solution based on the vague search terms I enter.
I know starting off the question with "Fast and easy" will probably draw out a few sighs, so my apologies.
Here is the deal.
I have a logged in area where an ADMIN can do a whole host of POST operations to input data relating to their profile. The way I have data structured is pretty distinct and well segmented in most tables as it relates to the ID of the admin.
Now, I have a table where I dump one type of data into and differentiate this data by assigning the ADMIN's unique ID to each record. In other words, all ADMINs have this one type of data writing to this table. I just differentiate by the ADMIN ID with each record.
I was planning on letting the ADMIN remove these records by clicking on a link with a query string - obviously using GET. Obviously, the query structure is in the link so any logged in admin could then exploit the URL and delete a competitor's records.
Is the only way to safely do this through POST or should I pass through the session info that includes password and validate it against the ADMIN ID that is requesting the delete?
This is obviously much more work for me.
As they said in the auto repair biz I used to work in... there are 3 ways to do a job: Fast, Good, and Cheap. You can only have two at a time. Fast and cheap will not be good. Good and cheap will not have fast turnaround. Fast and good will NOT be cheap. haha
I guess that applies here... can never have Fast, Easy and Secure all at once ;)
Thanks in advance...
As a general rule, any operation that alters state (whether its session state, or database state) should use POST. That means the only 'safe' SQL operation you can perform with GET is SELECT. Even if you're only using a back-end admin thing, you shouldn't use get. Imagine re-opening your browser and finding that the last time you closed firefox was on your 'DELETE EVERYTHING' GET->delete page resulting in everything being deleted again.
One of the main reasons for this is preventing cross-site request forgeries. For example, if you had a page that took a GET variable such as http://example.com/account?action=logout, an attacker could post an image on your site like this:
<img src="http://example.com/account?action=logout" />
and anyone who opened a page containing that image tag would be immediately logged out, even if they were an admin. It would be very annoying to then search through your raw database for that data and remove it.
Although POST operations are 'nearly' as easy to forge, as a general rule with any web security issue, the trade-off is speed/simplicity vs. security, so you're going to have to choose one or the other.
You should have some kind of session set up.
Using POST over GET gets you nothing tangible as far as security is concerned. POSTs can be forged just like GETs.
So assuming once your admin logs in, you've got some kind of identifier in the session, you just leverage that.
Consider something roughly similar to this:
<?PHP
session_start();
if (empty ($_SESSION['admin_id'])) die("Log in, son.");
if (empty($_GET['record_id'])) die("You've got to tell me which record to delete!");
if (! is_numeric($_GET['record_id'])) die("Invalid record ID");
//just to be totally safe, even though we just enforced that it's numeric.
$record_id = mysql_real_escape_string($_GET['record_id']));
$sql = "DELETE FROM table WHERE record_id='{$record_id}' AND admin_id = {$_SESSION['admin_id']}";
mysql_query($sql);
echo "Record was deleted (assuming it was yours in the first place)";
?>
In that example, we're avoiding the "delete someone else's records" problem by leveraging the WHERE clause in the DELETE query. Of course, to be more user friendly, you'd want to first fetch the record, then compare the admin_id on the record to the admin_id in $_SESSION, and if they don't match, complain, log something, throw an error, etc.
HTH
You're saying: "What if Admin 123 access the URL of Admin 321 directly, thus deleting his stuff?" no?
If that's so then, every admin that is logged in should have at least one session with some unique identifier to that admin. If not, he shouldn't be able to be in the admin section in the first place. Can't you just compare the identifier in the URL with the identifier in the session?
If any user, not just an admin, access those 'delete me' URLs he should be identified by a session as the original 'owner' (admin) of that data.

Categories