【问题标题】:Protect Stored Procedure保护存储过程
【发布时间】:2010-11-12 16:57:58
【问题描述】:

我想知道是否有办法对某些用户隐藏存储过程的文本。

我正在使用带有 Visual Studio 2008 SP1 的 MySQL 5.1.48 和 Net Connector 6.2.3。

我在 MySQL 中有两个用户,一个是 root(具有管理 porpuses 的所有权限),另一个是 admin(具有特定模式中的 select、update、insert、delete、create_temp_table、lock_table 和执行权限)。

所有的存储过程都是由root用户创建的,所以admin用户只能执行它们而不知道它们是如何工作的或阅读它们的内容。

在我们开始创建 DataSet 和使用 WebService 之前一切正常,为此管理员用户需要 mysql 表中的 proc 列中的 select 权限。问题是有了这个权限,管理员可以看到所有的存储过程,包括它们的内容。

请让我知道是否有保护 SP 的方法,我不知道最新版本的 MySQL 和它的 .Net 连接器是否已修复此问题。

【问题讨论】:

  • 什么是“DataSets”和“WebService”,为什么需要访问“mysql”数据库?读起来有点像“我把家里的钥匙给了我的朋友,我怎样才能阻止他进入我的房子?”
  • 读起来很好——你只需要知道他在说什么。

标签: mysql stored-procedures privileges


【解决方案1】:

通常在将 MySQL .NET 连接器与存储过程结合使用时,您必须授予“应用程序用户”对 mysql.proc 的选择权限,否则连接器无法看到您尝试通过它调用的存储过程。

因此,您的应用用户通常会获得以下两项授权:

grant execute on foo_db.* to foo_dbo@localhost identified by 'pass';
grant select on mysql.proc to foo_dbo@localhost;

正如您正确指出的那样,这是一个安全问题,但我确实知道一种解决方法,这可能会让您感到害怕,但是,它工作得非常好,并且如果不比标准方法性能更好,它实际上是性能相同的(想想- 更少的开销)。

所以在撤销 mysql.proc 上的授权选择并刷新权限后...

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 !!

希望这会有所帮助:)

编辑:由于我的回答引起了一些混乱,我想我会在我看到的问题上添加一个完整的演练......

创建演示数据库

只是一个包含两个用户 demo_dbo 和 demo_usr 的快速演示数据库 (demo_db)。

demo_dbo 被授予对 demo_db 的完全权限 - 他们是数据库所有者 (dbo),可以在该数据库中做任何他们想做的事情,例如创建、删除、创建过程、截断等...

demo_usr 是我们的应用程序用户 - 他们只被授予执行权限,所以他们所能做的就是调用存储的过程;

call list_users();

所以我以 root 身份登录并运行此脚本 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;

创建 demo_db 架构

好的,现在我有一个可以玩的数据库,所以接下来我以 demo_dbo 身份登录并开始创建我的模式对象:表、触发器、存储过程、视图等...(不要忘记 demo_dbo 是所有者,可以这样做他们喜欢里面的 demo_db)

所以我以 demo_dbo 身份登录并运行此脚本 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 ; 

好的,这就是创建的 demo_db 架构(1 个表和 1 个 sproc),所以我最好在仍然以 demo_dbo 身份登录时进行一些测试。

从表格中选择 - PASS

select * from users;
user_id
=======
1
2
3
4
5
6

调用存储过程 - PASS

call list_users();
user_id
=======
1
2
3
4
5
6

显示创建过程 - 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

好吧,一切看起来都很棒 demo_dbo(所有者)可以在 demo_db 中完成他们应该能够做的所有事情,所以现在我们最好测试一下我们的 demo_usr 可以做什么。 p>

我以 demo_usr 身份登录并运行 demo_usr.sql 脚本:

显示表格 - 失败

show tables;

Tables_in_demo_db
=================
NULL

没什么可看的 - demo_usr 多么令人失望,他们甚至看不到数据库中有哪些表。

从用户表中选择 - 失败

select * from users;
SELECT command denied to user 'demo_usr'@'localhost' for table 'users'

正如预期的那样,他们无法直接访问表,所以我猜他们获取数据的唯一方法是通过我的存储过程 API。

调用存储过程 - PASS

call list_users();
user_id
=======
1
2
3
4
5
6

终于有结果了,不过我们最好看看demo_usr能看到什么关于这个sproc的信息……

显示创建过程 - 失败

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

demo_usr 可以看到存储过程存在(他们毕竟可以调用它)但看不到主体。

演示应用程序

所以我打开了这个又快又脏使用 MySQL .NET 连接器的 C# 应用程序(顺便说一句,这很棒)并尝试调用 list_users() 存储过程并以应用程序用户的身份填充数据表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();
        }
    }
}

好吧,这失败了 - 为什么?这是异常消息:

oops - SELECT command denied to user 'demo_usr'@'localhost' for table 'proc'

请记住,我们只为 demo_db 授予了 demo_usr 的执行权限 - 没有别的!

现在我们可以通过授予他们对 mysql.proc 的选择权限来解决此问题,但如果我们这样做,他们将能够看到存储过程主体 - 这是我们想要避免的。

那么我们如何解决这个问题呢?那么我们可以以 demo_usr 的身份登录 mysql 控制台并从命令行调用存储过程,所以也许可以在应用程序代码中做同样的事情?

mysql> use demo_db;
Database changed
mysql> call list_users();
+---------+
| user_id |
+---------+
|       1 |
|       2 |
|       3 |
|       4 |
|       5 |
|       6 |
+---------+

所以换行:

 adr.SelectCommand.CommandType = CommandType.StoredProcedure;

adr.SelectCommand.CommandType = CommandType.Text;

我们现在得到...

user_id = 1
user_id = 2
user_id = 3
user_id = 4
user_id = 5
user_id = 6

Press any key to quit.

太棒了——它有效。

希望对你有帮助

Rgds f00

【讨论】:

  • @f00 - 问题,如果用户 admin 可以从 proc 中选择,是否意味着他可以做到 show create producedure xxx
  • 显示创建过程需要在 mysql.proc 上进行授权选择,因此如果管理员有此授权,则可以。
  • 非常感谢你们的帮助...我对.Net不太了解,但我会尝试使用这段代码,它也可以在DataSet中工作吗?
  • @foo - 在这种情况下,程序是可见的,@AndreKR 已经指出了这一点
  • 我在我的答案中添加了一两节来澄清。
【解决方案2】:

您可以使用GRANT 将权限限制为您的存储过程。

也许你可以使用类似的东西 -

GRANT EXECUTE ON PROCEDURE db.storedproc TO 'username'@'somehost';

您可以找到 GRANT here 的完整语法

编辑 -

GRANT 是提供权限,而 REVOKE 则相反。您可以找到有关 REVOKE here 的更多信息。

你可以尝试使用 -

FLUSH PRIVILEGES; 

这会从 mysql 数据库的授权表中重新加载权限。

如果用户已经拥有执行权限,而你想撤销该用户的所有权限,那么你可以使用 -

REVOKE ALL PRIVILEGES ON db.* FROM username@somehost

要撤销用户对特定存储过程的执行授权,您可以使用

REVOKE EXECUTE ON PROCEDURE db.storedproc FROM username;  

始终确保您不会不必要地授予全局权限。

【讨论】:

  • 但是...用户已经拥有架构中所有存储过程的执行权限。我刚刚尝试过,但用户仍然可以访问存储过程的内容。
  • 我认为他试图阻止 SHOW CREATE 在他的存储过程上,而不是执行。
猜你喜欢
  • 1970-01-01
  • 2016-07-07
  • 1970-01-01
  • 2015-08-05
  • 2015-03-08
  • 2011-05-25
  • 2014-03-13
  • 1970-01-01
  • 2011-04-01
相关资源
最近更新 更多