在Web编程过程中,存在着很多安全隐患。比如在以前的ASP版本中,Cookie为访问者和编程者都提供了方便,并没有提供加密的功能。打开IE浏览器,选择“工具”菜单里的“Internet选项”,然后在弹出的对话框里单击“设置”按钮,选择“查看文件”按钮,在弹出的窗口中,就会显示硬盘里的缓存数据,其中就有大量的Cookie文件。如图1所示。问题是,这样安全吗?
于是,我们会想到很多安全措施,比如使用SSL、建立网上银行一样的强认证方式等,但是,对于大部分网站而言,可能这些方法具有一定的困难性,因为要实现这些比较强的安全措施,最起码需要一台服务器,在经济上显然不是小数目。那么,有没有比较经济的方法来解决这类问题呢?答案是肯定的,现在.NET构架在System.Security.Cryptography命名空间里提供了许多加密类可以利用,包括安全的数据编码和解码以及散列法、随机数字生成和消息身份验证。下面,我们来看看几个比较典型的例子,以供参考。
实现ASP.net表单的安全提交
使用ASP.NET环境下的电子邮件功能,我们可以容易的实现发送电子邮件。可能我们会说,不用ASP.NET,直接使用HTML,也可以实现以上功能。其实,这是完全不一样的:使用HTML,用户提交信息的时候,是调用客户端的邮件收发软件,使用用户的邮箱发送表单信息,如果用户没有邮箱或者用户没有邮件收发软件,就不能提交信息;而采用ASP.NET,可以使用服务器端(程序设计者)提供的邮箱和SMTP服务器,也不需要客户安装邮件收软件,一切都直接在服务端完成,显然更加适合我们的要求。现在,我们就来看以上功能的代码实现。首先,我们来看网页界面实现的关键代码:
<Form id="form1" runat="server"> 姓名: <asp:TextBox id="txtFname" runat="server" /> 地址: <asp:TextBox id="txtAddr" runat="server" /> 内容: <asp:TextBox id="txtContent" runat="server" /> <asp:Button id="btnEmail" Text="提交" onclick="doEmail" runat="server" /> </Form> |
以上只是很简单的几个提交项,在实际中,我们可以根据需要增加。还是根据上述例子,因为需要用到电子邮件功能,所以,我们需要引入Email名字空间:<%@ Import Namespace="System.Web.Mail" %>;然后,我们来实现按钮btnEmail的点击事件:
Sub doEmail(Source as Object, E as EventArgs) Dim sMsg as String sMsg+="提交信息:" & vbcrlf sMsg+="姓名 :" & txtFname.Text & vbcrlf sMsg+="地址: " & txtAddr.Text & vbcrlf sMsg+="内容: " & txtContent.Text & vbcrlf Dim objEmail as New MailMessage objEmail.To="whpub@tom.com" objEmail.FROM="from@tom.com" objEmail.SUBJECT="提出意见" objEmail.BODY=sMsg objEmail.BodyFormat = MailFormat.Text SmtpMail.SmtpServer ="mail.tom.com " SmtpMail.Send(objEmail) End Sub |
以上我们用很简单的举例介绍了安全实现表单提交。实际使用中,我们可以在以上代码中加入附件提交等功能完善我们的程序。
Cookie的安全问题
还是接我们最初提到的一个话题。在ASP.net编程中,Cookie数据包含在HTTP请求和响应的包头里透明地传递,也就是说聪明的人是能清清楚楚看到这些数据的。其次,Cookie数据以Cookie文件格式存储在浏览者计算机的cache目录里,其中就包含有关网页、密码和其他用户行为的信息,那么只要进入硬盘就能打开Cookie文件,所以实际编写中,我们不要将敏感的用户数据存放在Cookie中,要么就通过加密将这些数据保护起来。使用一种算法不仅要考虑加密强度,还要考虑其他因素,比如Cookie的大小要限制在4KB。一般来说,由于加密后的Cookie数据将变大,并且密钥越大,加密后的数据就越大,就会占有更多的服务器资源,进而减慢整个站点的访问速度。
另外,我们不能直接修改一个Cookie,应该是创建一个同名的 Cookie,并把该 Cookie 发送到浏览器,覆盖客户机上旧的 Cookie。同样,我们无法直接将其删除一个Cookie,可以通过修改一个Cookie达到让浏览器删除Cookie的目的,修改Cookie的有效期为过去的某个时间,当浏览器检查 Cookie 的有效期时,就会删除这个已过期的 Cookie。
通过web.config进行站点配置
使用ASP、PHP、JSP编写代码时,虽然我们编写了用户登录,注册,验证页面,但是效果总是不理想,因此,站点安全性总是需要花费不少的时间,而且往往会造成顾此失彼的问题。但是,在.NET环境下,这个问题就很容易了,而问题的关键是要充分理解Web.config文件。Web.config文件的相关选项具体含义如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> <!-- 动态调试编译 设置 compilation debug="true" 以将调试符号插入到编译页中。因为这将创建执行起来较慢的大文件,所以应该只在调试时将该值设置为 true,而所有其他时候都设置为false。 --> <compilation defaultLanguage="vb" debug="true" />
<!-- 自定义错误信息 设置 customErrors mode="On" 或 "RemoteOnly" 以启用自定义错误信息,或设置为 "Off" 以禁用自定义错误信息。可以为每个要处理的错误添加 <error> 标记。 --> <customErrors mode="RemoteOnly" />
<!-- 身份验证 设置应用程序的身份验证策略。可能的模式是 /“Windows/”、/“Forms/”、/“Passport/”和 /“None/” --> <authentication mode="Windows" />
<!-- 授权 此节设置应用程序的授权策略。可以允许或拒绝用户或角色访问应用程序资源。通配符:"*" 表示任何人,"?" 表示匿名(未授权的)用户。 --> <authorization> <allow users="*" /> <!-- 允许所有用户 -->
<!-- <allow users="[逗号分隔的用户列表]" roles="[逗号分隔的角色列表]"/> <deny users="[逗号分隔的用户列表]" roles="[逗号分隔的角色列表]"/> --> </authorization>
<!-- 应用程序级别跟踪记录 应用程序级别跟踪在应用程序内为每一页启用跟踪日志输出。 设置 trace enabled="true" 以启用应用程序跟踪记录。如果 pageOutput="true",则跟踪信息将显示在每一页的底部。否则,可以通过从 Web 应用程序根浏览 "trace.axd" 页来查看应用程序跟踪日志。 --> <trace enabled="false" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" />
<!-- 会话状态设置 默认情况下,ASP.NET 使用 cookie 标识哪些请求属于特定的会话。如果 cookie 不可用,则可以通过将会话标识符添加到 URL 来跟踪会话。若要禁用 cookie,请设置 sessionState cookieless="true"。 --> <sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;user id=sa;password=" cookieless="false" timeout="20" />
<!-- 全球化 此节设置应用程序的全球化设置。 --> <globalization requestEncoding="utf-8" responseEncoding="utf-8" /> </system.web> </configuration> |
下面,我们来看一个具体的设置实例,为了防止用户没有经过验证就访问站点,我们的处理方法是:当用户没有通过验证的时候点击任何页面将会直接跳到Login.aspx页面,具体代码如下:
<authentication mode="Forms"> <forms name="yourAuthCookie" loginUrl="login.aspx" protection="All" path="/" /> </authentication> <authorization> <deny users="?" /> </authorization> |
但是这样会产生一个问题,那就是如果我的站点有一些信息是可以让任意用户随意访问的,比如站点简介、使用说明等。不同级别的处理也是一个比较麻烦的问题,如果按照上面的处理方法,肯定很麻烦。在ASP.NET中也会有相应的解决办法,下面的代码可以实现匿名用户访问Test.aspx页面,具体如下:
<location path="test.aspx"> <system.web> <authorization> <allow users="?" /> </authorization> </system.web> </location> |
.net加密的典型应用
在网站上建立数据库时,保护用户的信息安全是非常必要的。多数用户不愿意让别人知道自己的信息,同时网管也不想因为安全问题而丢失网站的信誉。无论对于谁,安全问题都是非常重要的。简单的讲,就是将用户提供的口令加密之后,然后让它和存放于系统中的数据比较,如果相同,则通过验证。目前有两种加密方法:对称加密(或称私有密钥)和非对称加密(或称公共密钥)。.NET构架从基本的SymmetricAlgorithm类中,扩展了四种算法,具体如下:
·System.Security.Cryptography.DES ·System.Security.Cryptography.TripleDES ·System.Security.Cryptography.RC2 ·System.Security.Cryptography.Rijndael |
ASP.NET在名字空间System.Web.Security中包含了类FormsAuthentication,其中有一个方法HashPasswordForStoringInConfigFile,这个方法可以将用户提供的字符变成乱码,然后存储起来,甚至可以存储在cookies中。另外,在我们的网页中有各种密码需要保护,把密码直接放在数据库或者文件中存在不少安全隐患,所以密码加密后存储是最常见的做法。在ASP.NET中实现加密非常容易。.NET提供了HashPasswordForStoringInConfigFile方法可直接使用MD5和SHA1算法。例子如下,首先Encrypting.aspx文件:
<%@ Page language="c#" Codebehind="encrypting.aspx.cs" AutoEventWireup="false" Inherits="encrypting.encrypting" %> <HTML> <HEAD> <meta name="GENERATOR" Content="Microsoft Visual Studio 7.0"> <meta name="CODE_LANGUAGE" Content="C#"> </HEAD> <body> <form method="post" runat="server" ID="Form1"> <p><FONT face="宋体"></FONT> </p> <p><asp:TextBox id="TextBox1" runat="server"></asp:TextBox><asp:Button id="Button1" runat="server" Text="MD5加密"></asp:Button></p> <p><asp:TextBox id="TextBox2" runat="server"></asp:TextBox><asp:Button id="Button1" runat="server" Text="SHA1加密"></asp:Button></p> <p>加密后的字串1是:<asp:Label id="MD5" runat="server"></asp:Label></p> <p>加密后的字串2是:<asp:Label id="SHA1" runat="server"></asp:Label></p> </form> </body> </HTML> |
以下是实现加密的代码encrypting.cs摘要:
…… public void Button1_Click (object sender, System.EventArgs e) { //取出MD5值 MD5.Text = FormsAuthentication.HashPasswordForStoringInConfigFile(TextBox1.Text,"md5"); //可以取出SHA1值 SHA1 use FormsAuthentication.HashPasswordForStoringInConfigFile(TextBox2.Text,"SHA1");} } } } …… |
结合以上典型例子,通过简单的两句代码,我们就可以实现以前需要几十行甚至上百行代码的功能,这也是ASP.net吸引人之处。类似的操作还有很多,综合运用,我们就会对ASP.net中的一些安全编程一定有了一个初步的了解。当然,实际使用中,我们还需要结合System.Security.Cryptography命名空间,做一些更加有益的尝试,从而编写出更加健壮、安全的代码。