第12讲:将安全隐患扼杀在摇篮之中——用Microsoft .NET来保护数据和应用程序的安全
2005.3.22 欧岩亮
课程介绍
如何应用.NET Framework中的特性来保护代码的安全
基础内容
熟悉.NET开发
课程安排
身份验证
授权
加密
强命名程序集
代码访问安全
中间层安全
如何避免SQL注入
身份验证
使用Credential来唯一标明一个用户
可以使用Microsoft Windows的集成身份验证,使用用户登录Windows时的用户凭证
编写自己的用户身份验证的程序,应用程序来管理用户凭证
System.Security.IIdentity接口和System.Security.GenericIdentity类
System.Security.Principal.WindowsIdentity.GetCurrent().Name
演示一
Authentication
这里的密码是1234的MD5加密密文,NorthwindCredentials的构造函数传入了用户名以及用户的三个角色。另一个用户fred只有销售权限。
下面是NorthwindCredentials类
构造函数
Identity就是一个用户的标识,roles是用户的角色数组。
登录窗体
如果是登录验证成功,那么将当前执行线程的CurrentPrincipal用户凭证,置成了WebService返回的creds对象。
Authorization
使用基于角色的安全
可以通过编程实现基于角色的授权
角色可以代表商业流程中的工作职责,例如:秘书、经理、管理员、总监等
用角色的方式来管理用户会更加简便
如何使用基于角色的安全
PrincipalPermissionAttribute和SecurityAction.Demand
在类或者方法上加上PrincipalPermission就表示要访问类或者方法需要通过身份验证。Authenticated为true表示必须要通过身份验证,Role表示用户需要访问所必须具有的角色。.NET在运行的时候就会去查看当前运行线程的CurrentPrincipal,判断用户是不是有相应的权限。
IPrincipal.isInRole()
判断当前的用户是不是有某一个角色。Context.User是Asp.net里面来访问当前使用应用程序用户的Class。这个例子通过判断当前用户所具有的角色来赋予界面元素不同的属性。
演示二
Authorization
SecurityAction.Demand表示需要具有某种东西,Role表示需要具有Manager的角色权限。这里Manager是个字符串常量,
如果不具有相应权限想访问这个类,就会抛出异常。
这样做还是会有一些弊端,如果我们想通过数据库来规定用户需要哪些角色能访问,就不能通过这种标签的方式。这种标签加权限限制访问的方式只能够用硬代码规定角色的常量字符串名称。
所以.NET为我们提供了另外一种方式。
在构造函数中,我们可以实例化一个PrincipalPermission,将角色通过字符串方式传入,而不同在类上面加标签。如果frmProducts类不使用,那么就不会得到此类的任何方法;如果要使用,必须通过构造函数中的权限验证。
为了更好的用户体验,我们把界面元素的属性也根据权限来设置可见与不可见。
加密
加密将字节打乱
对称加密与非对称加密
对称加密适用一个相同的密钥进行加密/解密
非对称加密使用一个密钥对进行加密/解密,加密与解密的密钥是不同的(公钥/私钥对)
非对称加密算法更加安全
对称加密算法更加高效
System.Security.Cryptography名称空间
如何进行密钥管理
演示三
加密
加密字节序列,我们使用了.NET内建的一种加密方法,DESCryptoServiceProvider。
解密方法
CryptoStream实际上是对MemoryStream的封装,当我们对cs进行写操作的时候,会把解密后的数据写入MemoryStream。
这里加密后的数据要进行一个Base64的转换,因为网络上传输的时候不能直接传输二进制,而一般使用xml进行传输,xml里面仍然不能写入二进制数据,但我们可以通过把二进制数据转换为Base64的编码形式,而Base64的字符串是可以存储在xml文件中的。当xml数据文件传输到对方之后,对方再用逆向的方法把Base64的String转换为二进制,再进行解密。
运行结果
我们的配置文件中可以把连接字符串加密存入配置文件,然后使用的时候解密,这样能提高程序的安全性。
在获取连接字符串时用同样的密钥"NorthwindKey"进行解密
但是我们也最好不要把密钥和解密算法放在程序中,因为可以通过反编译看出加密规则。我们可以通过把密钥放在注册表中,或者通过加密狗等程序来提高安全性。
Code Access Security
最低权限的策略
让所有用户具有最低权限。
Code Group——按照逻辑分类的代码组合
Code Group可以按照多种方式划分URL,string name,zone,etc
Permission Sets——许可集,用来定义代码能够访问的资源:文件I/O,Isolated Storage独立存储,SQL客户端,等等。
Link demands
演示四
代码访问安全
我们可以用这样的代码来保证我们的应用程序在运行的时候一定要具有某些权限才可以运行。应用程序本身可以检测当前Windows操作系统上的.NET框架配置上面是否允许运行。例如,WebPermission表示要求计算机要能访问Web。IsolatedStorage标签表示需要有能够访问独立存储的功能。
Refused set表示要拒绝的功能。
进入cmd,在当前工程的路径,我们可以通过permview命令来观看当前应用程序集需要什么样的权限。
我们也可以在配置工具中的Code Group来划分权限,以及设置PermissionSet
强命名应用程序集
使用sn.exe工具来创建强命名公钥/私钥对,并存储在文件当中:
sn.exe –k Northwind.snk
sn是strong name的缩写,我们可以用-k的方式,后面带密钥文件名,生成一个公钥/私钥对。编译器的签名工具会利用私钥对程序集加密,生成加密摘要,放在Manifest中,并在Manifest中存放公钥,在运行程序集时,会拿到公钥信息,验证签名是否有效。
AssemblyKeyFileAttribute
可以在AssemblyInfo中的这个标签来标记密钥文件的路径
优势
可以被装在到GAC当中
Side-by-side部署,支持多个版本的应用程序集
在编译时,.NET的客户端代码使用了强命名应用程序集,在运行时能够有效地防止装入“木马”应用程序集。其他人虽然可以反编译或者修改我们的代码,但是他们不具有我们的私钥文件,在使用dll的时候,程序会检测dll是否具有强命名,如果不具有,程序是不会理会的。
Delayed Signing
私钥保密性,确保开发团队中的多个组建使用相同的强命名
如果我们的开发团队很庞大,我们如果想让团队的人对项目进行强命名,我们就需要把密钥文件给他,这就相当于把私钥给他了。这种方式是很危险的,我们不能保证每个开发人员都是效忠的。我们可以使用下面的方法。
只导出公钥
我们让所有程序员在开发的时候,只写上只包含公钥的snk地址,然后我们加入标签AssemblyDelaySign(True),这样在真正用私钥加密之后的签名不会在每一次生成都放到dll中。
Sn -Vr<assemblyname>:关闭对只拥有公钥的强命名应用程序集进行验证
Sn –R<assemblyname><keyfilename>:在发布之前对应用程序集进行缓签名
在发布之前才对所有程序进行真正的签名
Link Demands
连接请求发生在外部的代码对本应用程序集进行调用的时候
System.Security.Permissions名称空间
例如:StrongNameIdentityPermission属性
从某个应用程序集中提取公钥标示(一个十六进制的字符序列)
sn –Tp NorthwindModel.dll
例如:
这个标签要求别的应用程序集在装载我们程序集时,必须具有这种公钥才可以,如果不是这种证书签名的程序,将不会被允许连接到我们程序集内部。
例如如果我们想查看某个强命名的程序集公钥的Token。
我们只要把这个标示填充到标签中的PublicKey属性即可。
Middle Tier安全
最现成的方法,COM+是最安全的解决方案——与Windows整合并支持配置
Web Service的安全可以通过HTTPS和IIS安全来控制
当不能使用IIS安全的时候,可以使用WS-Security(Web Service Enhancements的一部分)来保证平台之间的Web Service安全
Remoting安全
IIS宿主和TCP,使用IIS安全和HTTPS
其他宿主,需要实现定制的Channel或Sink
演示五
中间层安全
IIS安全,上图是默认的匿名的验证,下图是Basic验证
其中Cache添加的时候注明了是使用Basic验证。这种验证会将用户名和密码以明文的方式传输到IIS上面,所以我们在使用Basic验证时应该最好使用Https来保证传输安全。
SQL注入
SQL注入这种威胁发生在动态生成SQL查询语句的时候,动态生成的SQL语句可能会被篡改
例如:
如果在上面的SQL语句中加入'OR 1=1'这个条件,users表中的所有数据将作为结果返回
所有使用动态SQL查询的应用程序都有可能受到这种威胁
为了防御这种攻击,去掉所有的动态SQL查询:使用ADO.NET中的SqlParameters
演示六
防止SQL注入攻击
上面是危险的方式,下面是安全的方式
注入攻击
如果是第一种方式的代码,那么登录始终会成功,但如果是用第二种方式,登陆就会失败。
安全设计目标
最小化攻击范围
分析具体的攻击并阻止它们:
拒绝服务攻击
基于文件或目录的攻击
SQL注入
引诱攻击(Luring)
防患于未然,提早预知新的攻击
使用经过验证的安全技术:身份验证,给予角色的授权,HTTPS,加密
总结
.NET包括了强大的内建的安全特性
具体问题具体分析,根据情况来选择安全技术,编写安全的基于Windows Forms的代码
不需要绞尽脑汁去发明新的安全代码
2010.10.20