mysql- How to apply grants to column? - mysql

username - revoke select to database.Person
I set
GRANT SELECT (id) ON database.Person TO 'username'#'localhost'
not is work ->
SELECT secret FROM Person // Good!
not is work ->
SELECT id FROM Person WHERE secret = 1 // BAD!
I need that would SELECT id FROM Person WHERE secret = 1 is worked!

I'm not sure that I'm understanding the question correctly, but it seems to be asking for the ability to restrict the person selecting data from the Persons table so that they cannot see the value in the Secret column, but they should be allowed to use the Secret column in the interior of the query (in the WHERE clause, etc).
CREATE TABLE Person
(
Id ...,
Secret ...,
...
);
REVOKE ALL ON Person FROM PUBLIC;
GRANT SELECT(id) ON Person TO SomeOne;
So, if my interpretation is correct, when SomeOne selects data:
SELECT Id FROM Person; -- Works (as required)
SELECT Secret FROM Person; -- Fails (as required)
SELECT Id
FROM Person
WHERE Secret = 1; -- Fails (but we want it to work)
SQL does not allow that, and for good reason. Basically, if you can condition query results on Secret, you can determine the value of Secret with repeated queries, so what is supposed to be secret does not remain a secret. It is very easy to leak information.
Looking at the query that fails but "shouldn't"...from its results, you know that every Id returned has the Secret value of 1, so for those Id values, the Secret is no longer secret.
If you look into Statistical Databases, where you're only allowed to search on aggregate data, you find there are things called Unique Trackers which basically allow you to identify the characteristics of one person, even if you're only allowed to see aggregate (SUM, COUNT, ...) values in the result sets. This is a more complex scenario than you're facing (but a fascinating one). C J Date's (long out of print) "Introduction to Database Systems, Vol II" has a discussion of Statistical Database and Unique Trackers. (Google search on 'statistical database unique tracker' reveals useful looking information that is more accessible.)
So, if I've understood what is desired, I believe the desire is misguided — and the SQL standard does not permit what you seek.
Are there any workarounds?
If the query can be built into a view, the person creating the view can access the underlying detail data and grant access to the view, but the people using the view cannot execute the raw query; this might give you protection you seek. Similar comments apply to stored procedures and allow the query to be parameterized better.

That is impossible to work, as you have not allowed access to the "Secret" column. Either you want the programmer to be able to read Secret. In that case, you should GRANT him access to the Secret column.
If you do NOT want to allow access to the Secret column, then you should not allow a WHERE clause either. Using binary search, it is possible to find out the actual value of Secret. Simply perform a SELECT and check if it returns anything each time, and repeat.

Related

Securing MySQL id numbers so they are not sequential

I am working on a little package using PHP and MySQL to handle entries for events. After completing an entry form the user will see all his details on a page called something like website.com/entrycomplete.php?entry_id=15 where the entry_id is a sequential number. Obviously it will be laughably easy for a nosey person to change the entry_id number and look at other people's entries.
Is there a simple way of camouflaging the entry_id? Obviously I'm not looking to secure the Bank of England so something simple and easy will do the job. I thought of using MD5 but that produces quite a long string so perhaps there is something better.
Security through obscurity is no security at all.
Even if the id's are random, that doesn't prevent a user from requesting a few thousand random id's until they find one that matches an entry that exists in your database.
Instead, you need to secure the access privileges of users, and disallow them from viewing data they shouldn't be allowed to view.
Then it won't matter if the id's are sequential.
If the users do have some form of authentication/login, use that to determine if they are allowed to see a particular entry id.
If not, instead of using a url parameter for the id, store it in and read it from a cookie. And be aware that this is still not secure. An additional step you could take (short of requiring user authentication) is to cryptographically sign the cookie.
A better way to implement this is to show only the records that belong to that user. Say the id is the unique identifier for each user. Now store both entry_id and id in your table (say table name is entries).
Now when the user requests for record, add another condition in the mysql query like this
select * from entries where entry_id=5 and id=30;
So if entry_id 5 does not belong to this user, it will not have any result at all.
Coming towards restricting the user to not change his own id, you can implement jwt tokens. You can give a token on login and add it to every call. You can then decrypt the token in the back end and get the user's actual id out of it.

Mapping strings to relations

I'm creating a system where tasks can be assigned to different users. The problem is that tasks are mapped through a string column called recipient, that in the end maps to a collection of users. The contents of this column could look like this:
has:tasks-update,tasks-access - Users that have the tasks-update and tasks-access Permission.
role:administrator - Users that have the administrator role.
Right now I'm resolving it problematically. This is somewhat easy when I have to figure out who has access to a specific task, but cumbersome when a user needs to know what tasks are "assigned" to them.
Right now I'm resolving each recipient column to see if the user is included, this is unfortunately not very feasible as it comes with a huge performance cost.
I already have indices on the appropriate columns to speed the look-ups up.
A solution to this, was that I would resolve the recipients when the recipient was changed and then place the relationships between users and tasks in an intermediate table. While this lets me quickly look up the tasks a user is assigned to, it also becomes problematic since now I need to keep track of (for example) each time a user has been given the administrator role and now synchronize this to the intermediate table.
I was hoping I could get some insight into solving this issue without sacrificing performance like I am right now, but also not have to synchronize all the time.
Storing a list of anything as a string in a singular column can lead to all sorts of problems down the line
As you have encountered already.. any relational look-up, insert, update or delete operations on the list will first require some form of parsing of the existing list
It is worth noting that any indexes on this column will likely NOT be usable by the engine for these tasks, as indexes on string based columns (other than FULL TEXT) are only really useful when searching the start of the string
For example,
SELECT *
FROM site_user
WHERE recipients LIKE '%tasks-update%'
Will not be able to use an index on the recipients column
A suggestion
I would split out your current lists into new tables, like
role - id, name, …
e.g. {3, 'administrator',… }
permission - id, name, …
e.g. {5, 'tasks-access',… }
e.g. {9, 'tasks-update',… }
site_user - id, name, role_id, …
e.g. {7, 'Jeff', 3,… }
site_user_permission - id, site_user_id, permission_id, …
e.g. {1, 7, 5,… }
e.g. {2, 7, 9,… }
Where from the example records, 'Jeff' is an 'administrator' and has been assigned the 'tasks-update', and 'tasks-access' permissions
Lookups should be easily achievable using JOINs, and stay consistent when data is added or removed. Data integrity can be maintained by adding appropriate foreign keys and unique indexes
N.B. Without specific examples of the operations that are causing you issues, or more details on how you intend to use user roles and permissions, it is difficult to do more than make general suggestions
The tried and good approach, complying to normal forms would be to have task_type and role tables. You of course have a user table and since a user can have many roles and privileges, you will need a user_role and a user_privilege table to handle the many-to-many relations. An easy way to handle the problems is to have some numbers representing some privileges and roles, like 1 for administrator and 2, 3, 5, 7, 11, 13, 17 and so on for other privileges. Having a similar number for a role as a primary key would ease the role matching problem. For example, let's consider the case when you have a privilege with code 7. If you search for roles with the id divisible with this code, then you will get 7 (data_read, for example) and 1 (administrator).
You need for sure a relation table between users and tasks and of course in this relation you have also to flag if user is adminstrator or not. This is the best way for design the structure of your application instead of merging information into a single columns which cause performance/complexity issue. Go ahead with this approach ,your work will benefit from this.

When to use a relational database structure?

I'm creating a file hosting service, but right now I am creating the account email activation part of registering. So I had to come up with a database structure.
And right now it's:
users
id
first_name
last_name
email
password
since
active
hash_activate
But I can do it like a relational database too:
users
id
first_name
last_name
email
password
since
activation
id
user_id
hash
active
What would be the best way to go about it? And why?
If every person has only one activation hash active at at time, then it's feasible to store it in same table with users.
However, one advantage of separating it is that users only have an activation hash for a brief period of time, so to keep the user records smaller, you could store the hashes in a separate table. Keeping the user records small keeps it more performant. In this case, you wouldn't have active column. You'd just delete inactive hashes.
If you do store the activation columns in the user table, just be sure to select the columns by name. E.g. in most cases, you'll want do this:
SELECT id, first_name, last_name, email, password
FROM users
Instead of:
SELECT *
FROM users
You'd only want to select the activation columns when you needed them.
The second would only be sensible if one user could have multiple activations. You don't say whether this is true or false, so I couldn't possibly advise you.
If activations are a temporary thing, or having a hash defines someone as active, then make them different. Otherwise, that really won't matter.
However, neither is necessarily more or less relational than the other, without much more information. If you put a unique constraint on the combination of values in each row, and set each column up with a NOT NULL constraint, your first one would be quite relational.
You use a relational design when correctness of data, over time, is as important, if not more important, than what the application does with that data, and/or when data structure correctness/consistency is critical to the correct operation of an application, but might not necessarily be guaranteed by the application's own operation.

Mysql: allow query on an otherwise inaccesible column?

I have a table with a column that I want to prevent certain users from seeing. I understand that I should be able to do this using a view, i.e. have a view which excludes the particular column, and deny access to the table but allow access to the view (note, users do not need to be able to update the table/view).
I do however want to allow an equality query against the field. Such as:
SELECT * FROM some_table_or_view WHERE hidden_field = 'some_value';
To clarify:
it should not be possible to have the hidden_field values be returned in a general query
it should be possible to run a query with a constraint (preferably only an equality constraint) on the hidden_field value
Is this possible?
(EDIT: if there's a solution in a dbms other than Mysql, I'd be happy to hear about that, too).
You can create a stored procedure which would return all the fields you allowed it to return, and then you can pass the hidden_value (filtering criterion) as a parameter.
Forbid your database users accessing the table, but allow them to call stored procedures.
Then of course, you would have to create several stored procedures if you had several types of queries against the table. But at least it solves your problem with the rights.
No it is not. Giving a user a possibility to filter the results with the column hidden_value means that they have select rights, and that also means they can see the column, and therefore select it.
Here http://dev.mysql.com/doc/refman/5.1/en/grant.html
is a list of the rights you can grant or not grant to the users in mySQL.

Database schema for ACL

I want to create a schema for a ACL; however, I'm torn between a couple of ways of implementing it.
I am pretty sure I don't want to deal with cascading permissions as that leads to a lot of confusion on the backend and for site administrators.
I think I can also live with users only being in one role at a time. A setup like this will allow roles and permissions to be added as needed as the site grows without affecting existing roles/rules.
At first I was going to normalize the data and have three tables to represent the relations.
ROLES { id, name }
RESOURCES { id, name }
PERMISSIONS { id, role_id, resource_id }
A query to figure out whether a user was allowed somewhere would look like this:
SELECT id FROM resources WHERE name = ?
SELECT * FROM permissions WHERE role_id = ? AND resource_id = ? ($user_role_id, $resource->id)
Then I realized that I will only have about 20 resources, each with up to 5 actions (create, update, view, etc..) and perhaps another 8 roles. This means that I can exercise blatant disregard for data normalization as I will never have more than a couple of hundred possible records.
So perhaps a schema like this would make more sense.
ROLES { id, name }
PERMISSIONS { id, role_id, resource_name }
which would allow me to lookup records in a single query
SELECT * FROM permissions WHERE role_id = ? AND permission = ? ($user_role_id, 'post.update')
So which of these is more correct? Are there other schema layouts for ACL?
In my experience, the real question mostly breaks down to whether or not any amount of user-specific access-restriction is going to occur.
Suppose, for instance, that you're designing the schema of a community and that you allow users to toggle the visibility of their profile.
One option is to stick to a public/private profile flag and stick to broad, pre-emptive permission checks: 'users.view' (views public users) vs, say, 'users.view_all' (views all users, for moderators).
Another involves more refined permissions, you might want them to be able to configure things so they can make themselves (a) viewable by all, (b) viewable by their hand-picked buddies, (c) kept private entirely, and perhaps (d) viewable by all except their hand-picked bozos. In this case you need to store owner/access-related data for individual rows, and you'll need to heavily abstract some of these things in order to avoid materializing the transitive closure of a dense, oriented graph.
With either approach, I've found that added complexity in role editing/assignment is offset by the resulting ease/flexibility in assigning permissions to individual pieces of data, and that the following to worked best:
Users can have multiple roles
Roles and permissions merged in the same table with a flag to distinguish the two (useful when editing roles/perms)
Roles can assign other roles, and roles and perms can assign permissions (but permissions cannot assign roles), from within the same table.
The resulting oriented graph can then be pulled in two queries, built once and for all in a reasonable amount of time using whichever language you're using, and cached into Memcache or similar for subsequent use.
From there, pulling a user's permissions is a matter of checking which roles he has, and processing them using the permission graph to get the final permissions. Check permissions by verifying that a user has the specified role/permission or not. And then run your query/issue an error based on that permission check.
You can extend the check for individual nodes (i.e. check_perms($user, 'users.edit', $node) for "can edit this node" vs check_perms($user, 'users.edit') for "may edit a node") if you need to, and you'll have something very flexible/easy to use for end users.
As the opening example should illustrate, be wary of steering too much towards row-level permissions. The performance bottleneck is less in checking an individual node's permissions than it is in pulling a list of valid nodes (i.e. only those that the user can view or edit). I'd advise against anything beyond flags and user_id fields within the rows themselves if you're not (very) well versed in query optimization.
This means that I can exercise blatant
disregard for data normalization as I
will never have more than a couple
hundred possible records.
The number of rows you expect isn't a criterion for choosing which normal form to aim for.
Normalization is concerned with data integrity. It generally increases data integrity by reducing redundancy.
The real question to ask isn't "How many rows will I have?", but "How important is it for the database to always give me the right answers?" For a database that will be used to implement an ACL, I'd say "Pretty danged important."
If anything, a low number of rows suggests you don't need to be concerned with performance, so 5NF should be an easy choice to make. You'll want to hit 5NF before you add any id numbers.
A query to figure out if a user was
allowed somewhere would look like
this:
SELECT id FROM resources WHERE name = ?
SELECT * FROM permissions
WHERE role_id = ? AND resource_id = ? ($user_role_id, $resource->id)
That you wrote that as two queries instead of using an inner join suggests that you might be in over your head. (That's an observation, not a criticism.)
SELECT p.*
FROM permissions p
INNER JOIN resources r ON (r.id = p.resource_id AND
r.name = ?)
You can use a SET to assign the roles.
CREATE TABLE permission (
id integer primary key autoincrement
,name varchar
,perm SET('create', 'edit', 'delete', 'view')
,resource_id integer );