编者按:与传统的包过滤防火墙技术不同,本文从应用层网关技术入手,深入探讨了利用WinSock2 SPI进行网络内容访问控制的问题。这是网络安全的一项新内容,或者说,它为网络安全技术的爱好者和研发人员提供了一个新的思路。
防火墙可以实施和执行网络访问策略,但是,传统的防火墙技术集中于如何防范外部网络对内部网络的网络和攻击上,而对于如何控制内部用户对外部网络的访问问题研究不够深入,相关的控制技术也不多。据权威资料显示,全球现有大约25万色情网站,单纯依靠传统的包过滤等防火墙技术,势必会严重影响网络性能。针对这一问题,我们从应用层网关技术入手,利用WinSock2 SPI技术,进行了研究和探讨。
Winsock2 SPI原理图
Winsock2 SPI(Service Provider Interface)服务提供者接口建立在Windows开放系统架构WOSA(Windows Open System Architecture)之上,是Winsock系统组件提供的面向系统底层的编程接口。Winsock系统组 件向上面向用户应用程序提供一个标准的API接口;向下在Winsock组件和Winsock服务提供者(比如TCP/IP协议栈)之间提供一个标准的 SPI接口。各种服务提供者是Windows支持的DLL,挂靠在Winsock2 的Ws2_32.dll模块下。对用户应用程序使用的Winsock2 API中定义的许多内部函数来说,这些服务提供者都提供了它们的对应的运作方式(例如API函数WSAConnect有相应的SPI函数 WSPConnect)。多数情况下,一个应用程序在调用Winsock2 API函数时,Ws2_32.dll会调用相应的Winsock2 SPI函数,利用特定的服务提供者执行所请求的服务。
Winsock2 SPI允许开发两类服务提供者——传输服务提供者和名字空间服务提供者。“传输提供者”(Transport Providers, 一般称作协议堆栈,例如TCP/IP)能够提供建立通信、传输数据、日常数据流控制和错误控制等传输功能方面的服务。“名字空间提供者”(Name Space Providers,例如DNS名字解析服务)则把一个网络协议的地址属性和一个或多个用户友好名称关联到一起,以便启用与应用无关的名字解析方案。
Winsock2中使用的传输服务提供者有两类:基础服务提供者和分层服务提供者。基础服务提供者执行网络传输协议(比如TCP/IP)的 具体细节,其中包括在网络上收发数据之类的核心网络协议功能。“分层式”(Layered)服务提供者只负责执行高级的自定义通信功能,并依靠下面的基础 服务提供者,在网络上进行真正的数据交换。
为了进行内部用户对外访问控制,我们需要在现有的基础提供者TCP/IP提供者上设立一个分层式的URL过滤管理者。通过URL过滤管理者我们可以 截获用户请求的HTTP数据包中的URL地址,继而可以通过高效的数据检索算法(如利用Fibonacci散列函数的哈希表),在访问规则库(被禁止访问 的IP集合)中查找指定的IP,根据结果拒绝或提供访问服务。
传输服务提供者的安装方式决定了它不仅是一个分层提供者,还是一个基础服务提供者。Winsock 2使用系统配 置数据库配置传输服务提供者。配置数据库让Winsock2得知服务提供者的存在,并定义了提供的服务类型。要在Winsock2服务提供者数据库内成功 安装和管理服务提供者,需要四个函数:WSCEnumProtocols、WSCInstallProvider、WSCWriteProvider Order、WSCDeInstallProvider。这些函数利用WSAPROTOCOL_INFOW结构,对服务提供者数据库进行查询和操作。要安 装分层式服务提供者,需要建立两个WSPPROTOCOL_INFOW目录条目结构。一个代表分层提供者(协议链长度等于0),另一个将代表一个协议链 (协议长度大于1),该协议链把分层提供者与一个基础服务提供者链接起来。应该使用现有服务提供者的WSAPROTOCOL_INFOW目录条目结构的属 性来初始化这两个结构。调用WSCEnumProtocols可以获得已有的服务提供者的WSAPROTOCOL_INFOW目录条目结构。初始化之后, 首先需要使用WSCInstallProvider来安装我们的访问控制分层服务提供者目录条目,然后,利用WSCEnumProtocols列举出所有 的目录条目,获得安装之后为这个结构分配的目录ID。然后,用这个目录条目来设置一个协议链目录条目,通过它,将我们的访问控制服务提供者和另一个提供者 (TCP基础提供者)链接起来。然后再次调用WSCInstallProvider来安装我们的分层链式服务提供者。
在用WSCInstallProvider安装一个服务提供者时,目录条目自动成为配置数据库中的最后一个条目。要实现访问控制就必须使我 们的URL过滤服务提供者成为默认的TCP/IP提供者,必须通过调用WSCWriteProviderOrder函数来完成此项工作,对数据库中提供者 目录条目进行重新排序,并把协议链目录条目放在TCP/IP基础提供者之前。
Winsock2传输服务提供者随标准的Windows动态链接库模块一起执行。我们必须在我们的服务提供者动态链接库模块中导入 DLLMain函数,同时还必须导入一个名为WSPStartup的单一函数条目。我们的URL过滤服务提供者必须提供对WSPStartup函数和其他 30个SPI函数的支持。调用WSAStartup期间,Winsock根据WSASocket调用的地址家族、套接字类型和协议参数,来决定需要加载哪 个服务提供者。只有在一个应用程序通过socket或WSASocket API调用建立一个采用地址家族AF_INET、套接字类型为SOCK_STREAM的套接字时,Winsock才会搜索并加载与之相应的、能够提供 TCP/IP能力的传输服务提供者。WSPStartup的参数UpcallTable取得Ws2_32.dll的SPI函数派遣表,我们的访问控制分层 服务提供者利用这些函数来管理自身和Winsock2之间的I/O操作。
我们利用WSPConnect函数来实现访问控制功能。在用户请求HTTP服务时,需要首先建立与目标站点的连接,连接成功后,在此连接基 础上发送HTTP请求数据包。用户应用程序调用connect或WSAConnect函数建立连接时,SPI会调用对应的WSPConnect函数: INT WSPAPI WSPConnect(...,const struct sockaddr FAR *name,...,INT FAR *lpErrno)。在sockaddr类型的参数name中包含了用户将要访问的目标站点的IP地址信息。我们将name参数传递到IP可访问性判定例 程IPFilter。如果IPFilter函数返回代表授权访问的结果,我们采用协议链命令路由,调用下一层的基础服务提供者(TCP/IP)来完成连接 请求。如果IPFilter函数返回代表拒绝服务的结果,我们设置lpErrno参数为相应的错误码,然后返回,不进行协议链下一层服务提供者的调用,从 而实现访问控制。