SQL SERVER 2008安全配置

0x00 sql server 2008 权限介绍


在访问sql server 2008的过程中,大致验证流程如下图:

2014050520343187669.png

当登录操作一个数据库的时候,会经过三次验证:

1. 操作系统的验证
2. SQL SERVER登录名的验证
3. 数据库用户名的验证

当使用windows身份认证模式的时候,使用的windows账号会通过操作系统的验证,然后以sysadmin的服务器角色通过SQL SERVER 的验证,在访问一个具体的数据库的时候又以其映射的dbo用户名登录相应的数据库。

当使用SQL SERVER 身份认证模式的时候,首先会使用类似于IIS中的匿名账号来通过windows验证,然后以其对应的服务器角色来通过SQL SERVER 的验证,在访问一个具体数据库的时候以其映射的数据库用户名来登录相应的数据库。

操作系统层面:在windows方式验证的过程中,由所使用的windows账号。使用sql账号验证的时候,应该是使用了一个类似于iis中的匿名账号来通过windows验证的。

SQL SERVER 层面:由sql server的登录用户的权限来控制,具体的权限由对应的服务器角色来决定。

(SA是SQL SERVER的一个登录用户名,而不是数据库的用户名)

数据库层面:由数据库的用户名的权限来控制,具体可以通过选择不同的数据库角色或者自定义权限来实现。

(SQL SERVER的登录用户名SA通过映射数据库的用户名来访问数据库内容 )

所以在SQL SERVER 中,对权限起决定性作用的就是服务器登录名角色和数据库用户名角色。

0x01 sql server 2008 服务器角色及数据库角色


固定服务器角色

(登录SQL SERVER登录用户的权限角色,如SA默认拥有sysadmin角色权限)

Dbcreator:这个服务器角色的成员可以创建、更改、删除和还原任何数据库。
Diskadmin:这个服务器角色用于管理磁盘文件,比如镜像数据库和添加备份设备。Processadmin:SQL Server 2008能够多任务化,也就是说可以通过执行多个进程做多个事件。
Securityadmin:这个服务器角色的成员将管理登录名及其属性。他们可以授权、拒绝和撤销服务器级权限。也可以授权、拒绝和撤销数据库级权限。另外,它们可以重置SQL Server 2008登录名的密码。
Serveradmin:这个服务器角色的成员可以更改服务器范围的配置选项和关闭服务器。Setupadmin:为需要管理链接服务器和控制启动的存储过程的用户而设计。这个角色的成员能添加到setupadmin,能增加、删除和配置链接服务器,并能控制启动过程。
Sysadmin:这个服务器角色的成员有权在SQL Server 2008中执行任何任务。
Public: 有两大特点,第一,初始状态时没有权限;第二,所有的数据库用户都是它的成员。
固定数据库角色:
(数据库用户权限)
微软提供了9个内置的角色,以便于在数据库级别授予用户特殊的权限集合。
db_owner: 该角色的用户可以在数据库中执行任何操作。
db_accessadmin: 该角色的成员可以从数据库中增加或者删除用户。
db_backupopperator: 该角色的成员允许备份数据库。
db_datareader: 该角色的成员允许从任何表读取任何数据。
db_datawriter: 该角色的成员允许往任何表写入数据。
db_ddladmin:该角色的成员允许在数据库中增加、修改或者删除任何对象(即可以执行任何DDL语句)。
db_denydatareader: 该角色的成员被拒绝查看数据库中的任何数据,但是他们仍然可以通过存储过程来查看。
db_denydatawriter: 像db_denydatareader角色,该角色的成员被拒绝修改数据库中的任何数据,但是他们仍然可以通过存储过程来修改。
db_securityadmin: 该角色的成员可以更改数据库中的权限和角色。
public:在SQL Server 2008中每个数据库用户都属于public数据库角色。当尚未对某个用户授予或者拒绝对安全对象的特定权限时,这该用户将据称授予该安全对象的public角色的权限,这个数据库角色不能被删除。

0x02 安全配置方案


1. 限制可以访问数据库的IP

一般可以通过安全策略里面的ipsec或者windows防火墙来限制。

2. 修改数据库的访问端口

可以在SQL SERVER 配置管理器修改访问端口为不常见端口来防止一般性扫描

3. 修改日志审核策略

将登录审核修改为“开启登录成功和失败的日志”,这样方便数据库被入侵之后,对日志的查看。

4. 对SA账户的处理

如使用混合身份验证模式,建议禁用掉SA账户,否则设置非常强的SA密码。

5. 网站使用的数据库账号

对于每一个网站新建一个登陆用户并关联一个数据库用户,设置强密码,给予服务器角色为:PUBLIC,给予对应的数据库角色为:db_owner(需更加安全的保护对应数据库的数据需要调节更加严格的权限,此处给予的为此数据库的最高权限,若此账号泄露,此数据库中的数据不保,配合后续配置不影响服务器安全)

6. 数据库服务运行账号

数据库需要往磁盘写文件,或者通过存储过程执行命令的权限由数据库服务器的运行账户权限决定,使用NETWORK SERVICE账户来运行数据库服务器是较安全的。

7. 磁盘权限的合理配置

合理配置数据库运行,账户在磁盘上的权限,一定不要随意给写权限,特别是网站目录及数据库存储目录以外的位置,这样即使是SA也不能往磁盘写文件。

8. 危险存储过程的处理

由于在sql server 2008中sp_dropextendedproc 不能删除系统扩展存储过程,所以直接禁用常见危险存储过程。具体可根据实际情况来操作,因为有些存储过程是正常需要的。对于一般账户而言,根本没有执行这些高危存储过程的权限,而对于SA账户来说,以上所有的操作都是可恢复的,个人觉得SA账户被入侵之后的关键控制点在于合理配置数据库服务运行账号的权限。

0x03 常见入侵分析及防御


1. 数据库0day

zhangsan认为,升级吧!

2. 网站被入侵获取到普通服务器权限的账号

如果网站被入侵(类似SQL注入),很显然其对应的账号的数据库的数据肯定是不保了。普通账号(只给PUBLIC的账户)是无法执行系统命令的,可能的方式是通过备份数据库来达到写马的操作,防御的最好方法是严格控制数据库运行账号对磁盘的写权限

3. SA账户被入侵

SA账户被入侵后,一般会通过开启xp_cmdshell存储过程来执行系统命令。但是如果配置的数据库服务运行账号NETWORK SERVICE,它能够执行的系统命令也就有限了,甚至可以取消NETWORK SERVICE对于cmd.exe的执行权限。还有就是需要合理配置NETWORK SERVICE的磁盘写入权限。

0x04 常见操作介绍


1. 开启xp_cmdshell

EXEC sp_configure 'show advanced options', 1;
go
RECONFIGURE;
GO
EXEC sp_configure 'xp_cmdshell', 1;
go
RECONFIGURE;
GO

2. 使用xp_cmdshell执行系统命令的操作

exec xp_cmdshell 'whoami'

(如果该存储过程可以执行说明可能已经被入侵)

3. 高危存储过程处理

常见危险存储过程:

xp_cmdshell
xp_delete_file
xp_regread
xp_regwrite
xp_dirtree

由于在sql server 2008中sp_dropextendedproc 不会删除系统扩展存储过程,故直接禁用即可

EXEC sp_configure 'show advanced options', 0;
EXEC sp_configure 'xp_cmdshell', 0; 

求指正,求指教!

©乌云知识库版权所有 未经许可 禁止转载


30
破影 2014-11-19 13:34:38

一般应用程序中的账号是可精确到表、存储过程,数据库本身的进程就比较模糊。分发清除、代理的权限如何精确设置,这些哪里有介绍吗?

30
Lee Swagger 2014-05-16 00:21:45

你删除了 难道不能从web端上传?另外 odsole70.dll呢,
HEX直接上传.
降权 精确到表 存储过程

30
破影 2014-05-14 08:22:25

最彻底的方式是重命名或删除xplog70.dll,但在测试中发现SQL代理需要使用xplog70.dll,分发清除(复制订阅)需要调用sys.xp_cmdshell。不知道各位大牛是如何解决的?

30
zhangsan 2014-05-11 13:19:32

谢谢指教!
不过问题二我还是更能理解我的想法。(无所谓对与错的问题,这个也不是重点,我只是从原理上这样理解分析这个问题。)
问题三四,我觉得还得根据现实情况处理。至少我们公司没办法做到那么细。比较甲方安全能不能推行还得看老板有多重视。但是按我推荐的做法至少可以防止90%的攻击了。。我们能做的,需要做的也只是相对安全。也没存在所谓的绝对安全。

其实我写这篇paper是为我们公司写的,不涵盖所有情况。

30
zcgonvh 2014-05-11 12:13:26

问题二:这里没有任何模拟其他账户,进行文件等其他操作时候就是数据库服务进程的权限,而iis匿名账户是用来做windows权限隔离的,这两个没有可比性。
匿名账户还一个作用就是“在没有通过认证时返回一些有意义的数据”,这里明显是必须要认证才可以。
打个比方:你监听一个端口,如果传入的是abc就返回abc.txt,否则返回错误。判断是否是abc就好比登录过程,读取文件就好比查询,这里没有任何模拟登录之类,所以也无所谓匿名认证。
什么是匿名认证:你监听一个端口,如果传入的是abc就返回abc.txt,否则返回guest.txt,两个返回的都是有意义的东西,所以这个是匿名认证。
当然,你用这种方式理解数据库本身的文件操作也无可厚非,但这些一般是必须同时透明的,必须代表没有这些权限会直接报错,透明是因为这是操作系统内部的操作,关心这个完全没必要。

问题三四:这点事都办不好,DBA和安全工程师是干什么吃的?配置数据库安全本身就是DBA和安全工程师的事,别人想配置,能接触到数据库服务器么?要是这么乱用权限的话,配置跟没配置基本没区别了,管理混乱早晚会出问题。
network service 很难执行高危命令:爆破administrator密码并以其身份执行程序,或者关闭某些同属network service的进程导致拒绝服务,严重不严重?不是不能执行命令就安全了,为什么sqlserver、iis7+都分配了新的虚拟服务账户?原因就是这个。不同的服务进程账户必须隔离开。

30
zhangsan 2014-05-11 10:25:36

关于问题1:“当使用windows身份认证模式的时候,使用的windows账号会通过操作系统的验证,然后以sysadmin的服务器角色通过SQL SERVER 的验证。”

解答:其实不管对于windows认证还是sql server认证,在登录到数据库服务器之后就没有啥区别了。我讲的是默认情况下,window是的administrator拥有sysaadmin权限,就像sa默认拥有sysadmin权限一样。可能表述不同而已,完全认同你说的这一点。

问题二:“当使用SQL SERVER 身份认证模式的时候,首先会使用类似于IIS中的匿名账号来通过windows验证”
"使用sql账号验证的时候,应该是使用了一个类似于iis中的匿名账号来通过windows验证的。"
错,sqlserver从来就不存在匿名帐号,使用sqlserver身份认证也不需要windows认证。

解答:这点我不是很同意你的看法。我个人认为任何服务监听一个端口对外提供服务的时候,其实都存在一个了“类似匿名账户的概念”我说的是类似,因为我们要去操作服务某个文件(只是打个比方说是操作文件)的时候,服务器肯定需要知道你是以什么身份来访问我这个服务或者文件的。这点我没办法证实,但是我觉得这样理解会更容易一些。

问题三:network service账户比普通用户权限还要大一点,有一个特权很好用。而且network service关联的服务太多了。所以还是新建一个运行账户吧,注意组策略中赋予作为服务登录的权限。

解答:这个我也不赞同你的意思,为什么了?我写这篇文章是考虑了实际场景的就是说,在一个大公司,很多业务的情况下,你让别人去新建一个账号,然后给什么什么样的权限,其实是很难的。因为很多人不懂。但是使用network service 默认就会给予比如数据库的数据文件夹写权限,等等。而且network service 很难执行高危命令。我觉得做安全配置,讲究这个“度”很重要。不能过,过了就很难实施了。还有就是我说到会取消network service在cmd,net等文件的执行权限等操作。当然你可以说上传一个,我们安全配置讲究一个纵深防御的理念,别指望一点封死。

问题四:“……普通账号(只给PUBLIC的账户)是无法执行系统命令的,可能的方式是通过备份数据库来达到写马的操作……”
单纯的public数据库角色是不能备份的,能备份的只有dbo和backupoperator。

解答:还是刚刚说的问题,我这里有实际场景。因为我没办法判断在一个大公司,是不是他们都懂配置,所有我给予的数据库角色为db_owner,所以我说它可以进行备份。可能是我没写清楚。

有啥不对的地方还是希望多多指出。谢谢。

30
zcgonvh 2014-05-11 03:05:48

第0x00小节前半部分有一些问题。

“当使用windows身份认证模式的时候,使用的windows账号会通过操作系统的验证,然后以sysadmin的服务器角色通过SQL SERVER 的验证。”
错,sqlserver会根据 安全性->登录名 中的列表,判断用户是否可以登录。使用查询分析器进行windows验证显示为sysadmin是因为安装sqlserver时默认将安装的windows账户加入了登录名列表并授予sysadmin服务器角色。
对于windows身份认证,会先进行最准确匹配,即假如某windows身份属于列表的多个登录名(用户或组)中,以范围最小的登录名为准。
例如:仅属于iis_wpg和guest组的iis匿名帐户不能使用windows验证;任意属于users组的账户均可以使用windows验证;administrator同时与BULITIN\USERS和ZCGONVH\administrator匹配,按照最准确匹配会匹配到ZCGONVH\administrator。

之后根据匹配出的登录名获取对应数据库用户,根据用户数据库角色权限进行操作。如果没有映射的数据库,则会根据服务器角色分配默认的用户。例如users默认情况下仅仅是public服务器角色,分配的数据库用户是guest。新建一个普通用户,用runas执行最后的测试脚本可以看到以下输出:
user:guest
is sa:false
is dbo:false
is public:true

几个细节:
当登录名列表中某windows账户(或组)未指定数据库,则会根据服务器角色分配默认数据库用户。只有两种可能:非sysadmin下分配guest,sysadmin下分配dbo。
当某windows账户在登录名列表中只处于某个windows用户组内,同时这个组赋予了某指定数据库的访问权限,则会生成与windows用户名相同的虚拟用户(不在用户列表内)。例如,为users组勾选master数据库,然后以test用户连接数据库,select user显示的是domain\test。
当某windows账户指定了数据库,则根据指定数据库时输入的用户名决定。

“在访问一个具体的数据库的时候又以其映射的dbo用户名登录相应的数据库。”
错,应该是数据库映射的用户名,不一定是dbo。
sqlserver没有“登录数据库”一说,只有“登录 [数据库服务器] 并 [访问] 指定数据库”,这里应该是判断权限。
用户名就是个记录特定登录名在数据库内权限的标记,从来都不是登录的凭据,也不能用来登录。
可以把数据库当成文件,数据库访问权限(即在用户名分配的权限)当成文件ACL,登录名当成windows用户来理解。

“当使用SQL SERVER 身份认证模式的时候,首先会使用类似于IIS中的匿名账号来通过windows验证”
"使用sql账号验证的时候,应该是使用了一个类似于iis中的匿名账号来通过windows验证的。"
错,sqlserver从来就不存在匿名帐号,使用sqlserver身份认证也不需要windows认证。
iis匿名帐号是为了权限隔离与模拟验证,与sqlserver登录认证没有可比性,别想当然。

权限配置这里的一个问题:

“数据库需要往磁盘写文件,或者通过存储过程执行命令的权限由数据库服务器的运行账户权限决定,使用NETWORK SERVICE账户来运行数据库服务器是较安全的。”
network service账户比普通用户权限还要大一点,有一个特权很好用。而且network service关联的服务太多了。所以还是新建一个运行账户吧,注意组策略中赋予作为服务登录的权限。

常见入侵分析这里的一个问题:

“……普通账号(只给PUBLIC的账户)是无法执行系统命令的,可能的方式是通过备份数据库来达到写马的操作……”
单纯的public数据库角色是不能备份的,能备份的只有dbo和backupoperator。
不知道是不是我理解有误,sqlserver很坑爹,服务器角色有一个叫public,数据库角色也有个public。

测试环境:win7+sqlserver2008 express
windows登录名:
BULITIN\USERS
NT AUTHORITY\SYSTEM
NT SERVICE\MSSQL$SQLEXPRESS
ZCGONVH\administrator

测试脚本:
import System;
import System.Data;
import System.Data.SqlClient

var conn:SqlConnection=new SqlConnection("Server=.\\sqlexpress;Trusted_Connection=True")
try{conn.Open();
print("user:"+new SqlCommand("SELECT user",conn).ExecuteScalar())
print("is sa:"+Boolean(new SqlCommand("SELECT is_srvrolemember('sysadmin')",conn).ExecuteScalar()))
print("is dbo:"+Boolean(new SqlCommand("SELECT is_member('db_owner')",conn).ExecuteScalar()))
print("is public:"+Boolean(new SqlCommand("SELECT is_member('public')",conn).ExecuteScalar()))
conn.Close()}
catch(e){print(e.message);}
编译:jsc xxx.js

感谢知乎授权页面模版