I have the MySQL version of this question for SQL-Server
I'm running mysql Ver 14.14 Distrib 5.5.49, for debian-linux-gnu (x86_64) using readline 6.3
I have created a user with only this permission:
CREATE USER 'username'#'localhost' IDENTIFIED BY 'p#55w0rd';
GRANT EXECUTE ON dbname.* TO 'username'#'localhost';
I have created this procedure on dbname:
DELIMITER $$
CREATE PROCEDURE seed_database()
this_procedure:BEGIN
INSERT INTO `dbtable` VALUES (1,'data');
END $$
DELIMITER ;
But when I login with that user and try to CALL seed_database();, I get:
ERROR 1142 (42000) at line 1: INSERT command denied to user 'username'#'localhost' for table 'dbtable'
I'm trying to follow the principle of least privilege by only allowing a user to execute stored procedures. I don't want to give a user privilege to directly INSERT on a table with the obvious GRANT INSERT ON dbname.dbtable TO 'username'#'localhost';, in case the credentials are compromised and because I have some complex logic inside the stored procedure that produces inputs to be stored (represented by hard-coded data in the example (1,'data')) that I don't want the user generating and inserting directly. But I would like for them to use the stored procedure to accomplish the same objective.
I would suggest you read up on definer's rights procedures. You should be able to define your procedure to run as a privileged user, then GRANT EXECUTE to unprivileged users:
GRANT INSERT ON TABLE dbtable
TO 'privileged_user'#'localhost';
CREATE
DEFINER = 'privileged_user'#'localhost'
PROCEDURE seed_database()
BEGIN
INSERT INTO `dbtable` VALUES (1,'data');
END;
GRANT EXECUTE ON PROCEDURE dbname.seed_database
TO 'unprivileged_user'#'localhost';
DISCLAIMER: I'm not set up to test this at the moment, but it should work.
By default, MySQL executes stored procedures with "definer's rights," that is, with the privileges of the person who is creating the stored procedure. This means that this user must have privileges on all the data objects the procedure accesses. When the DEFINER clause is specified in the CREATE FUNCTION / PROCEDURE, MySQL will instead execute the procedure with the privileges of the user named in the DEFINER clause. In both cases, as long as the definer has privileges on the data objects, the invoker only needs privilege on the procedure itself.
Invoker's or definer's rights can also be specified explicitly, as in
CREATE PROCEDURE seed_database()
SQL SECURITY DEFINER
BEGIN
...
Specifying SECURITY DEFINER without a DEFINER = clause causes the definer to default to the person actually executing the CREATE statement. This is the same as not specifying either clause.
Specifying SECURITY INVOKER causes MySQL to execute with the privileges of the person using the stored procedure. This means that the invoker must have privileges on the procedure and on all data objects the procedure accesses. This may be done, for example, with administrative routines so that a user who isn't allowed to muck about in the system tables also can't use a procedure that mucks about in the system tables even if accidentally granted access to that procedure.
Related
Basically I am trying to create a stored procedure with an if else statement that checks the mysql server version and based on the version executes a create user with the correct mysql syntax to create the user. we have a mysql 5 and mysql 8 server, and depending on the customers choice we need to create a user on the specific server version, ie mysql 8 or 5
USE test;
DROP PROCEDURE IF EXISTS add_user ;
CREATE PROCEDURE add_user()
BEGIN
IF (SELECT VERSION() = '5.7.20-log') THEN
GRANT ALL PRIVILEGES ON test.* TO 'akoos'#'localhost' IDENTIFIED BY 'Password';
ELSE
CREATE USER 'akoos'#'localhost' IDENTIFIED BY 'Password';
GRANT ALL ON test.* TO 'akoos'#'localhost';
END IF ;
FLUSH PRIVILEGES;
END
;
CALL add_user() ;
DROP PROCEDURE IF EXISTS add_user ;
I tried this but I keep getting syntax errors, all i need to do is create a stored procedure with the correct syntax to create the user and grant privilidges based on the server version its executed on. Ie if its 5 execute the mysql 5 code for creating a user, if its mysql 8 execute the code for creating a user in mysql 8 syntax. The reason I need this is the wordpress intaller that we use has a mysql script which creates the below code
Mysql 5 code
USE test;
DROP PROCEDURE IF EXISTS add_user ;
CREATE PROCEDURE add_user()
BEGIN
GRANT ALL PRIVILEGES ON test.* TO 'akoos'#'localhost' IDENTIFIED BY 'Password';
FLUSH PRIVILEGES;
END
;
CALL add_user() ;
DROP PROCEDURE IF EXISTS add_user ;
Mysql 8 needs to use the following syntax to create a user
CREATE USER 'akoos'#'localhost' IDENTIFIED BY 'Password';
GRANT ALL ON test.* TO 'akoos'#'localhost';
So I want to use an if else statement to create a stored proc based on the db server version used
You should use the CREATE USER / GRANT method for both versions.
https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_auto_create_user says:
It is preferable to create MySQL accounts with CREATE USER rather than GRANT. NO_AUTO_CREATE_USER is deprecated and the default SQL mode includes NO_AUTO_CREATE_USER. Assignments to sql_mode that change the NO_AUTO_CREATE_USER mode state produce a warning, except assignments that set sql_mode to DEFAULT. Expect NO_AUTO_CREATE_USER to be be removed in a future release of MySQL, and its effect to be enabled at all times (and for GRANT not to create accounts any longer).
While it is enforced in MySQL 8.0 that you can't create a user implicitly by granting privileges to them, it's a good idea to let the NO_AUTO_CREATE_USER mode be in effect in MySQL 5.x as well. You should create the user explicitly no matter what version you use.
Therefore there's no need for your conditional code.
If you did want to write the conditional code, you can reference a system variable this way:
IF (##version = '5.7.20') THEN
...
The -log suffix may or may not be there. It's the old, old way that MySQL indicated that one of the query logs or the binary log were enabled, before there were other configuration variables for each of the logs. This weird feature of the version variable is no longer used (finally) in MySQL 8.0. But in both versions, there may be another suffix -debug that indicates the MySQL Server instance is running a debug build.
So a better to test this variable would be:
IF (LEFT(##version, 2) = '5.') THEN
...
Then you don't have to worry about version suffixes, or if it's 5.6 versus 5.7, or if it's 5.7.21 versus 5.7.40.
We created a user/login for some devs, and we have an issue where this user/login cannot view stored procedure code.
This is for MySQL 5.6.
I checked the GRANTS and also the information_schema (schema_privileges) and things look "good" to me.
Here are the commands I used to GRANT the database access and privileges:
GRANT SELECT, INSERT, UPDATE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON `mydatabase`.* TO 'dev-user'#'%' ;
After that, I run the FLUSH PRIVILEGES command.
And, when I run SHOW GRANTS for `dev-user` , I get the following response:
GRANT SELECT, INSERT, UPDATE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON mydatabase.* TO 'dev-user'#'%'
After that the user runs the command SHOW CREATE procedure mydatabase.sp_test and the output has NULL in the CREATE PROCEDURE Column.
This same user/login can create a new procedure and running SHOW CREATE procedure has the procedure code visible in that CREATE PROCEDURE column.
And, as a quick test, i created another user with GRANT ALL PRIVILEGES on the database and i get the same results. That user also not see the stored procedure code.
When I run a query on the information_schema.SCHEMA_PRIVILEGES system table, i can see that the user has included:
ALTER
ALTER ROUTINE
CREATE
CREATE ROUTINE
Does anyone have any suggestions or can see something i am missing, or forgot?
Thanks for any help
As stated in the documentation:
To use either statement, you must be the user named in the routine DEFINER clause or have SELECT access to the mysql.proc table.
So grant them that access:
GRANT SELECT ON mysql.proc TO 'dev-user'#'%';
BTW, it's not necessary to use FLUSH PRIVILEGES after the GRANT statement. See MySQL: When is Flush Privileges in MySQL really needed?
I was wondering if it's possible to grant EXECUTE permissions to a user without granting SELECT, INSERT etc. permissions on the table that a procedure runs on?
Using it for a Logins table for a webapp. MySQL is running in a Docker container. SQL for creating procedures is copied across as part of the docker build process (when run, the sql is used in entrypoint.sh). Login_db is created when running the container (-e flag).
I'd like to remove the GRANT SELECT line from below so, no matter what happens, the webapp server can never run a SELECT query - such as doing SELECT * FROM logins.
CREATE USER 'logins'#'172.24.0.7' IDENTIFIED BY 'some-password';
GRANT SELECT, INSERT, UPDATE on logins_db.login TO 'logins'#'172.24.0.7';
GRANT EXECUTE ON PROCEDURE logins_db.sp_login16 TO 'logins'#'172.24.0.7';
FLUSH PRIVILEGES;
This doesn't solve it - as being the table owner would expose the same privileges:
Execute stored proc fails with GRANT EXECUTE because of table permissions
This might explain why I can't, but the table names are a bit odd to me (MySQL newbie - I'm under the impression that mysql.proc is a system table, so not sure if it applies):
How to grant execute on specific stored procedure to user
Could it be that root doesn't have SELECT privileges when creating the procedure and so the the logins user cannot run it? (Because Docker MySQL runs entrypoint.sh and then the environment variable)?
The procedure code is here (I know, not the most elegant) - could I GRANT and then REVOKE privileges for the logins user within this, considering the DEFINER is root ?
CREATE DEFINER=`root`#`localhost` PROCEDURE `sp_login16`(
IN p_email VARCHAR(120),
IN p_password VARCHAR(120))
BEGIN
SELECT user_id,user_password FROM login WHERE user_email = p_email;
Yes, you can do this by using sql security definer while declaring the stored procedure:
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.
The DEFINER clause specifies the MySQL account to be used when
checking access privileges at routine execution time for routines that
have the SQL SECURITY DEFINER characteristic.
If a user value is given for the DEFINER clause, it should be a MySQL
account specified as 'user_name'#'host_name', CURRENT_USER, or
CURRENT_USER(). The default DEFINER value is the user who executes the
CREATE PROCEDURE or CREATE FUNCTION statement. This is the same as
specifying DEFINER = CURRENT_USER explicitly.
To sum it up: the user in the definer clause has to have the select / insert privileges to the underlying table in this ase, while the user who executes the stored proc must have execute privileges to the stored proc.
Added a new user ADMIN#localhost with SELECT, INSERT and UPDATE privileges. ADMIN then became the DEFINER for all the procedures, with 'logins'#'172.24.0.7' only being granted EXECUTE permissions. Runs perfectly now!
Apparently you can't use root in the way I was trying to. Kudos to #Shadow for pointing me in the right direction.
Setting up the admin user:
CREATE USER 'admin'#'localhost' IDENTIFIED BY '<password>';
GRANT SELECT, INSERT, UPDATE, DELETE ON db.table_name TO 'admin'#'localhost';
GRANT ALTER ROUTINE, CREATE ROUTINE, EXECUTE ON *.* TO 'admin'#'localhost';
FLUSH PRIVILEGES;
Defining a stored procedure that creates a entry using the limited admin user
DELIMITER $$
CREATE DEFINER=`admin`#`localhost` PROCEDURE `sp_createTableEntry`(
IN value_one VARCHAR(120),
IN value_two VARCHAR(200)
)
BEGIN
IF ( select exists (select 1 from table_name where column_one = value_one) ) THEN
select 'Column One Exists !!';
ELSE
insert into table_name
(
column_one,
column_two
)
values
(
value_one,
value_two
);
END IF ;
END $$
DELIMITER ;
Create procedure:
CREATE DEFINER=`gnysoftxuser`#`%` PROCEDURE `insertadmin`(IN adi VARCHAR(150))BEGIN Insert Into mekan_tablo (mekan_adi) values (adi); END;
Mysql query
CALL insertadmin('test');
Mysql error:
execute command denied to user 'gnysoftxuser'#'%'for routine 'xdb.insertadmin'
That means that the MySQL user that you are connecting with does not have the EXECUTE priviledge for the database. https://dev.mysql.com/doc/refman/5.5/en/privilege-system.html
I can find no documentation on it but it seems that DEFINER user has to have EXECUTE granted even though it is not doing any executing(1).
Create user userexecuteonly
Grant userexecuteonly EXECUTE
Create user userselectonly
Grant userselectonlySELECT
Create procedure setting DEFINER as userselectonlythat selects data
Connect as userexecuteonly
Call procedure
note error SQL Error (1370): execute command denied to user userselectonly
Grant userselectonlyEXECUTE
Call procedure
Procedure runs and returns data.
(1) There is no reason the procedure would not call another procedure
Which could be a concern if userselectonly is used elsewhere and should not be able to EXECUTE procedures.
You need to grant execute privileges. From the MYSQL command line, logged in as a user with the ability to grant, you would write:
GRANT EXECUTE ON PROCEDURE 'xdb'.'insertadmin' TO 'gnysoftxuser'#'localhost';
flush privileges;
(in your case localhost would be replaced by %, which is a wildcard and used alone allows any IP address or localhost. You may want to use a specific IP, localhost or a wildcard for a part such as '192.168.1.%')
After executing those 2 lines, try again.
I created user 'restriceduser' on my mysql server that is 'locked down'. The mysql.user table has a N for all priveledges for that account. The mysql.db table has Y for only Select, Insert, Update, Delete, Create, Drop; all other privileges are N for that account. I tried to create a stored procedure and then grant him access to run only that procedure, no others, but it does not work.
The user receives: Error: execute command denied to user 'restricteduser'#'%' for routine 'mydb.functionname'
The stored procedure:
CREATE DEFINER = 'restriceduser'#'%' FUNCTION `functionname`(sIn MEDIUMTEXT, sformat MEDIUMTEXT)
RETURNS int(11)
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
....
END;
The grant statement I tried:
GRANT EXECUTE ON PROCEDURE mydb.functionname TO 'restricteduser'#'%';
I was able to work around by modifying his mysql.db entry with
update mysql.db set execute_priv='Y' where user='restricteduser'
This seems to be more then I want, because it opens up permissions for him to run any stored procedure in that database, while I only wanted him to have permissions to run the designated function.
Does anyone see where my issue may lie?
The user table for the restricted user that you have created
will need execute_priv = 'Y'. This grant supersedes the db grant.