水如烟

                 顺其自然,水到渠成 LzmTW

文或代码皆是面向初学者.我是爱好者,也是初学者.那些"文章",只按自己理解写,我是不知术语名词的.所以只供参考,也仅供参考.

posts - 94, comments - 807, trackbacks - 1, articles - 0

Author:水如烟

.NET的安全性问题是一个综合性的问题,这不是我现在能学习好理解透的,我估计也不是一般的.NET的编码者能够把握住应用好的。

至少,在代码集中,.NET的全局变量,或者说受Private修饰保护的对象,是不安全的。

在现实中有一个传话游戏,其要义是,信息在信息链的传递过程中会失真。.NET也一样,很难保证一个关键的信息在传递的过程中保持“纯洁”而不被潜伏中的攻击而受污染。特别是在大规模的代码集中。

SecureString类
在.NET的设计中,SecureString类应该是安全系数比较高的。它有两个作用,一是赋值后MakeReadOnly以便在后续的传递中不致修改,二是在必要的时候进行销毁。但是用它来传递关键信息,比如密码,还是不安全的。这个类里头有一个m_readOnly As Boolean的Private变量来保存当前实例可否修改的状态,你可以重置该值为False,便可以对它重新修改,MakeReadOnly也就失去了它本身的作用。

ReadOnlyCollection类
它的本义是不能对当前实例的项目进行增删。在它的设计中,用一个list的Private变量来存储当前集合。事实并不能保证它的作用。你甚至可以用自己的list替换了它。

测试代码

Imports System.Runtime.InteropServices
Imports System.Collections.ObjectModel

Public Class NoSecure

    
Public Sub TestSecureString()
        
Dim t As New Security.SecureString
        t.AppendChar(
"A"c)
        t.AppendChar(
"B"c)
        t.MakeReadOnly()

        Console.WriteLine(Marshal.PtrToStringAuto(Marshal.SecureStringToBSTR(t)))

        
Dim h As New LzmTW.uSystem.uReflection.TypeHelper(t)
        h.SetMemberValue(
"m_readOnly"False)

        t.AppendChar(
"C"c)

        Console.WriteLine(Marshal.PtrToStringAuto(Marshal.SecureStringToBSTR(t)))
    
End Sub

    
Public Sub TestReadonlyCollection()

        
Dim t As New ReadOnlyCollection(Of String)(New String() {"A""B""C"})
        
For i As Integer = 0 To t.Count - 1
            Console.Write(t.Item(i))
        
Next

        Console.WriteLine()

        
Dim h As New LzmTW.uSystem.uReflection.TypeHelper(t)
        
Dim mNowList As New List(Of String)
        mNowList.AddRange(
New String() {"A""B""C""D"})
        h.SetMemberValue(
"list", mNowList)

        
For i As Integer = 0 To t.Count - 1
            Console.Write(t.Item(i))
        
Next

    
End Sub

End Class

上面用到的 TypeHelp类:

TypeHelper.Methods.vb
Imports System.Reflection

Namespace LzmTW.uSystem.uReflection

    Partial 
Class TypeHelper

        
Public Sub SetCurrentObj(ByVal obj As Object)
            
If Not obj Is Nothing AndAlso Not Me.CurrentType.IsInstanceOfType(obj) Then
                
Throw New ArgumentException("实例类型与内部类型不相符")
            
End If

            
Me.gCurrentObjct = obj
        
End Sub

        
Public Function GetMemberValue(ByVal name As StringByVal ParamArray args() As ObjectAs Object
            
Return Me.CurrentType.InvokeMember( _
                name, _
                MemberGetBinding, _
                
Nothing, _
                
Me.CurrentObject, _
                args)
        
End Function


        
Public Sub SetMemberValue(ByVal name As StringByVal ParamArray args() As Object)
            
Me.CurrentType.InvokeMember( _
                name, _
                MemberSetBinding, _
                
Nothing, _
                
Me.CurrentObject, _
                args)
        
End Sub

        
Public Function MethodInvoke(ByVal name As StringByVal ParamArray args() As ObjectAs Object
            
Return Me.CurrentType.InvokeMember( _
                name, _
                MethodBinding, _
                
Nothing, _
                
Me.CurrentObject, _
                args)
        
End Function

        
Public Function NewInstance(ByVal ParamArray args() As ObjectAs Object
            
Dim mParaCount As Integer = args.Length
            
Dim mCtors As ConstructorInfo() = Me.CurrentType.GetConstructors(MethodBinding)

            
For Each ctro As ConstructorInfo In mCtors
                
If ctro.GetParameters.Length = mParaCount Then
                    
Return ctro.Invoke(args)
                
End If
            
Next

            
Return Nothing
        
End Function

        
''' <summary>
        ''' 可以使用*?[abc][!abc],忽略大小写
        ''' </summary>
        Public Function FindMember(ByVal name As StringAs MemberInfo()
            
If String.IsNullOrEmpty(name) OrElse name = "*" Then
                
Return Me.CurrentType.GetMembers(Binding)
            
End If

            
Dim mPattern As String = "*[*?]*"
            
If Not name Like mPattern Then Return Me.CurrentType.GetMember(name, Binding)

            
Dim mArray As New List(Of MemberInfo)
            
For Each m As MemberInfo In Me.CurrentType.GetMembers(Binding)
                
If m.Name.ToLower Like name.ToLower Then
                    mArray.Add(m)
                
End If
            
Next

            
Return mArray.ToArray
        
End Function

        
Private MemberGetBinding As BindingFlags = _
                BindingFlags.Instance 
Or _
                BindingFlags.Public 
Or _
                BindingFlags.NonPublic 
Or _
                BindingFlags.Static 
Or _
                BindingFlags.GetField 
Or _
                BindingFlags.GetProperty 
Or _
                BindingFlags.IgnoreCase

        
Private MemberSetBinding As BindingFlags = _
                BindingFlags.Instance 
Or _
                BindingFlags.Public 
Or _
                BindingFlags.NonPublic 
Or _
                BindingFlags.Static 
Or _
                BindingFlags.SetField 
Or _
                BindingFlags.SetProperty 
Or _
                BindingFlags.IgnoreCase

        
Private MethodBinding As BindingFlags = _
                BindingFlags.Instance 
Or _
                BindingFlags.Public 
Or _
                BindingFlags.NonPublic 
Or _
                BindingFlags.Static 
Or _
                BindingFlags.InvokeMethod 
Or _
                BindingFlags.IgnoreCase
    
End Class

End Namespace


TypeHelper.vb
Imports System.Reflection

Namespace LzmTW.uSystem.uReflection

    
Public Class TypeHelper

        
Private gType As Type
        
Private gCurrentObjct As Object

        
Public ReadOnly Property CurrentType() As Type
            
Get
                
Return gType
            
End Get
        
End Property

        
Public ReadOnly Property CurrentObject() As Object
            
Get
                
Return gCurrentObjct
            
End Get
        
End Property


        
Sub New(ByVal referrenceObj As Object)
            
If Me.IsType(referrenceObj) Then

                
Me.gType = CType(referrenceObj, Type)
            
Else

                
Me.gType = referrenceObj.GetType
                
Me.gCurrentObjct = referrenceObj
            
End If
        
End Sub

        
Sub New(ByVal assembly As AssemblyByVal fullTypeName As String)
            
Me.InternalCreate(assembly, fullTypeName)

            
Me.InternalCheckIsValid(fullTypeName, True)
        
End Sub

        
Sub New(ByVal referrenceType As Type, ByVal typeName As StringOptional ByVal isNestedType As Boolean = False)

            
Dim mAssembly As Assembly = referrenceType.Assembly

            
Me.InternalCreate(mAssembly, typeName)

            
If Me.InternalCheckIsValid(typeNameFalseThen Return

            
Dim mFullTypeName As String = GetFullTypeName(referrenceType, typeName, isNestedType)

            
Me.InternalCreate(mAssembly, mFullTypeName)

            
Me.InternalCheckIsValid(mFullTypeName, True)
        
End Sub

        
Private Sub InternalCreate(ByVal ass As AssemblyByVal fulltypename As String)
            gType 
= ass.GetType(fulltypename, FalseTrue)
        
End Sub

        
Private Function InternalCheckIsValid(ByVal typename As StringByVal throwOnError As BooleanAs Boolean
            
If gType Is Nothing Then
                
If throwOnError Then
                    
Throw New ArgumentException(String.Format("typeName: {0} 不存在"typename))
                
Else
                    
Return False
                
End If
            
End If

            
Return True
        
End Function

        
Private Function GetFullTypeName(ByVal referrenceType As Type, ByVal typeName As StringByVal isNestedType As BooleanAs String
            
Dim mFullName As String = Nothing

            
Dim mRefFullName As String = referrenceType.FullName

            
If isNestedType Then

                mFullName 
= String.Concat(mRefFullName, "+"typeName)
            
Else

                
Dim mLastIndex As Integer = mRefFullName.LastIndexOf(referrenceType.Name)

                mFullName 
= String.Concat(mRefFullName.Substring(0, mLastIndex), typeName)
            
End If

            
Return mFullName
        
End Function

        
Private Function IsType(ByVal instance As ObjectAs Boolean
            
Return instance.GetType.IsSubclassOf(GetType(Type))
        
End Function

        
Public Const Binding As BindingFlags = _
            BindingFlags.Instance 
Or _
            BindingFlags.Public 
Or _
            BindingFlags.NonPublic 
Or _
            BindingFlags.Static 
Or _
            BindingFlags.CreateInstance 
Or _
            BindingFlags.IgnoreCase


    
End Class

End Namespace

Feedback

#1楼    回复  引用  查看    

2007-11-20 12:03 by 编写人生      
问题在于你提供别人使用代码访问你的实例,例如使用插件或脚本,你是否将他们隔离到另外一个应用程序域,这是一个基本的问题。
理论上,所有的语言在设计上是信任本进程的程序的,所以你看见的VBA等系统他们允许你编写的代码都是在额外的应用程序域里。
.NET 3.5提供了Addin类,可以方便的提供此功能。

#2楼    回复  引用    

2007-11-20 12:07 by fawfa [未注册用户]
Web application,WCF等不受此影响。
Windows程序自然得做更安全的构想。

#3楼 [楼主]   回复  引用  查看    

2007-11-20 12:11 by 水如烟(LzmTW)      
重新提出这个话题,是因为想到规模较大时的代码监控。

十年前吧,有一个案例,说是某银行因一个程序职员的辞职而使系统瘫痪,因为他在代码中留下了“炸弹”。

随着代码工程规模的不断扩大,相似的案例会存在。

#4楼    回复  引用  查看    

2007-11-20 12:51 by 双鱼座      
@水如烟(LzmTW)
没明白“代码集”是什么概念?是“程序集(Assembly)”么?
你说的案例和.net有关么?安全性是一个相对的话题。只要有机会接触到你的程序实例,一定有机会修改的,特别是现如今的调试技术越来越先进。这个与.net没有关系。

#5楼    回复  引用  查看    

2007-11-20 12:53 by Bao      
剪刀也是可以杀人的,不用剪刀吗?

#6楼 [楼主]   回复  引用  查看    

2007-11-20 12:59 by 水如烟(LzmTW)      
编写人生 说了,想安全首先隔离到另外一个应用程序域。能不能切入到任何一个程序域,我现在做不到(但别人可以做到,我曾下了一个,好象用C编的,我忘了),所以我只能说是代码,代码集的意思可以理解为源代码集。

在大型的工程中,下一流水线可以修改上一流水线交给的“产品”,或者说,我有作弊的能力,就看我有没有这个动机和行动了。

我不是专业的,无法用术语来表达我的思想。真不好意思。

#7楼 [楼主]   回复  引用  查看    

2007-11-20 13:02 by 水如烟(LzmTW)      
实际些:

源代码的审核是必不可少的,不看源代码,单是用测试工具来验BUG,是不可信的。

#8楼    回复  引用    

2007-11-20 14:47 by longer [未注册用户]
看VB看得头晕

#9楼    回复  引用    

2007-11-20 15:17 by A1 [未注册用户]
任何单纯靠阻止/隔离访问的方式实现的安全都是相对的,你不会指望编出系统内核保护那个级别的安全吧?
安全不在于保护逻辑的某一要件不被伪造或篡改,而在于保证能发现这些行为,如果同时能保证让破坏者不知道你怎么发现的,并都作了哪些相应的对策,让他的破坏行为变得像是在篡改历史一样,即时他能让当时一切看上去太平,但再某个时日就会发现世界已经变得不是他想的那样了。

至于防内鬼?最难防了。

#10楼    回复  引用  查看    

2007-11-20 15:42 by 妖居      
感觉水兄怎么有点逻辑混乱了?如果说防治.Net程序发布后被恶意修改,那么估计现在所有的程序都有被破解的可能。这个并不是.Net自己的问题。就看我们的程序在什么样的安全级别下面运行了。运行于局域网内部,企业内部的软件,可能安全级别稍低一点。运行于外网则要多一些安全工作。但是绝对的安全是没有的,我觉得。
如果说防止我的程序结果或者实例的内容被别的开发者非正常修改。比如我的一个string是私有的,只有通过我的类才能改。但是另外一个开发者为了开发省事,用了别的方式修改了这个值。那么这个东西好像就不属于安全问题了,因该是那个developer的开发方式,开发思维有问题。

#11楼 [楼主]   回复  引用  查看    

2007-11-20 16:09 by 水如烟(LzmTW)      
TO @妖居

你的表达还真清晰。是我糊涂,一是不善于表达,二是把握不住自己的思维,易“飞跃”。

#12楼    回复  引用  查看    

2007-11-20 17:22 by 装配脑袋      
不给Full trust权限,代码就不能反射Private的东西。有时候CAS也应该好好利用一下

#13楼    回复  引用  查看    

2007-11-20 17:38 by Cat Chen      
private和readonly提供的是设计时的安全,避免你无意修改,不是执行时保密与防篡改。如果你要说执行时安全,那么没必要提及private和readonly,这完全不是同一个话题内的吧。

#14楼 [楼主]   回复  引用  查看    

2007-11-20 17:39 by 水如烟(LzmTW)      
TO @装配脑袋

你都跑哪去了?
我在论坛上说了一句话,有感而发:

忽尔有感

使用.NET也有三四年了吧,可惜不是吃这行饭,要不应该有进步。现在它还是一个小巧灵珑的玩具,想拆拆看看里面的东西,可确实的闲心难续。
三四年前的一帮人,是今多有拥着金山挥洒宇宙,自己呢,手数硬币自得其乐。

#15楼 [楼主]   回复  引用  查看    

2007-11-20 17:43 by 水如烟(LzmTW)      
TO @Cat Chen:

本该如你所说的。

所以呢,我要重新理解MSDN文档中的说明。它的说明是针对设计时的。

#16楼    回复  引用  查看    

2007-11-20 18:07 by 装配脑袋      
@水如烟(LzmTW)

其实我一直在附近潜水的说。。。

#17楼    回复  引用  查看    

2007-11-21 13:32 by G yc {Son of VB}      
羡慕啊~楼主对反射这么了解~~

我个人认为,安全是很大的一个话题,
从某一角度说,防止安全的是人,破坏安全的也是人。
因此,我觉得我们做开发的,要尽可能的保持程序的安全。
也要加上,一些检验机制,如果必要的话

事实上,对上面所作的,感觉像游戏修改器一样。
但是,现在有的游戏可以进行防止修改(虽然能被破解),
个人认为,可以用上面的代码,检查程序的安全。

一下仅代表我个人观点
如果攻击者要泄露数据,尝试在修改程序集,加入代码的话,也就会破坏程序的结构了,虽然可以,不过还不如来个反编译,比较快。
想楼上说的,如果是使用插件的话,本是就需要开启反射,那么,安全问题也就来了,攻击者可以够找恶意的插件,读取内部变量等。

对于攻击者,攻击应该是有目的的,理由可以很简单,也可以很复杂(比如靠黑客吃饭的),没有获得好处或意义的话,攻击者也不会做的,所以小程序是不会被攻击。但对于大程序,安全,就变成一个很复杂的问题。在易用性和安全间作出合理的选择。

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-11-20 11:53 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: