Remoting安全可以从三方面进行考虑
权限验证、防火墙技术、数据流加密
一、权限验证
权限验证----------,我们可以在Remoting中加入CallContext信息实现类似于HTTP信息头的东西进行检测。MSDN中对CallContext的介绍如下:
Callcontext它提供与执行代码路径一起传送的属性集,CallContext是类似于方法调用的线程本地存储的专用集合对象,并提供对每个逻辑执行线程都唯一的数据槽。数据槽不在其他逻辑线程上的调用上下文之间共享。当 CallContext 沿执行代码路径往返传播并且由该路径中的各个对象检查时,可将对象添加到其中。当对另一个 AppDomain 中的对象进行远程方法调用时,CallContext 类将生成一个与该远程调用一起传播的 LogicalCallContext 实例。只有公开 ILogicalThreadAffinative 接口并存储在 CallContext 中的对象被在 LogicalCallContext 中传播到 AppDomain 外部。不支持此接口的对象不在 LogicalCallContext 实例中与远程方法调用一起传输。
CallContext.SetData方法存储给定对象并将其与指定名称关联,CallContext.GetData方法从 CallContext 中检索具有指定名称的对象。
例如:系统中需要登陆验证,在下面的例子中我们可以通过CheckConnectUser进行登录验证。
[Serializable]
public class CheckLgoinContext: ILogicalThreadAffinative
{
public string userName;
public string userPass;
public CheckLgoin(string UserName, string UserPass)
{
this.userName = UserName;
this.userPass = UserPass;
}
}
客户端使用方法
CheckLgoinContext checkContext = new CheckLgoinContext ("User", "1");
CallContext.SetData("CheckUser", checkContext);
服务端获取方法
private bool CheckConnectUser()
{
CheckLgoinContext checkContext = CallContext.GetData("CheckUser") as CheckLgoin;
if (checkContext == null) return false;
string userName = checkContext.UserName;
string userPass = checkContext.UserPass;
Console.WriteLine("请求登陆名:{0},密码:{1}", userName, userPass);
if (userName.Equals("User") && userPass.Equals("1"))
{
return true;
}
else
{
return false;
}
}
二、防火墙技术(实现IP地址过滤 )
在Remoting 中我们可以实现类似于IP防火墙的技术实现指定IP进行访问,当客户端和服务端的IP地址相对固定的时候我们可以通过IP过滤进行限制访问。对于IP地址的过滤我们通过IAuthorizeRemotingConnection接口进行限制。
IAuthorizeRemotingConnection
接口提供了一些方法,可根据客户端的网络地址和用户标识来指示客户端是否被授权连接至当前信道
获取一个布尔值,该值指示客户端的网络地址是否已被授权连接至当前信道。 | |
获取一个布尔值,该值指示客户端的用户标识是否已被授权连接至当前信道。 |
将 IAuthorizeRemotingConnection 接口的实例传递到 TcpServerChannel(IDictionary, IServerChannelSinkProvider, IAuthorizeRemotingConnection)。它为 TcpServerChannel 上所有传入连接提供了单个授权点。在所有传入消息均被反序列化,从而将来自不受信任的源的安全威胁降到最低之前,它允许对调用方授权。
public class AuthorizationModule : IAuthorizeRemotingConnection
{
bool IAuthorizeRemotingConnection.IsConnectingEndPointAuthorized(EndPoint endPoint)
{
Console.WriteLine(endPoint); //连接的IP
return true;
}
bool IAuthorizeRemotingConnection.IsConnectingIdentityAuthorized(.IIdentity identity)
{
Console.WriteLine(identity.Name); //连接用户计算机名
return true;
}
}
服务端配置示例:
IDictionary TcpChannlDic = new Hashtable();
TcpChannlDic["port"] = 7788;
TcpChannlDic["name"] = "MyTcpServer";
IServerChannelSinkProvider ServerProvider = new BinaryServerFormatterSinkProvider();
TcpServerChannel ch=newTcpServerChannel(TcpChannlDic, ServerProvider,
(IAuthorizeRemotingConnection)new AuthorizationModule());//IP验证
三、数据流加密
在基于消息的远程方法调用中主要有以下几个重要角色:
l Client Proxy: 负责在客户端处理基于栈的参数传递模式和基于消息的参数传递模式之间的转换。
l Requestor: 负责将消息对象转换成可在网络上传输的数据流,并将其发送到服务器。
l Invoker:与Client Proxy的功能相反。
l Marshaller: 负责消息对象的序列化与反序列化。
l Client Request Handle:负责以数据流的格式发送客户端的请求消息。
l Server Request Handel:负责接收来自客户端的请求消息。
从上图中我们可以看到客户端的Transparent Proxy与服务器端的StackBuilderSink分别扮演了Client Proxy与Invoker的角色。Remoting依靠这两个对象实现了基于栈的方法调用与基于消息的方法调用的转换,并且这一过程对于开发者是完全隐藏的。
Marshaller的角色由Formmatter Sink完成,在Remoting中默认提供了两种Fommatter:一个实现了消息对象与二进制流的相互转换,另一个实现了消息对象与Soap数据包的相互转换,而支持Soap格式则说明Remoting具有实现Web Service技术的可能。
Client Request Handle与Server Request Handel都是.NET中实现网络底层通讯的对象,如HttpWebRequest、HttpServerSocketHandler等。在Remoting中我们并不直接接触这些对象,而是通过Channel对它们进行管理。在框架中默认提供了三种Channel:HttpChannel、TcpChannel与IpcChannel。但是在上面这副图中,我们并没有看到Channel对象,那么Channel又是如何影响网络底层的通讯协议的呢?
真正能对通讯时所采用的网络协议产生影响的元素是Transport Sink,对应不同的协议Remoting框架中提供了三种共计六个Transport Sink:HttpClientTransportSink、HttpServerTransportSink与TcpClientTransportSink、TcpServerTransportSink以及IpcClientTransportSink、IpcServerTransportSink,它们分别放置在客户端与服务器端。
综上所述我们可以通过在数据出站前进行加密,在接收到信息之后进行解密。
IClientChannelSink 接口
为客户端信道接收器提供所需的函数和属性。
信道接收器提供插件点,该插件点允许访问流过该信道的基础消息,还允许访问传输机制用来将消息发送到远程对象的流。信道接收器在信道接收器提供程序链中链接在一起,在消息被序列化并传输之前,所有信道消息都流过该接收器链。
internal class CustomSink : IClientChannelSink
{
IClientChannelSink nextSink;
private CustomSink(object nextSink)
{
this.nextSink = nextSink as IClientChannelSink;
}
... IClientChannelSink接口实现...
public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream)
{
//数据加密
nextSink.ProcessMessage(msg, requestHeaders, requestStream, out responseHeaders, out responseStream);
//数据解密
}
}
IServerChannelSink接口
提供用于安全和传输接收器的方法。
信道接收器提供插件点,该插件点允许访问流过该信道的基础消息,还允许访问传输机制用来将消息发送到远程对象的流。信道接收器在信道接收器提供程序链中链接在一起,并且所有信道消息在被序列化和传输之前都要流过此接收器链。
internal class CustomSink : IserverChannelSink
{
IServerChannelSink nextSink;
private CustomSink(object nextSink)
{
this.nextSink = nextSink as IServerChannelSink;
}
... IserverChannelSink接口实现...
` ServerProcessing IServerChannelSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders,
Stream requestStream, out IMessage responseMsg,
out ITransportHeaders responseHeaders, out Stream responseStream)
{
//接收数据后解密
ServerProcessing process = nextSink.ProcessMessage(sinkStack, requestMsg, requestHeaders, requestStream, out responseMsg, out responseHeaders, out responseStream);
//处理完成后数据加密回传至客户端
return process;
}
}
IClientChannelSinkProvider 接口
信道接收器通过 IClientChannelSinkProvider 接口的实现连接到客户端信道。所有远程处理客户端信道均提供以 IClientChannelSinkProvider 为参数的构造函数。
信道接收器提供程序存储在一个链中,在向信道构造函数传递外部信道接收器提供程序之前,用户负责将所有的信道接收器提供程序链接在一起。为此,IClientChannelSinkProvider 提供了名为 Next 的属性。
IClientChannelSinkProvider.Next 获取或设置信道接收器提供程序链中的下一个接收器提供程序。
IServerChannelSinkProvider 接口
信道接收器通过 IServerChannelSinkProvider 接口的实现连接到服务器信道。所有远程处理服务器信道均提供以 IServerChannelSinkProvider 为参数的构造函数。
信道接收器提供程序存储在一个链中,在向信道构造函数传递外部信道接收器提供程序之前,用户负责将所有的信道接收器提供程序链接在一起。为此,IServerChannelSinkProvider 提供了名为 Next 的属性。
IServerChannelSinkProvider.Next 获取或设置信道接收器提供程序链中的下一个接收器提供程序。
public class CustomProvider : IServerChannelSinkProvider, IclientChannelSinkProvider
{
private object NextSink;
public IServerChannelSink CreateSink(IChannelReceiver channel)
{
IServerChannelSink nextServerSink = null;
if (NextSink != null)
nextServerSink = (NextSink as IServerChannelSinkProvider).CreateSink(channel);
return new CustomSink(nextServerSink);
}
public void GetChannelData(IChannelDataStore channelData)
{
}
public IServerChannelSinkProvider Next
{
get{ return NextSink as IServerChannelSinkProvider; }
set{ NextSink = value; }
}
public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData)
{
IClientChannelSink nextClientSink = null;
if (NextSink != null)
{
nextClientSink = (NextSink as IClientChannelSinkProvider).CreateSink(channel, url, remoteChannelData);
if (nextClientSink == null)
{
return null;
}
}
return new CustomSink(nextClientSink);
}
IClientChannelSinkProvider IClientChannelSinkProvider.Next
{
get{ return NextSink as IClientChannelSinkProvider; }
set{NextSink = value; }
}
}