Protect Stored Procedure - mysql

I would like to know if there's a way to conceal the text of a stored procedure from certain users.
I'm working with MySQL 5.1.48 and Net Connector 6.2.3 with Visual Studio 2008 SP1.
I have two users in MySQL, one is root (with all privileges for administration porpuses) and the other one is admin (with select, update, insert, delete, create_temp_table, lock_table and execute privileges in a specific schema).
All the stored procedures have been created by the root user, so the admin user can only execute them without knowing how they work or reading their content.
Everything was working fine until we started creating DataSets and using WebService, for this the admin user needed the select privilege in the column proc from the table mysql. The problem is that with this privilege, admin can see all the stored procedures including their content.
Please let me know if there's a way to protect the SP's, I don't know if this is fixed in the latest version of MySQL and it's .Net connector.

Normally when using MySQL .NET connector in conjunction with stored procedures you have to grant your "application user" select permissions on mysql.proc otherwise the connector can't see the sproc you're trying to call through it.
So usually you have the following two grants for your app user:
grant execute on foo_db.* to foo_dbo#localhost identified by 'pass';
grant select on mysql.proc to foo_dbo#localhost;
As you correctly pointed out this is a bit of a security issue but I do know one way around it which might freak you out but, it works abosultely fine and is actually as performant if not more performant than the standard approach (think - less overhead).
So after revoking grant select on mysql.proc and flushing privileges...
string sqlCmd = string.Format("call list_users({0})", userType);
MySqlConnection cn = new MySqlConnection();
cn.ConnectionString = "Server=....";
adr = new MySqlDataAdapter(sqlCmd, cn);
adr.SelectCommand.CommandType = CommandType.Text; // change from sproc to CommandType.Text
dt = new DataTable();
adr.Fill(dt); //opens and closes the DB connection automatically !!
Hope this helps :)
EDIT: as my answer was causing some confusion I thought I'd add a complete walk through of the problem as I see it...
Create demo database
Just a quick demo database (demo_db) with two users demo_dbo and demo_usr.
demo_dbo is granted full permissions on demo_db - they are the database owner (dbo) and can do as they wish inside this database e.g. create, drop, create procedure, truncate etc...
demo_usr is our application user - they have only been granted execute permimissions so all they can do is call stored procs;
call list_users();
So I login as root and run this script demo_db.sql
drop database if exists demo_db;
create database demo_db
character set latin1
default character set latin1
collate latin1_swedish_ci
default collate latin1_swedish_ci;
revoke select on mysql.* from demo_dbo#localhost;
revoke all privileges on demo_db.* from demo_dbo#localhost;
revoke grant option on *.* from demo_dbo#localhost;
drop user demo_dbo#localhost;
revoke select on mysql.* from demo_usr#localhost;
revoke all privileges on demo_db.* from demo_usr#localhost;
revoke grant option on *.* from demo_usr#localhost;
drop user demo_usr#localhost;
grant all on demo_db.* to demo_dbo#localhost identified by 'pass';
grant select on mysql.* to demo_dbo#localhost;
grant execute on demo_db.* to demo_usr#localhost identified by 'pass';
flush privileges;
select host, user from mysql.user;
Create the demo_db schema
OK now I have a database to play in so next I login as demo_dbo and start to create my schema objects: tables, triggers, sprocs, views etc... (don't forget demo_dbo is the owner and can do as they like inside demo_db)
So I login as demo_dbo and run this script demo_dbo.sql
use demo_db;
drop table if exists users;
create table users(
user_id int unsigned not null auto_increment primary key
)
engine=innodb;
insert into users values (null),(null),(null),(null),(null),(null);
drop procedure if exists list_users;
delimiter #
create procedure list_users()
begin
select * from users order by user_id;
end #
delimiter ;
Ok that's the demo_db schema created (1 table and 1 sproc) so I'd better do some testing while I'm still logged in as demo_dbo.
select from table - PASS
select * from users;
user_id
=======
1
2
3
4
5
6
Call stored procedure - PASS
call list_users();
user_id
=======
1
2
3
4
5
6
Show create procedure - PASS
show create procedure list_users;
Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
========= ======== ================ ==================== ==================== ==================
list_users CREATE DEFINER=`demo_dbo`#`localhost` PROCEDURE `list_users`()
begin
select * from users order by user_id;
end utf8 utf8_general_ci latin1_swedish_ci
Ok that all looks great demo_dbo (owner) can do all the things they should be able to do inside of demo_db so now we'd better test what our demo_usr can get up to.
I login as demo_usr and run the demo_usr.sql script:
show tables - FAIL
show tables;
Tables_in_demo_db
=================
NULL
Not much to see - how disappointing for demo_usr that they can't even see what tables are in the database.
select from user table - FAIL
select * from users;
SELECT command denied to user 'demo_usr'#'localhost' for table 'users'
Ok as expected - they can't access tables directly so I guess the only way they can get data is through my stored procdure API.
call stored procedure - PASS
call list_users();
user_id
=======
1
2
3
4
5
6
Finally a result but we'd better see what information about this sproc the demo_usr can see so...
show create procedure - FAIL
show create procedure list_users;
Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
========= ======== ================ ==================== ==================== ==================
list_users utf8 utf8_general_ci latin1_swedish_ci
The demo_usr can see the stored procdure exists (well they can call it after all) but can't see the body.
Demo Application
So I knock up this quick and dirty C# app that uses MySQL .NET connector (which is great BTW) and attempt to call the list_users() stored procedure and populate a datatable as the application user demo_usr.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MySql.Data;
using MySql.Data.MySqlClient;
using System.Data;
namespace demo_db
{
class Program
{
static void Main(string[] args)
{
MySqlConnection cn = new MySqlConnection("Server=127.0.0.1;Uid=demo_usr;Pwd=pass;Database=demo_db;");
MySqlDataAdapter adr = null;
try
{
DataTable dt = new DataTable();
adr = new MySqlDataAdapter("call list_users", cn);
adr.SelectCommand.CommandType = CommandType.StoredProcedure;
adr.Fill(dt); //opens and closes the DB connection automatically !!
foreach (DataRow dr in dt.Rows)
Console.WriteLine(string.Format("user_id = {0}", dr.Field<uint>("user_id").ToString()));
}
catch(Exception ex)
{
Console.WriteLine(string.Format("oops - {0}", ex.Message));
}
finally
{
adr.Dispose();
cn.Dispose();
}
Console.WriteLine("\nPress any key to quit.");
Console.ReadKey();
}
}
}
Ok this fails - why ?? Here's the exception message:
oops - SELECT command denied to user 'demo_usr'#'localhost' for table 'proc'
Remember we only granted execute permissions to demo_usr for demo_db - nothing else !!
Now we can fix this by granting them select perms on mysql.proc but if we do that they will be able to see the stored procedure body - which we want to avoid.
So how do we get around this ? Well we can login into mysql console as demo_usr and call a stored procedure from the command line so could perhaps do the same in the app code ?
mysql> use demo_db;
Database changed
mysql> call list_users();
+---------+
| user_id |
+---------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
+---------+
So changing the line:
adr.SelectCommand.CommandType = CommandType.StoredProcedure;
to
adr.SelectCommand.CommandType = CommandType.Text;
we now get...
user_id = 1
user_id = 2
user_id = 3
user_id = 4
user_id = 5
user_id = 6
Press any key to quit.
Great - it works.
Hope this helps
Rgds f00

You can restrict premissions to your stored procedures using GRANT.
Perhaps you can use somethng like -
GRANT EXECUTE ON PROCEDURE db.storedproc TO 'username'#'somehost';
You can find the complete syntax for GRANT here
Edit -
GRANT is to provide permissions and REVOKE is to do the opposite of it. You can find more information about REVOKE here.
You can try using -
FLUSH PRIVILEGES;
This reloads the privileges from the grant tables in the mysql database.
If the user already has execute permissions and you want to revoke all the permissions for the user then you can use -
REVOKE ALL PRIVILEGES ON db.* FROM username#somehost
To revoke a user's execute grant on a specific stored procedure, you can use
REVOKE EXECUTE ON PROCEDURE db.storedproc FROM username;
Always make sure that you don't grant global privileges unnecessarily.

Related

mariadb-mysql permissions issue

I have an issues in that when i try to execute a stored procedure through my web application, i am shown the following error
SQLSTATE[42000]: Syntax error or access violation: 1142 INSERT command denied to user 'elitecareers_admin'#'%' for table 'user'
Here is my stored procedure just for clarity
DELIMITER $$
CREATE PROCEDURE `sp_create_account`( username_param VARCHAR(40), email_param VARCHAR(60), pass_w VARCHAR(30), category_id TINYINT )
BEGIN
DECLARE salt VARCHAR(60);
DECLARE password_var VARCHAR(128);
SET salt = 'ELiCrs#4$^7EC%?';
SET salt = CONCAT( username_param, salt );
SET password_var = SHA2( CONCAT( pass_w, salt ), 0 );
INSERT INTO elite.user
( user_id, username, email, pass, active, date_joined, user_category_id )
VALUES
( DEFAULT, username_param, email_param, password_var, DEFAULT, DEFAULT, category_id );
END $$
DELIMITER ;
and here is how the stored procedure looks when i run the show command
Here is my server information too
Here are the privileges for the user 'elitecareers_admin'#'%' under which the web application is executing
What i don't understand if the user has all privileges on the database, then why does mysql/maria server keeping those errors?
"The SQL SECURITY characteristic can be DEFINER or INVOKER to specify the security context; that is, whether the routine executes using the privileges of the account named in the routine DEFINER clause or the user who invokes it. This account must have permission to access the database with which the routine is associated. The default value is DEFINER. The user who invokes the routine must have the EXECUTE privilege for it, as must the DEFINER account if the routine executes in definer security context. " -- https://dev.mysql.com/doc/refman/5.7/en/create-procedure.html
So, regardless of who runs the proc, the permissions for 'elitecareers_admin'#'%' will be used for things such as accessing the table elite.user. But that user seems to have privileges only to the database elitecareers\_elite.
Plan A: Recreate the proc with SQL SECURITY INVOKER if that is appropriate.
Plan B: Figure out the difference between databases elite and elitecareers\_elite.
Plan C: Add another GRANT to let that admin get to elite.
Plan D: (There are probably other solutions.)

How to GRANT SELECT permission to all procedures

To secure the database interaction, I have created a restricted user who can only execute the stored procedures. He doesn't have any rights to query any table.
This scenario I have implemented very well!. Now the problem is, one of my stored procedure involves dynamic SQL execution, which fails the execution by saying that I don't have the permission to run SELECT query on table X.
To avoid this, I need to provide explicit SELECT permission to such procedures NOT TO USERS.
Please help me!
There is no build in function to give execute to a user.
Create a role with execute permission and add that role to the user.
CREATE ROLE db_executer
GRANT EXECUTE to db_executer
EXEC sp_addrolemember N'db_executer', N'<username>'
MNagel has the correct link for this, but to reiterate, you want to look at using something like:
CREATE PROC dbo.MyProc
WITH EXECUTE AS 'SomeUser'
AS
BEGIN --Proc
--Do Stuff
END --Proc
GO
"Execute As" has some other gotchas along the way. You have to allow others to impersonate the specified user and that user would need the appropriate select permissions to the underlying tables.
If you want to mass grant permissions, you can do that at the Schema level instead of the object level - something like:
GRANT SELECT, EXEC ON Schema::dbo TO MyRole;
I've used that before to greatly simplify a lot of grant statements for our roles. We have very few overrides for the more general roles so this helps quite a bit.
define the stored procedure to be executed as a user with appropriate rights:
http://msdn.microsoft.com/en-us/library/ms188354.aspx
Is this what you want?
USE DatabaseName
GO
-- 1 - db_executestoredprocedures
-- 1a - Create role
CREATE ROLE db_executestoredprocedures
GO
-- 1b - Grant permissions
GRANT EXECUTE TO db_executestoredprocedures
GO
-- 2 - db_selecttablevaluedfunctions
-- 2a - Create role
CREATE ROLE db_selecttablevaluedfunctions
GO
-- 2 - Create permissions
DECLARE #Function_Name nvarchar(250);
DECLARE #CMDEXEC1 nvarchar(2000);
DECLARE db_cursor CURSOR FOR
SELECT [name]
FROM sys.objects
WHERE Type = 'TF'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #Function_Name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #CMDEXEC1 = 'GRANT SELECT ON [' + #Function_Name + '] TO db_selecttablevaluedfunctions;'
--- SELECT #CMDEXEC1
EXEC(#CMDEXEC1)
FETCH NEXT FROM db_cursor INTO #Function_Name
END
CLOSE db_cursor
DEALLOCATE db_cursor
GO
On the SP where sql is complaining about you not having the permissions to run a SELECT query on table X, do you have a broken chain of ownership on that particular SP and table?

Can't execute MySQL stored procedure

I'm dipping my toe into using stored functions with MySQL and am having trouble.
Having created a function and tested it, I don't seem to be able to allow other users to execute it. From the documentation, it seems that I need to grant EXECUTE access to the other users, but that doesn't appear to be sufficient.
I've put together a couple of scripts that I believe demonstrate the problem:
# This script creates two databases with a stored function in each.
#
# On one database, tester in granted all privileges.
# On the other, tester only gets a few.
#
# We want to find the minimum privileges required to allow tester to execute the
# stored function.
#
# This script must be run by an administrative user, i.e. root
CREATE DATABASE test1;
DELIMITER $$
CREATE FUNCTION test1.foo () RETURNS VARCHAR(255) DETERMINISTIC
BEGIN
RETURN ('garp');
END$$
DELIMITER ;
GRANT ALL PRIVILEGES ON test1.* TO 'tester'#'localhost';
#
CREATE DATABASE test2;
DELIMITER $$
CREATE FUNCTION test2.foo () RETURNS VARCHAR(255) DETERMINISTIC
BEGIN
RETURN ('garp');
END$$
DELIMITER ;
GRANT EXECUTE ON PROCEDURE test2.foo TO 'tester'#'localhost';
and
# This script tests whether tester can access the stored functions
#
# It should be executed by tester
SELECT 'test1.foo(): ', test1.foo ();
SELECT 'test2.foo(): ', test2.foo ();
When I run execute the second script, I get an error:
$ mysql --user=tester --password=tester --skip-column-names < testScript2.sql
test1.foo(): garp
ERROR 1370 (42000) at line 6: execute command denied to user 'tester'#'localhost' for routine 'test2.foo'
I have no doubt that I'm missing something obvious, but I can't see what that is. I imagine that I've got something wrong in the GRANT EXECUTE... statement in the first script, and am deeply suspicious of my use of single quotes, but I recall trying most of the combinations of placement and inclusion of single quotes without success.
I'd be really grateful to anyone who can point out my error.
For reference, I'm running Server version: 5.1.67-0ubuntu0.10.04.1 (Ubuntu) (on Ubuntu!).
Thanks
test2.foo is a function not a procedure.
Try:
GRANT EXECUTE ON FUNCTION test2.foo TO 'tester'#'localhost';
(I was able to reproduce the problem locally and confirm that this change works.)

SELECT command denied to user

Im getting a funny MySQL error. Here's whats happening:
query:
SELECT COUNT(id) AS mycount FROM ip_protection.hits WHERE datetime_created>DATE_SUB(NOW(), INTERVAL 10 MINUTE) AND ip='166.248.6.19'
error:
SELECT command denied to user 'goatpric_db'#'166.248.6.19' for table 'hits'
privileges:
GRANT ALL PRIVILEGES ON `goatpric\_ip\_protection`.* TO 'goatpric_db'#'%'
'hits' is a table in ip_protection. Any thoughts?
You are using wrong database name everywhere. Use correct Database, which according to your question is ip_protection.
Add Use Procedure Bodies = false; to the connection string.
For example:
server=localhost; user id=dbuser; password=password; database=dbname; Use Procedure Bodies=false;

Grant select on views which use functions

I'm using postgresql 8.4 and am having a bit of a problem granting select privileges on a view from a database onwed by a different user when the view uses any of the databases functions.
As the new user, when I try to run, for example select * from users_pwd; where users_pwd is defined as:
create view users_pwd as
select *, get_pwd(id)
from users;
and get_pwd as:
CREATE OR REPLACE FUNCTION get_pwd(p_id integer)
RETURNS text AS
$BODY$
declare u record;
BEGIN
select into u * from users where id = p_id;
return u.password;
END;
$BODY$
LANGUAGE plpgsql;
I get the following error:
ERROR: permission denied for relation users
CONTEXT: SQL statement "select * from users where id = $1 "
PL/pgSQL function "get_pwd" line 3 at SQL statement
The only way to have the user query the view is to explicitly grant select on the table users which I don't want to do.
If a view doesn't use any function, but rather just other tables which the new user doesn't have explicit access to it works perfectly fine.
You could create the function with owner who can select from the table users. Such a function should be created with SECURITY DEFINER clause, so it will be executed with the owner rights.
More information you can find here: http://www.postgresql.org/docs/9.0/interactive/sql-createfunction.html
You can also GRANT EXECUTE privileges on functions. See GRANT in the docs.