摘要:本文
ASP.NET
应用程序身份验证的概念,介绍了各种身份验证模式并进行了比较,阐述了选择身份验证模式的机制,并给出了一种基于窗体身份验证模式的实现方法。
关键字:身份验证
authentication ASP.NET WEB
应用
1.
身份验证概念
任何成功的应用程序安全策略的基础都是稳固的身份验证和授权手段,以及提供机密数据的保密性和完整性的安全通讯。
身份验证(
authentication
)是一个标识应用程序客户端的过程,这里的客户端可能包括终端用户、服务、进程或计算机,通过了身份验证的客户端被称为主体(
principal
)。身份验证可以跨越应用程序的多个层发生。终端用户起初由
Web
应用程序进行身份验证,通常根据用户名和密码进行;随后终端用户的请求由中间层应用程序服务器和数据库服务器进行处理,这过程中也将进行身份验证以便验证并处理这些请求。
图
1
列出了各种安全技术以及每种技术所提供的主要验证方式。
2.
身份验证模式
如图
1
所示,
Windows 2000
上的
.NET
框架上提供了以下几种身份验证:
·
ASP.NET
身份验证模式
·
Enterprise Services
身份验证
·
SQL Server
身份验证
2.1 ASP.NET
身份验证模式
ASP.NET
身份验证模式包括
Windows
、
Forms
(窗体)、
Passport
(护照)和
None
(无)。
2.1.1
Windows
身份验证
使用这种身份验证模式时,
ASP.NET
依赖于
IIS
对用户进行验证,并创建一个
Windows
访问令牌来表示已通过验证的标识。
IIS
提供以下几种身份验证机制:
·
基本身份验证
·
简要身份验证
·
集成
Windows
身份验证
·
证书身份验证
·
匿名身份验证
2.1.2
护照身份验证
使用这种身份验证模式时,
ASP.NET
使用
Microsoft Passport
的集中式身份验证服务,
ASP.NET
为
Microsoft Passport
软件开发包(
SDK
)所提供的功能提供了一个方便的包装(
Wrapper
)。此
SDK
必须安装在
WEB
服务器上。
2.1.3
窗体身份验证
这种验证方式使用客户端重定向功能,将未通过身份验证的用户转发到特定的登录窗体,要求用户输入其凭据信息(通常是用户名和密码)。这些凭据信息被验证后,系统生成一个身份验证票证(
ticket
)并将其返回客户端。身份验证票证可在用户的会话期间维护用户的身份标识信息,以及用户所属的角色列表(可选)。
2.1.4
None
使用这种身份验证模式,表示你不希望对用户进行验证,或是采用自定义的身份验证协议。
2.2 Enterprise Services
身份验证
Enterprise Services
身份验证通过使用底层的远程过程调用(
RPC
,
Remote Procedure Call
)传输结构来进行,而这种结构又使用了操作系统安全服务提供程序接口(
SSPI
,
Security Service Provider Interface
)。可以利用
Kerberose
或
NTLM
身份验证机制对
Enterprise Services
应用程序的客户端进行验证。
2.3 SQL Server
身份验证
SQL Server
可以通过
Windows
身份验证机制(
Kerberose
或
NTLM
),也可以通过其内置的身份验证方案
-SQL
身份验证机制进行验证。通常有两种可用的验证方案。
2.3.1
SQL Server and Windows
客户端可用通过
SQL Server
身份验证或
Windows
身份验证机制来连接
SQL Server
的某个实例。这种方式有时也被称为混合模式的身份验证。
2.3.2
Windows Only
客户端必须通过使用
Windows
身份验证机制来连接到
SQL Server
的一个实例。
3.
选择身份验证机制
设计分布式应用程序的身份验证是一项具有挑战性的任务。在应用程序开发的早期阶段,进行适当的身份验证设计有助于降低许多安全风险。
3.1
各种身份验证机制的比较
|
用户是否需要在服务器域中拥有
Windows
帐户
|
是否支持委托
|
是否需要
Windows 2000
客户端和服务器
|
凭据是否明文传输(需要
SSL
)
|
是否支持非
IE
浏览器
|
基本身份验证
|
是
|
是
|
否
|
是
|
是
|
简要身份验证
|
是
|
否
|
是
|
否
|
否
|
NTLM
身份验证
|
是
|
否
|
否
|
否
|
否
|
Kerberos
身份验证
|
是
|
是
|
是
|
否
|
否
|
证书身份验证
|
否
|
是
|
否
|
否
|
是
|
窗体身份验证
|
否
|
是
|
否
|
是
|
是
|
护照身份验证
|
否
|
是
|
否
|
否
|
是
|
3.2
选择身份验证机制需要考虑的因素
标识
只有当应用程序的用户具有的
Windows
帐户可以通过一个受信任的权威机构(它可以被应用程序
Web
服务器访问)来进行验证时,使用
Windows
身份验证机制才是合适的。
凭据管理
Windows
身份验证的一个关键优势在于它可以使用操作系统进行凭据管理。当使用非
Windows
身份验证方式,例如窗体身份验证时,必须仔细考虑在何处以及如何保存用户凭据。其中最常用的方式是使用
SQL Server
数据库或是使用位于
Active Directory
中的
User
对象。
标识流动
是否需要实现一个模拟
/
委托模型,并将原始调用者的安全上下文在操作系统级进行跨层流动
-
例如,以便支持审核或针对每个用户的精细授权。
浏览器类型
应用程序的所有用户是否都拥有
IE
浏览器?或是你是否需要支持一个具有混合型浏览器的用户群?
我们选择身份验证时需要根据各种方式的特点,综合考虑以上因素。
3.3 Intranet
系统的选择决策流程
参见图
2
。
3.4 SQL Server
用户验证
对
SQL Server
的客户端进行验证,一般说来
Windows
身份验证要比
SQL Server
身份验证更安全,原因主要有以下几点:
·
前者负责管理用户的凭据信息,而且用户的凭据不会在网络上传输。
·
可以避免在连接字符串中嵌入用户名和密码。
·
可通过密码过期时限、最小密码长度、以及多次无效登录后请求的帐户锁定等措施改进登录安全性。这样可以见少词典攻击的威胁。
但是某些特定的应用程序方案中不允许使用
Windows
身份验证,例如:
·
数据库客户端和数据库服务器由一个防火墙分隔开,从而导致无法使用
Windows
身份验证。
·
应用程序需要使用多个标识连接到一个或多个数据库。
·
连接到的数据库不是
SQL Server
。
·
在
ASP.NET
中没有一种安全的方式以特定的
Windows
用户的身份运行代码。
在以上这些方案中,将必须使用
SQL
身份验证,或是数据库的本机身份验证机制。
4. ASP.NET
身份验证实现
4.1
方案特性
在这部分,仅提供了一种
Intranet
下交互式
WEB
应用程序的身份验证的实现,本方案假设具有以下特性:
·
只有通过了身份验证的客户端才能访问应用程序。
·
数据库相信应用程序对用户进行了相应的身份验证
-
即应用程序代表用户对数据库进行调用。
·
WEB
应用程序通过使用
ASP.NET
进程帐户连接到数据库。
·
用户的凭据信息是根据
SQL Server
数据库进行验证的。
·
使用窗体身份验证模式。
在
WEB
应用程序中,用户的凭据信息是根据
SQL Server
数据库,采用窗体身份验证模式,便于实现用户个性化设计。采用应用程序代表用户对数据库进行调用的方式,可采用受信任子系统模型,更好地利用数据库连接池,并且可以保证用户不能直接访问后端数据库,另外可以减少后端的
ACL
管理工作。
4.2
安全配置步骤
4.2.1
IIS
配置步骤
对
Web
服务的虚拟根目录启用匿名访问。
主要方法是使用
IIS MMC
管理单元,右击应用程序的虚拟目录,然后单击属性
---
〉目录安全性
--
〉匿名访问和安全控制
--
〉编辑。
4.2.2 A
SP.NET
配置步骤
1
.
将
ASPNET
帐户(用于运行
ASP.NET
)的密码重新设置为一个更安全的密码。
这样允许在数据库服务器上复制一个本地帐户(具有相同的用户名和密码)。为了使用
Windows
身份验证连接到数据库时,能够使
ASPNET
帐户对来自数据库的网络身份验证要求进行响应,这是必须的。
具体方法是编辑位于
%windr%\Microsoft.NET\Framework\v1.1.4322\CONFIG
目录下的
Machine.config
文件,将
<processModel>
元素上的密码属性重新配置,将其默认值
<!-UserName="machine" password="AutoGenerate" -->
改为
<!-UserName="machine" password="NewPassword" -->
。
2
.
配置
ASP.NET
,使用窗体身份验证。
编辑位于
WEB
服务的虚拟根目录下的
Web.config
文件,将
<authentication>
元素设置为:
<authentication mode="Forms" >
<forms name="MyAppFormAuth" loginUrl="login.aspx" protection="All" timeout="20" path="/">
</forms>
</authentication>
4.2.3
配置
SQL Server
1
.
在
SQL Server
数据库上创建一个和
ASP.NET
进程帐户匹配的
Windows
帐户。
用户名和密码必须和
ASP.NET
应用程序帐号匹配。
2
.
配置
SQL Server
,使其使用
Windows
身份验证。
3
.
为自定义的
ASP.NET
应用程序帐户创建一个
SQL Server
登录,授予对
SQL Server
的访问权。
4
.
创建一个新的数据库用户,并将登录名映射为数据库用户。
5
.
创建一个用户定义的新数据库角色,并将数据库用户添加到该角色。
6
.
为数据库角色确定数据库权限。
4.3
程序代码
4.3.1
身份验证事件序列
当未通过身份验证的用户试图放一个受保护的文件或资源被拒绝时,触发的事件序列如图
3
所示。
4.3.2
代码实现步骤
1
.
建一个
WEB
登录窗体并验证用户提供的凭据信息
根据
SQL Server
数据库来验证凭据信息。
2
.
从数据库里获取角色列表
3
.
创建窗体身份验证票证
在票证中保存所获取的角色信息。示例代码如下:
private void btnLogin_Click(object sender, System.EventArgs e)
{
//
根据
SQL Server
数据库进行验证(具体实现略)。
bool isAuthenticated = IsAuthenticated( txtUserName.Text, txtPassword.Text );
if (isAuthenticated == true )
{
//
获取用户的角色
string roles = GetRoles( txtUserName.Text, txtPassword.Text );
//
创建身份验证票证
FormsAuthenticationTicket authTicket = new
FormsAuthenticationTicket(1, // version
txtUserName.Text, // user name
DateTime.Now, // creation
DateTime.Now.AddMinutes(60),// Expiration
false, // Persistent
roles ); // User data
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
//
创建
Cookie
HttpCookie authCookie =
new HttpCookie(FormsAuthentication.FormsCookieName,
encryptedTicket);
Response.Cookies.Add(authCookie);
//
将用户重定向到最初请求页面。
Response.Redirect( FormsAuthentication.GetRedirectUrl(
txtUserName.Text,
false ));
}
}
4
.
创建
IPrincipal
对象
可在
Application_AuthenticateRequest
事件中创建一个
IPrincipal
对象,一般使用
GenericPrincipal
类。
5
.
将
IPrincipal
对象置于当前的
HTTP
上下文
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
//
提去窗体身份验证
cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if(null == authCookie)
{
return;
}
FormsAuthenticationTicket authTicket = null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch(Exception ex)
{
return;
}
if (null == authTicket)
{
return;
}
//
提取角色
string[] roles = authTicket.UserData.Split(new char[]{'|'});
//
创建
Identity object
FormsIdentity id = new FormsIdentity( authTicket );
GenericPrincipal principal = new GenericPrincipal(id, roles);
Context.User = principal;
}
具体的代码读者可以自行补充完成。