ASP.NET Forms Authentication所生成Cookie的安全性

最初我想,.net的验证应该是比较安全的吧,生成的cookie也应该与这台电脑的独特的参数相关,拿到另一台电脑上就应该无效了。那么是不是一个用户名对应一个cookie值呢?是否能够通过伪造cookie值来骗过表单验证呢?做一番试验。
web.c
<authentication mode="forms">
<forms name="mylab" loginurl="/login.aspx">
<credentials passwordformat="clear">
<user name="fancyray" password="fancyray"/>
</credentials>
</forms>
</authenticati

<authorizati
<deny users="?" />
</authorizati

login.aspx只有一个用户名输入框txtusername、一个密码输入框txtpassword和一个提交按钮,click事件如下:
if (formsauthentication.authenticate(this.txtusername.text, this.txtpassword.text))
{
formsauthentication.redirectfromloginpage(this.txtusername.text, true);
}
else
{
response.write("login denied");
}
借助iehttpheaders(http://www.blunck.info/)可以看到,通过验证后增加了一个类似这样的cookie:
mylab=3ff83247c29eb5d14d61f389d453eee0586b94e27609c321b017be7b88d1a94d249996428a7a18f5c2d69f3c4dd2b88c00172cafb0b4b4ed8784db62d1d61bcc0c786b4ea7868fc6
看来这就是加密以后的cookie了。下面要换一台电脑,直接将这个值设置为cookie,看看是否需要forms验证。
在login.aspx页面中加上这样一句话:
<script language=javascript>
document.cookie="mylab=3ff83247c29eb5d14d61f389d453eee0586b94e27609c321b017be7b88d1a94d249996428a7a18f5c2d69f3c4dd2b88c00172cafb0b4b4ed8784db62d1d61bcc0c786b4ea7868fc6";
</script>
这样只要一打开login.aspx页面就会自动加入这个cookie。
另一台电脑:输入同一个webapplication下的另外一个页面(应该会自动跳转到login.aspx页面)http://10.0.0.7/upload.aspx,这时成功跳转到了http://10.0.0.7/login.aspx?returnurl=%2fupload.aspx,正常。这时cookie的值应该已经生效了。那么我们再输入刚才那个页面的网址http://10.0.0.7/upload.aspx!
按照我的猜想,肯定还会跳到login.aspx页面的,因为那个cookie是在另一台电脑上生成的。实际呢,没有跳转!完整地显示出了upload.aspx的内容!而我们根本没有在这台电脑上登录,甚至我们连用户名都不知道!
我回到10.0.0.7这台电脑在upload.aspx页面的page_load()第一行添了一句:response.write (user.identity.name);,在另一台电脑上刷新显示出来了的upload.aspx,结果也出现了fancyray,正是我的用户名。
这说明,cookie的加密不依赖于登录的电脑。也就是说,一旦你的cookie被别人获得,他就有可能获得你在这台服务器上的权限。
那么cookie的这个值是怎么来的呢?黑客是否可能不通过穷举就得到这个值呢?

我们先来看看cookie中到底存储了一些什么,以及怎样进行的加密。reflactor(http://www.aisto.com/roeder/dotnet)上场!
public static void setauthcookie(string username, bool createpersistentcookie, string strcookiepath)
{
formsauthentication.initialize();
httpcontext.current.response.cookies.add(formsauthentication.getauthcookie(username, createpersistentcookie, strcookiepath));
}

public static httpcookie getauthcookie(string username, bool createpersistentcookie, string strcookiepath)
{
formsauthentication.initialize();
if (username == null)
{
username = "";
}
if ((strcookiepath == null) || (strcookiepath.length < 1))
{
strcookiepath = formsauthentication.formscookiepath;
}
formsauthenticati ticket1 = new formsauthenticati username, datetime.now, createpersistentcookie ? datetime.now.addyears(50) : datetime.now.addminutes((double) formsauthentication._timeout), createpersistentcookie, "", strcookiepath);
string text1 = formsauthentication.encrypt(ticket1);
formsauthentication.trace("ticket is " + text1);
if ((text1 == null) || (text1.length < 1))
{
throw new httpexception(httpruntime.formatresourcestring("unable_to_encrypt_cookie_ticket"));
}
httpcookie cookie1 = new httpcookie(formsauthentication.formscookiename, text1);
cookie1.path = strcookiepath;
cookie1.secure = formsauthentication._requiressl;
if (ticket1.ispersistent)
{
cookie1.expires = ticket1.expirati
}
return cookie1;
}
cookie中存储的值就是里面的text1,text1是由string text1 = formsauthentication.encrypt(ticket1);生成的,所以text1里面的信息就是ticket1了。 formsauthenticati
public formsauthenticati versi string name, datetime issuedate, datetime expirati bool ispersistent, string userdata, string cookiepath)
里面有用户名、生成ticket1的时间和过期时间。
看到这里我不禁打了一个冷颤。ticket1实际上只用到了用户名一个关键信息,连密码都没有用!这样岂不是任何一个用户的ticket1都可以轻易的制造出来吗?只要通过formsauthentication.encrypt(ticket1)就得到了cookie的值,来伪装成任何一个用户?太可怕了。现在只能寄希望于encrypt这个函数了。看看它的实现:
public static string encrypt(formsauthenticati ticket)
{
if (ticket == null)
{
throw new argumentnullexcepti
}
formsauthentication.initialize();
byte[] buffer1 = formsauthentication.maketicketintobinaryblob(ticket);
if (buffer1 == null)
{
return null;
}
if (formsauthentication._protection == formsprotectionenum.n
{
return machinekey.bytearraytohexstring(buffer1, 0);
}
if ((formsauthentication._protection == formsprotectionenum.all) || (formsauthentication._protection == formsprotectionenum.validati
{
byte[] buffer2 = machinekey.hashdata(buffer1, null, 0, buffer1.length);
if (buffer2 == null)
{
return null;
}
formsauthentication.trace("encrypt: mac length is: " + buffer2.length);
byte[] buffer3 = new byte[buffer2.length + buffer1.length];
buffer.blockcopy(buffer1, 0, buffer3, 0, buffer1.length);
buffer.blockcopy(buffer2, 0, buffer3, buffer1.length, buffer2.length);
if (formsauthentication._protection == formsprotectionenum.validati
{
return machinekey.bytearraytohexstring(buffer3, 0);
}
buffer1 = buffer3;
}
buffer1 = machinekey.encryptordecryptdata(true, buffer1, null, 0, buffer1.length);
return machinekey.bytearraytohexstring(buffer1, buffer1.length);
}

看到了machinekey这个词,终于松了一口气。看来加解密过程是与服务器的参数有关系。也就是说,服务器上有自己的密钥,只有用这个密钥才能进行cookie的加解密。如果不知道这个密钥,别人是无法伪造cookie的。

看来cookie还是安全的,在你的电脑没有被入侵的前提下。与其他任何信息一样,所需注意的仅仅是网络传输中的安全性了。
cookie的这个值和sessi

还有一个问题不容忽视。虽然cookie的值的生成与具体的时间有关,也就是我注销后再次登陆所生成的cookie是不一样的,但是一个合法的 cookie值是永久有效的,不受是否改变密码及时间的影响。也就是说,我上一次生成的cookie在我注销以后拿到另一台电脑上仍然可以用,只要服务器的machinekey不变。的确是个安全隐患。我们也只能说:“cookie的值很长,要穷举到一个有效的cookie在有生之年是办不到的”来找一些安慰。密码可以通过频繁的更改来进一步减小穷举到的可能性,但合法的cookie却无法更改。密码是唯一的,但合法的cookie值却不是唯一的。这一切总让人觉得不太放心。
也许担心是多余的,因为电子签名、证书都是建立在“穷举要付出很大代价”的基础上的,要是考虑“碰巧被穷举到”的话,安全就不复存在了。相信在一般的安全领域,forms生成的cookie的安全级别还是足够的。

在asp.net中长内容自动分页的实现

在asp.net中长内容自动分页的实现
在一篇文章过长时,可以自动的写个小程序对其进行分页.
具体代码:

public class t3 : system.web.ui.page
{
private string str;//字符
private int strl;//字符总长度
private int pagesize;//每页显示的字符数
private void page_load(object sender, system.eventargs e)
{
// 在此处放置用户代码以初始化页面
str="1234567891234567898522555";
pagesize=3;
strl=str.length;
response.write(strl);
substr();
}

private void substr()
{
int ct=int32.parse(request.querystring["page"]);
if(strl==(strl/pagesize)*pagesize)//看看页面的总记录是否能被每页的记录数整除
{
for(int i=1;i<=strl/pagesize;i++)
{
response.write("页:<a href='#'  +">"+(i)+"</"+"a>");
}
string s=str.substring(pagesize*ct-pagesize,pagesize);
response.write(s);
}
else if(ct*pagesize>strl)//在不被整除的情况下,最后一页的设置,如字符长13,每页3,则处理最后那一页的显示
{
for(int i=1;i<=(strl/pagesize)+1;i++)
{
response.write("页:<a href='#' /"+"a>");
}
string s=str.substring((ct-1)*pagesize,strl-(ct-1)*pagesize);
response.write(s);

}
else //在不被整除的情况下其他页面的显示设置
{
for(int i=1;i<=strl/pagesize+1;i++)
{
response.write("页:<a href='#'  +">"+(i)+"</"+"a>");
}
string s=str.substring(pagesize*ct-pagesize,pagesize);
response.write(s);
}

}
}

解决ASP.NET上传文件大小限制

解决asp.net上传文件大小限制
对于asp.net,默认只允许上传2m文件,增加如下配置,一般可以自定义最大文件大小.

<httpruntime

executi

maxrequestlength="40960"

usefullyqualifiedredirecturl="false"/>

如果还不行,可以使用思归提供的方案:

我们在上传大文件时都遇到过这样或那样的问题。设置很大的maxrequestlength值并不能完全解决问题,因为asp.net会block直到把整个文件载入内存后,再加以处理。实际上,如果文件很大的话,我们经常会见到internet explorer显示 "the page cannot be displayed – cannot find server or dns error",好像是怎么也catch不了这个错误。为什么?因为这是个client side错误,server side端的applicati

handling server error when upload file too large

解决的方法是利用隐含的httpworkerrequest,用它的getpreloadedentitybody 和 readentitybody方法从iis为asp.net建立的pipe里分块读取数据

iserviceprovider provider = (iserviceprovider) httpcontext.current;
httpworkerrequest wr = (httpworkerrequest) provider.getservice(typeof(httpworkerrequest));
byte[] bs = wr.getpreloadedentitybody();
….
if (!wr.isentireentitybodyispreloaded())
{
int n = 1024;
byte[] bs2 = new byte[n];
while (wr.readentitybody(bs2,n) >0)
{
…..
}
}

ASP.NET程序员应用程序域须知

asp.net程序员应用程序域须知
本文将讨论.net的应用程序域,并且它们是如何对asp.net的执行和调度产生影响的。

  当在windows中启动notepad程序时,众所周知程序会执行一个包含在容器内的进程。可以启动多个notepad的实例,并且每个实例都会在一个专注的进行程运行。使用任务管理器,可以看到在系统中当前运行的所有进程的清单。

  一个进程包括可执行从操作系统中保留的在内存中的代码和程序数据。在进程之内只少有一个包含在进程之内的正在执行指令的线程,并且在多数情况下有多个线程。如果程序打开了任何文件或者资源,这些资源将属于这个进程。

  一个进程也有一个分界线。包含在进程之内的错误代码不能在当前进程之外的地区腐化。在一个进程之内很容易通讯,但是专业技术要求一个进程对另一个进程通讯是必需的。每一个进程也在一个特殊的上下文安全系统中运行,这个安全系统规定在机器和网络中进程做什么。

  一个进程是一个在windows 操作系统中独立运行的最小单位。这会给在一个单一服务器上对一大堆应用程序的isp提出一个问题。isp将会分离每一个在同一个服务器上的与另一个公司的应用程序干扰的asp.net应用程序。但是相关的发射和执行一个对成百上千的应用程序的过程成本禁止的。

  介绍应用程序域

  .net介绍一个应用程序域的概念,或者appdomain像一个过程,appdomain是既是容器又是边界线。.net运行时间使用 appdomain作为代码和数据的容器,就像操作系统一个过程作为代码和数据的容器一样。当操作系统使用一个过程来分离不整齐的代码时,.net运行时间使用一个appdomain来分离在一个安全边线内的代码。

  一个appdomain仅仅属于一个单过程,但是单个过程能够保持多重的appdomain。一个appdomain创建起来相对容易(与一个过程比较起来),并且与一个过程比较起来具有少的维护费用。由于这些原因,一个appdomain是isp(提供成千上万的应用程序)的很好的解决方案。每一个应用程序可以生存在一个独立的appdomain之内,并且许多这样的appdomain可以生存于一个单一的过程(节省费用)之内。

  appdomain

  在同服务器上创建了两个asp.net应用程序,并且没有任何特殊配置。会发生什么事情呢?

  一个单一的asp.net手工进程使asp.net应用程序变成两方面的主要程序。在windows xp和windows 2000中,这一程序被命名为aspnet_wp.exe,并且这一程序运行在本地的aspnet计数器的前后安全关系中。在windows 2003手工程序拥有w3wp.exe并且默认运行在networ service中。

  一个对旬可以进住在一个appdomain中。每一个asp.net应用程序将具有它自己的一套全局变量:cache, application进住进同一进程,.net appdomain是一个独立的单元。如果存有共享的或静态成员的类,并且那些类存在于两种应用程序之内,每一个appdomain拥有它自己的静态字段的备份—数据并不共享。每一个应用程序的数据和代码安全独立存在并且在一边界之内由appdomain提供。

  为了在appdomain之间通讯或者在appdomain之间交换对象,需要查看在.net中穿过边界的通讯技术,例如.net细微的或web 服务。

  对将appdomain作为边界思想的警告之一是asp.net应用程序在默认情况下会带着充分的信任运行。充分信任的代码可以执行本地代码,并且本地代码可以本质地在进程之内的任何内容。需要运行带着部分信任执行应用程序来约束存取不完整的代码并且对安全的appdomain验证所有代码。

  隐藏备份并且重新启动

  一旦一个集合加载到一个appdomain,没有办法从appdomain集合的办法。不过,从一个进程中移除一个appdomain是有可能的。

  如果将一个已更新的dll复制到一个应用程序的子目录中,从asp.net的运行时间知道有新代码要执行。既然asp.net不能将dll复制到已存在的appdomain中,它就会起动一个新appdomain。旧的应用程序域是“排水已停止”,那就是,存在的需要被允许完成执行并且一旦它们执行完成appdomain可以卸载。带有新代码的新的appdomain就会开始并且开始所有的新请求。

  典型地说,当一个dll加载进一个进程时,进程对dll加锁并且不能对磁盘的上的文件进行覆盖。不过,appdomain有一个众所周知的特点:隐藏复制那所有的允许保留在磁盘上的那些未被加锁的可替换的集合。

  运行时间对二进制子目录的带有shadow copy的asp.net进行初始化。appdomain将任何的加锁之前的dll从二进制子目录中拷贝到一个临时位置并且再将这些dll加载到内存。 shadow copy允许没有将网页在线的情况下对所有在二进制子目录中的任何dll进行重写。

  熟练掌握domain

  应用程序域替换os进程将为单独的.net结点单元。一个可理解的应用程序域将会给你一个在asp.net应用程序后的手工发生的概念。使用 appdomain类的currentdomain属性,可以检查关于代码正在运行的appdomain的属性,包括我们在此文章中讨论的shadow copy。