Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3161186
  • 博文数量: 797
  • 博客积分: 10134
  • 博客等级: 上将
  • 技术积分: 9335
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-22 22:57
个人简介

1

文章分类

全部博文(797)

文章存档

2022年(1)

2021年(2)

2017年(2)

2016年(1)

2015年(4)

2014年(1)

2013年(6)

2012年(6)

2011年(10)

2010年(26)

2009年(63)

2008年(61)

2007年(51)

2006年(563)

我的朋友

分类: 网络与安全

2006-08-07 23:22:19

很多 web 开发者没有注意到 SQL 查询是可以被篡改的,因而把 SQL 查询当作可信任的命令。殊不知道,SQL 查询可以绕开访问控制,从而绕过身份验证和权限检查。更有甚者,有可能通过 SQL 查询去运行主机操作系统级的命令。

直接 SQL 命令注入就是攻击者常用的一种创建或修改已有 SQL 语句的技术,从而达到取得隐藏数据,或覆盖关键的值,甚至执行数据库主机操作系统命令的目的。这是通过应用程序取得用户输入并与静态参数组合成 SQL 查询来实现的。下面将会给出一些真实的例子。

由于在缺乏对输入的数据进行验证,并且使用了超级用户或其它有权创建新用户的数据库帐号来连接,攻击者可以在数据库中新建一个超级用户。

例子 27-2. 一段实现数据分页显示的代码……也可以被用作创建一个超级用户(PostgreSQL系统)。


$offset
= $argv[0];
// 注意,没有输入验证!
$query  = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;"
;
$result = pg_query($conn, $query
);

?>
一般的用户会点击 $offset 已被斌值的“上一页”、“下一页”的链接。原本代码只会认为 $offset 是一个数值。然而,如果有人尝试把以下语句先经过 处理,然后加入URL中的话:

0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
    select 'crack', usesysid, 't','t','crack'
    from pg_shadow where usename='postgres';
--

那么他就可以创建一个超级用户了。注意那个 0; 只不过是为了提供一个正确的偏移量以便补充完整原来的查询,使它不要出错而已。

注: -- 是 SQL 的注释标记,一般可以使用来它告诉 SQL 解释器忽略后面的语句。

对显示搜索结果的页面下手是一个能得到密码的可行办法。攻击者所要做的只不过是找出哪些提交上去的变量是用于 SQL 语句并且处理不当的。而这类的变量通常都被用于 SELECT 查询中的条件语句,如 WHERE, ORDER BY, LIMIT 和 OFFSET。如果数据库支持 UNION 构造的话,攻击者还可能会把一个完整的 SQL 查询附加到原来的语句上以便从任意数据表中得到密码。因此,对密码字段加密是很重要的。

例子 27-3. 显示文章……以及一些密码(任何数据库系统)


$query 
=
"SELECT id, name, inserted, size FROM products
                 WHERE size = '$size'
                 ORDER BY $order LIMIT $limit, $offset;"
;
$result = odbc_exec($conn, $query
);

?>
可以在原来的查询的基础上添加另一个 SELECT 查询来获得密码:

'
union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable;
--

假如上述语句(使用 ' 和 --)被加入到 $query 中的任意一个变量的话,那么就麻烦了。

SQL 中的 UPDATE 也会受到攻击。这种查询也可能像上面的例子那样被插入或附加上另一个完整的请求。但是攻击者更愿意对 SET 子句下手,这样他们就可以更改数据表中的一些数据。这种情况下必须要知道数据库的结构才能修改查询成功进行。可以通过表单上的变量名对字段进行猜测,或者进行暴力破解。对于存放用户名和密码的字段,命名的方法并不多。

例子 27-4. 从重设密码……到获得更多权限(任何数据库系统)

$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
但是恶意的用户会把 ' or uid like'%admin%'; -- 作为变量的值提交给 $uid 来改变 admin 的密码,或者把 $pwd 的值提交为 "hehehe', admin='yes', trusted=100 "(后面有个空格)去获得更多的权限。这样做的话,查询语句实际上就变成了:


// $uid == ' or uid like'%admin%'; --
$query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --"
;

// $pwd == "hehehe', admin='yes', trusted=100 "
$query =
"UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE
...;"
;

?>

下面这个可怕的例子将会演示如何在某些数据库上执行系统命令。

例子 27-5. 攻击数据库所在主机的操作系统(MSSQL Server)


$query 
= "SELECT * FROM products WHERE id LIKE '%$prod%'"
;
$result = mssql_query($query
);

?>
如果攻击提交 a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- 作为变量 $prod的值,那么 $query 将会变成


$query 
=
"SELECT * FROM products
                   WHERE id LIKE '%a%'
                   exec master..xp_cmdshell 'net user test testpass /ADD'--"
;
$result = mssql_query($query
);

?>

MSSQL 服务器会执行这条 SQL 语句,包括它后面那个用于向系统添加用户的命令。如果这个程序是以 sa 运行而 MSSQLSERVER 服务又有足够的权限的话,攻击者就可以获得一个系统帐号来访问主机了。

注: 虽然以上的例子是针对某一特定的数据库系统的,但是这并不代表不能对其它数据库系统实施类似的攻击。使用不同的方法,各种数据库都有可能遭殃。

也许有人会自我安慰,说攻击者要知道数据库结构的信息才能实施上面的攻击。没错,确实如此。但没人能保证攻击者一定得不到这些信息,一但他们得到了,数据库有泄露的危险。如果你在用开放源代码的软件包来访问数据库,比如论坛程序,攻击者就很容得到到相关的代码。如果这些代码设计不良的话,风险就更大了。

这些攻击总是建立在发掘安全意识不强的代码上的。所以,永远不要信任外界输入的数据,特别是来自于客户端的,包括选择框、表单隐藏域和 cookie。就如上面的第一个例子那样,就算是正常的查询也有可能造成灾难。

  • 永远不要使用超级用户或所有者帐号去连接数据库。要用权限被严格限制的帐号。

  • 检查输入的数据是否具有所期望的数据格式。PHP 有很多可以用于检查输入的函数,从简单的和(比如 ,)到复杂的 都可以完成这个工作。

  • 如果程序等待输入一个数字,可以考虑使用 来检查,或者直接使用 来转换它的类型,也可以用 把它格式化为数字。

    例子 27-6. 一个实现分页更安全的方法


    settype
    ($offset, 'integer'
    );
    $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;"
    ;

    // 请注意格式字符串中的 %d,如果用 %s 就毫无意义了
    $query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;"
    ,
                    
    $offset
    );

    ?>

  • 使用数据库特定的敏感字符转义函数(比如 和 sql_escape_string())把用户提交上来的非数字数据进行转义。如果数据库没有专门的敏感字符转义功能的话 和 可以代替完成这个工作。看看第一个例子,此例显示仅在查询的静态部分加上引号是不够的,查询很容易被攻破。

  • 要不择手段避免显示出任何有关数据库的信心,尤其是数据库结构。参见错误报告和。

  • 也可以选择使用数据库的存储过程和预定义指针等特性来抽象数库访问,使用户不能直接访问数据表和视图。但这个办法又有别的影响。

除此之外,在允许的情况下,使用代码或数据库系统保存查询日志也是一个好办法。显然,日志并不能防止任何攻击,但利用它可以跟踪到哪个程序曾经被尝试攻击过。日志本身没用,要查阅其中包含的信息才行。毕竟,更多的信息总比没有要好。

 

---补充---

好长时间没有维护PGSQL了,有点忘。
你可以完全不用PGSQL的外部命令,就将pg_shandow当成一个普通的数据库,然后就可以用插入命令来完成对该库的操作:
template1=# select * from pg_shadow;先看看pg_shadow里有什么
template1=# update pg_shadow set usecreatedb = 'f' where usename = 'db_username' ;
UPDATE 1
template1=# select * from pg_shadow;再看看是不是已经将usecreatedb的创建数据库的属性改成false了?
当然也可以改成true即't'。
提醒一点,在LINUX下习惯从root不要密码转到postgres下,不要设postgres的系统用户,比较安全。

最近发现PostgreSQL7.4.X版本的用户密码采用了MD5,在pg_shadow中和MySQL一样不再明码显示用户密码,所以必须使用如下SQL命令添加用户并指定其属性:
CREATE USER username WITH PASSWORD 'PASSWORD' CREATEDB CREATEUSER IN GROUP 'group';
如果要修改用户权限则:
ALTER USER username WITH ...

 

pg_shadow说明:

视图 pg_shadow 存在是为了向下兼容: 它模拟了一个 PostgreSQL 版本 8.1 之前的系统表。 它显示了所有标记了 rolcanlogin 的角色的属性。

这个系统表的名字来自于该表不能被公众可读,因为它包含口令。 是一个在 pg_shadow 上公开可读的视图,只是把口令域填成了空白。

Table 42-42. pg_shadow 字段

名字 类型 引用 描述
usename name .rolname 用户名
usesysid oid .oid 用户的 ID
usecreatedb bool   用户可以创建数据库
usesuper bool   用户是超级用户
usecatupd bool   用户可以更新系统表。(即使超级用户,如果这个字段不是真,也不能更新系统表。)
passwd text   口令(可能是加密的)
valuntil abstime   口令失效的时间(只用于口令认证)
useconfig text[]   运行时配置变量的会话缺省
阅读(678) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~