详解ASP.NET中Membership表和阻止DOS攻击

开发 后端
本文将讲述如何在ASP.NET中建立Membership表和阻止DOS攻击,希望能加快大家的访问速度。

无需关闭站点的情况下如何查询ASP.NET 2.0 Membership表

这种查询会爽快的运行在你的开发环境中:

Select * from aspnet_users where UserName = 'blabla'

或者在无需任何问题的情况下获得一些用户的 profile :

Select * from aspnet_profile where userID = '…...'

甚至你可以像这样在aspnet_membership表中轻松的更新用户的邮件地址:

Update aspnet_membership
SET Email = 'newemailaddress@somewhere.com'
Where Email = '…'

但是当在你的产品服务器上有一个巨大的数据库时,运行这些脚本都会搞垮你的服务器。理由就是,虽然这些查询看起来明显是你经常使用的语句,但是这些语句没有任何索引。因此,上面所有在"Table Scan"中的结果(很糟糕的查询方式)都会响应数百万行记录。

这里的现象对我们而言意味着什么。我们使用了这些字段诸如UserName, Email, UserID, IsAnonymous等。关于Pageflakes的很多市场报表。这些报表中的一些仅仅市场部可以使用,其他人都不可以使用它。现在,站点运行得很好,但是市场部一天当中会使用它很多次而且用户常常打电话告诉我们网站太慢了,”用户报告网站性能十分地慢!”,一些页面甚至还发生超时等现象。

常常,当他们打电话个给我们的时候,我们就会告诉他们“稍等,马上检查”,然后我们开始对整个网站进行检查。我们使用SQL profiler工具来查看到底那里出错了。但是我们不能找到任何问题。Profiler显示了查询运行的摁件。CPU负载位于适当的参数范围内。站点运行得很好,很流畅。我们打电话给他们,“我们不能查到任何问题,到底怎么了?”。

因此,当我们试图调查这个问题并且在我们没有调查时,发现站点真的变得很慢的时候,为什么我们不能看到它变慢的任何问题?

市场部有时候每天需要很多次运行一些分析报表来查询数据。无论什么时候他们执行这些查询,当这些字段都是不是索引时,它会使得服务器的IO吞吐量更高以及CPU的使用率增高:

我们使用了15000 RPM的SCSI驱动器,虽然很贵,但是运行速度很快。CPU是双核Dual Xeon 64位的。尽管有这些强大的硬件支持,但是由于这个巨大的数据库在执行查询时仍然把我们压倒了。

但是当市场部给我们打电话时,这些问题从来没发生过,并且我们没有挂断电话并试图找出问题所在。因为当他们打电话给我们时,会跟我们交谈,他们没有运行使服务器垮掉的任何报表。让他们停留在了站点上的其它地方,几乎做了和用户一样的事情.

咱们看看如下索引:

表: aspnet_users
◆簇索引 = ApplicationID, LoweredUserName
◆非簇索引 = ApplicationID, LastActivityDate
◆主键 = UserID

表: aspnet_membership
◆簇索引 = ApplicationID, LoweredEmail
◆非簇索引= UserID

表: aspnet_Profile
◆簇索引 = UserID

大部分索引都有ApplicationID在其中。除非你放置ApplicationID='…'在WHERE从句中,此时它就不会使用任何索引。结果,所有的查询会进行全表扫描。仅仅是把ApplicationID放在where从句中(从aspnet_Application表中找到你的ApplicationID)所有的查询就会变得非常快。

不要在WHERE从句中使用Email或者UserName。他们不是索引的一部分,从而取代LoweredUserName和LoweredEmail字段。所有的查询必须在WHERE从句中有ApplicationID。

我们的管理站点上包涵了很多这种报表并且每个报表都包涵了很多这种在表aspnet_users、 aspnet_membership 和aspnet_Profile上的查询。结果,当市场部试图产生报表时,它们会占用所有CPU、HDD的资源,最终导致站点变得非常慢甚至毫无响应。
确保你总是检查了所有的WHERE和JOIN从句。否则当你的网站上线时会出现大问题。

阻止决绝服务(DOS)攻击

对黑客而言,Web服务是最容被袭击的目标,因为甚至一个入门级的黑客都可以通过重复调用一个代价昂贵的Web服务来使服务器死掉。像Pageflakes这样以Ajax技术构建的页面并将其作为起始页的站点将是DOS攻击的最大目标,因为如果你仅仅是在无需保存cookie的情况下重复地访问主页,这样每次点击都会产生一个新的用户、新的页面配置、新的widgets等。第一次访问是花费代价最昂贵的一次。

可是这也是最容易暴露并搞垮站点的一次机会。你可以自己试试这种方式,仅仅需要如下一段简单的代码即可:

for( int i = 0; i < 100000; i ++ )
{
 WebClient client = new WebClient();
 client.DownloadString("http://www.pageflakes.com/default.aspx");
}


令你感到吃惊的是,你会注意到,在进行两次调用之后,你不会得到一个有效的响应。这并不是说你已经成功地将服务器搞垮了。而是因为你的请求被决绝了。你可能会乐意你不再获得任何服务了,因此你达到了决绝服务(你自己)的目的。我们很高兴决绝了你的服务(DYOS)。

我提出的这种简便技巧就是记住从某个特殊IP地址发来的请求。当请求的数量超过阀门值时,便决绝后续发来的请求。这个办法是在ASP.NET缓存中记住调用者的IP并为每个IP维护一个计数的请求。当该计数值超过了预定义的限定值时,决绝一段时期后的发来的请求,比如10分钟。10分钟后,再次允许来自那个IP的请求。

我有一个名为ActionValidator的类,该类包涵了一些具体的活动诸如首次访问、再次访问、异步回传、添加新的widget、添加新的页面等等。检查这种指定活动的计数值是否指向了一个IP阀门值。

public static class ActionValidator
{
 private const int DURATION = 10; // 10 min period

 public enum ActionTypeEnum
 {
 FirstVisit = 100, // The most expensive one, choose the value wisely.
 ReVisit = 1000,// Welcome to revisit as many times as user likes
 Postback = 5000,// Not must of a problem for us
 AddNewWidget = 100,
 AddNewPage = 100,
 }

该枚举包涵了活动的类型以便用来在某个持续的时间――10分钟检查它们的阀门值。

使用 一个名为IsValid的static方法用来进行检查。如果请求限制没有通过,则返回true。如果请求需要被决绝,则返回false。一旦你获得false,你就可以调用Request.End ()方法并阻止ASP.NET进行未来的处理。你可以切换显示了“Congratulations! You have succeeded in Denial of Service Attack.”的页面。

public static bool IsValid( ActionTypeEnum actionType )
{
 HttpContext context = HttpContext.Current;
 if( context.Request.Browser.Crawler ) return false;
 
 string key = actionType.ToString() + context.Request.UserHostAddress;
 var hit = (HitInfo)(context.Cache[key] ?? new HitInfo());
 
 if( hit.Hits > (int)actionType ) return false;
 else hit.Hits ++;

 if( hit.Hits == 1 )
context.Cache.Add(key, hit, null, DateTime.Now.AddMinutes(DURATION),
 System.Web.Caching.Cache.NoSlidingExpiration,
 System.Web.Caching.CacheItemPriority.Normal, null);
 return true;
}

内置的缓存键将活动类型和客户端IP地址进行了合并。首先它检查是否有针对活动的任何入口和存储于缓存中的客户端IP。如果没有,开始计数并记住指定持续时间内缓存中的IP。缓存项中的绝对期满日期确保缓存项在持续一段时间后能够被清除并重新计算。当在缓存中已经存在一个入口时,获得最后的点击数,并检查是否超越了该限制。如果没有超过,则增加计数器。

不需要通过Cache[url]=hit的方式存储更新后的值到缓存中;因为点击对象是一个引用对象,改变它意味着在缓存中它也要改变。事实上,如果你再次将它放入缓存中,缓存期满计数器将重新开始并在指定持续时期后导致失败逻辑重新计算。

用法十分简单, default.Aspx页面上的代码如下:

protected override void OnInit(EventArgs e)
{
 base.OnInit(e);

 // Check if revisit is valid or not
 if( !base.IsPostBack )
 {
// Block cookie less visit attempts
if( Profile.IsFirstVisit )
{
 if( !ActionValidator.IsValid(ActionValidator.ActionTypeEnum.FirstVisit))
Response.End();
}
else
{
 if( !ActionValidator.IsValid(ActionValidator.ActionTypeEnum.ReVisit) )
Response.End();
}
 }
 else
 {
// Limit number of postbacks
if( !ActionValidator.IsValid(ActionValidator.ActionTypeEnum.Postback) )
Response.End();
 }
}

这里我对具体的场景如访问、再次访问、回发等进行检查。

当然你可以通过放置一些Cisco的防火墙来阻止DOS攻击。这样你就可以避免主机提供商的整个网络不会因为受到DOS或者DDOS攻击的影响。这些所能保证的只是网络级别的攻击,诸如TCP SYN攻击、奇怪的数据报文分组等。在没有cookie或者试图加载太多widgets的情况下, 他们没有办法能够分析这些报文并找出一个特殊的IP来试图多次加载站点。这些都被称为应用程序级别的DOS攻击,硬件不能对其进行阻止。这些必须在你自己的代码中进行实现处理。

现在几乎很少有站点采用这种预防措施来防止遭受这种应用程序级别的DOS攻击。因此可以非常容易地编写一些简单的代码从宽带连接中通过持续点击站点上这些耗费昂贵的页面或者进行Web服务调用来使得服务器崩溃。我希望这对你来说只是一个小问题,并可以采用这种有效的方式帮助你阻止自己Web应用程序遭受DOS攻击。

小结
 
你已经学到了很多技巧来应付在具有相同硬件配置的机器上使得ASP.NET的性能发挥到极致。你也学到了如何使用一些AJAX技术来使得你的站点加载更快更流畅。最后,你学到了如何防御站点因高点击量所带来的风险以及扩展静态内容跨过内容传输网络来抵御站点高峰期引起的阻塞。所有这些技术都可以使你的站点加载速度更快,运行得更流畅,以及以更低的成本获得更高的负载量。

【编辑推荐】

  1. ASP.NET应用程序设计的10大技巧
  2. ASP.NET性能提升秘诀之管道与进程优化
  3. 写给ASP.NET程序员:网站中的安全问题
责任编辑:彭凡 来源: IT168
相关推荐

2009-08-05 13:58:26

ASP.NET Mem

2009-07-27 15:34:11

MembershipASP.NET

2009-07-22 17:48:47

2009-07-29 16:08:07

ASP和ASP.NET

2009-12-07 09:23:05

ASP.NET MVC

2009-08-27 17:20:30

ASP.NET登陆控件membership配

2009-07-28 16:57:50

ASP.NET Ses

2009-08-05 11:14:33

ASP.NET ISA

2009-07-24 10:14:22

ASP.NET开发

2009-07-22 16:25:41

ASP.NET AJA

2009-07-23 13:19:51

2009-08-13 11:44:25

ASP.NET中的多种

2009-08-04 17:20:37

PostBack和ViASP.NET

2009-07-27 12:22:03

ASP.NET和ASPASP.NET入门教程

2009-07-23 13:09:23

2009-07-28 13:39:44

加载ViewStateASP.NET

2009-08-04 10:43:59

ASP.NET控件开发

2009-08-19 13:44:00

ASP.NET Lis

2009-08-04 18:10:35

ASP.NET动态编译

2009-10-29 09:15:32

ASP.NET MVCDropDownLis
点赞
收藏

51CTO技术栈公众号