序
曾经在一家公司有过这样的经历:上班第一天﹐同事在公司的内部网帮我开了一个账号﹐要我登录公司的管理系统学习一下公司的管理制度。看完这些“文件”后,我随便点了一下系统左边的"员工信息查询"菜单﹐随即右边网页的数据区域显示"您无权查看此页"的错误信息﹐本想退出﹐但发现该页面的查询条件输入区域仍在﹐而且查询按钮也只是灰掉而已﹐在查看了网页原代码后﹐抱着随便试一下的心态﹐我在浏览器的地址栏里输入了一行js代码:javascript:alert(document.all['querybtn'].disabled=false) 使查询按钮启用﹐然后单击它﹐居然真的把人事基本资料给查了出来,随后我又打开这个系统的其它页面﹐发现都只是把动作按钮给disable掉来管理权限。
当把人事薪资等非常敏感的资料放在web系统中时,如果只是通过上面这种方式来保证数据不被非法读取,很明显这个系统没有达到它应该达到的安全级别。
作为一个web系统设计师,在规划一个系统时,必然会考虑到系统的安全性。如何有效的保证系统的安全,如何规划和实现一个可重用,可扩展的安全管控方案都是在安全管控时要考虑的主题。
借着这个机会,笔者打算将自己从事web系统安全设计的经验和大家分享。从一个系统设计师的角度说明web系统安全管控。
1. web运作原理,您的系统到底有多安全?
2. 权限抽象,还一个统一的权限接口
3. 管控观念转换,柳岸花明又一村。
4. 通用安全组件,从此不再苦海挣扎。
Web运作原理
Web是由客户端(Client)的请求(Request)和服务器(Web Server)的响应(Response)构成,同一个客户端的多次Request对于Web Server来说都一样,服务器不会将当前收到的Request和以往任何的Request联系起来,因为它们交互的依据是http协议,而此协议规定了http连接的无状态特征。
以下为最一个最简单的Request请求:
GET /TestWeb/test.htm HTTP/1.1
Host: localhost
Connection: close
它表示向localhost主机请求路径为/TestWeb/test.htm的html网页,使用GET方法, 1.1版本的HTTP协议。
对此请求,Windows的IIS6.0是这样给出Response的:
HTTP/1.1 200 OK
Content-Length: 12
Content-Type: text/html
Last-Modified: Wed, 05 Nov 2008 01:01:17 GMT
Accept-Ranges: bytes
Server: Microsoft-IIS/6.0
Date: Wed, 05 Nov 2008 01:01:52 GMT
Connection: close
Hello World!
包括响应状态码200,body的长度,类型,所请求文件的最后修改日期等响应头(Response Header),还有简单的Hello World! 12个字符的html响应体(Response Body)
Request请求不依赖于浏览器,事实上您可以使用任何程序语言通过网络编程来做到,以下是一个C#发送Request的例子:
using System;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
public class RequestDemo
{
//建立socket连接
private static Socket ConnectSocket(string server, int port)
{
Socket s = null;
IPHostEntry hostEntry = null;
hostEntry = Dns.GetHostEntry(server);
foreach (IPAddress address in hostEntry.AddressList)
{
IPEndPoint ipe = new IPEndPoint(address, port);
Socket tempSocket =
new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
tempSocket.Connect(ipe);
if (tempSocket.Connected)
{
s = tempSocket;
break;
}
else
continue;
}
Console.WriteLine(s == null ? "" : "连接建立成功﹗");
return s;
}
//发送request请求并接收响应字符串
private static string SocketSendReceive(string request, string server, int port)
{
Byte[] bytesSent = Encoding.ASCII.GetBytes(request);
Byte[] bytesReceived = new Byte[256];
Socket s = ConnectSocket(server, port);
if (s == null)
return ("连接失败﹗");
Console.WriteLine("正在发送请求...");
s.Send(bytesSent, bytesSent.Length, 0);
int bytes = 0;
StringBuilder responsestr = new StringBuilder();
Console.WriteLine("正在接收web服务器的回应...");
do
{
bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
responsestr.Append(Encoding.UTF8.GetString(bytesReceived, 0, bytes));
}
while (bytes > 0);
return responsestr.ToString();
}
public static void
{
//读取在Request.txt中的Request字符串(request.txt末尾至少要留个空行,表明Request结束)
string requeststr = File.ReadAllText("C:\\tmp\\request.txt")
Console.WriteLine("请求字符串如下﹕\n{0}\n", requeststr;
//发送且接收Response
string result = SocketSendReceive(requeststr, "localhost", 80);
Console.WriteLine("\n{0}", result);
Console.ReadLine();
}
}
注:C:\tmp\request.txt中的内容就是前面的Request字符串。
程序执行的结果如下:
除了直接进行Request外,大部分时候,我们在网页上单击某个链接或按钮时,浏览器和web服务器也在背后进行着这样的请求和响应。
例如以下网页程序:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="form.aspx.cs" Inherits="form" %>
<!DOCTYPE html PUBLIC "-//W
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>测试窗体Request</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server" Text="你的名字:"></asp:Label>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="送出" />
<asp:Label ID="Label2" runat="server" ForeColor="OrangeRed"></asp:Label></div>
</form>
</body>
</html>
Aspx.cs代码如下:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class form : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Label2.Text = "你输入的名字是:" + TextBox1.Text;
}
}
当输入“小生”并按钮“送出”按钮时,实际上就是发送下面的这样一段Request
POST /TestWeb/form.aspx HTTP/1.1
Cache-Control: no-cache