C#安全性之代码访问安全和角色安全小记
2011-07-12 07:57
323 查看
本章概要:
1:FRAMEWORK安全性中的几个概念
1.1:安全权限
1.2:类型安全和安全性
1.3:安全策略
1.4:身份验证
2:代码访问安全之声明式安全性
3:代码访问安全之强制安全性
4:代码访问安全之请求权限
4.1:请求访问非托管代码的权限
4.2:通过使用RequestMinimum标志请求最小权限
4.3:通过使用RequestOptional标志请求可选权限
4.4:通过使用RequestRefuse标志拒绝授予权限
4.5:请求命名权限集的权限
5:基于角色的安全性
5.1:主体和标识对象
5.2:PrincipalPermission对象
5.3基于角色的安全检查
5.3.1如何:执行命令性安全检查
5.3.2执行声明式安全检查
5.3.3直接访问主体对象
代码访问权限,此权限表示对受保护资源的访问权或执行受保护操作的能力。
标识权限,此权限指示代码具有支持特定类型的标识的凭据。
例如,某个权限表示程序集必须具有的强名称,而另一权限表示代码必须源自的网站,诸如此类等等。由于标识权限具有一组与代码访问权限通用的功能,故它们与代码访问权限CodeAccessPermission从同一基类派生.
基于角色的安全性权限,此权限提供一种机制来确定用户(或代表用户的代理)是具有特定标识还是指定角色的成员。PrincipalPermission是唯一的基于角色的安全性权限。
SkipVerification授予SecurityPermission后才能运行。
FileIOPermission。如果代码不请求FileIOPermission,而且本地安全设置不允许应用程序拥有此权限,则会在应用程序尝试向磁盘写入时引发安全性异常。即使应用程序能够处理此异常,也不会允许它向磁盘写入。如果您的应用程序是文本编辑程序,而用户已经使用了很长一段时间,则此行为会使用户很失望。另一方面,如果应用程序请求FileIOPermission,而本地安全设置不允许您的应用程序拥有FileIOPermission,则应用程序将在启动时生成异常,用户不会遇到丢失任何工作的问题。另外,如果您的应用程序请求FileIOPermission并且它是受信任的应用程序,则管理员可以调整安全策略来允许它从远程共享执行。
下表描述权限请求的类型。
最小权限(RequestMinimum):您的代码要运行必须拥有的权限。
可选的权限(RequestOptional):您的代码可以使用的权限,但在没有这些权限时代码仍可有效运行。此请求隐式拒绝未明确请求的所有其他权限。
拒绝的权限(RequestRefuse):您要确保永远不授予您的代码的权限(即使安全策略允许将它们授予您的代码)。
对内置权限集执行上述任何请求(请求内置权限集):内置的权限集包括:Nothing、Execution、FullTrust、Internet、LocalIntranet和SkipVerification。
对XML编码的权限集执行上述任何请求(请求XML编码的权限):所需权限集的XML表示形式(或者是包含XML编码的权限集的字符串,或者是包含编码的权限集的XML文件的位置)。
SecurityPermissionAttribute并指定两个值:指定所发出权限请求的类型的SecurityAction值(在此例中为RequestMinimum)和指示所请求权限的标志。在此例中,SecurityPermissionFlag.UnmanagedCode指定请求非托管代码权限。assembly:语法通知编译器在程序集级别应用该属性。
如果上面的代码未接收到具有UnmanagedCode标志的SecurityPermission,则运行库将引发一个PolicyException并且不允许该代码执行。但是,如果代码一旦获得该权限,则允许该代码执行。
FileIOPermissionpdf。如果尚未被授予请求的权限,此示例将不会执行。此示例假定在LogNameSpace中存在一个假设的类Log。Log类包含可在本地计算机上创建新的日志文件的MakeLog方法。此应用程序创建Log类的一个新实例,并在try块中执行MakeLog方法。使用catch关键字,它可以截获引发的任何SecurityException并显示消息。
如果上面的代码有足够的权限,它会创建日志文件并将下面的消息显示到控制台上:
TheLoghasbeencreated.
如果从共享位置运行该代码,并且本地安全设置不允许此类代码拥有FileIOPermission,则不会授予该代码足够的权限,并显示下面的消息:
Thisapplicationdoesnothavepermissiontowritetothedisk.
SecurityAction.RequestOptional标志允许您请求一组权限,而拒绝所有其他权限(如果不拒绝,运行库将授予这些权限)。相反,RequestRefuse标志允许您通过明确指定不应该向您的代码授予哪些权限来拒绝权限。
与使用RequestMinimum标志相比,如果应用程序没有接收到使用RequestOptional标志请求的所有权限,则您的应用程序将执行,当该应用程序试图访问受保护的资源时,将引发SecurityException。如果您使用此类型的请求,必须使您的代码能够捕捉在未授予代码可选权限的情况下引发的任何异常。
下面的示例使用SecurityAction.RequestOptional标志请求FileIOPermission,间接拒绝所有其他权限。此示例假定在LogNameSpace中存在一个假设的类Log。Log类包含可在本地计算机上创建新的日志文件的MakeLog方法。此应用程序创建Log类的一个新实例,并在try块中执行MakeLog方法。使用catch关键字,它可以截获引发的任何SecurityException并显示消息。
如果上面的代码有足够的权限,它会创建日志文件并将下面的消息显示到控制台上:
TheLoghasbeencreated.
如果从共享位置运行该代码,并且本地安全设置不允许此类代码拥有FileIOPermissionpdf ,则不会授予该代码足够的权限,并显示下面的消息:
Thisapplicationdoesnothavepermissiontowritetothedisk.
RequestRefuse允许将大量权限作为可选的权限来请求,同时确保某些特定的权限不在授予之列。
下面的示例使用RequestRefuse来拒绝来自公共语言运行库安全系统的FileIOPermission:
上面的示例没有被授予创建文件的权限,将生成安全性异常。捕捉语句截获异常,同时应用程序将下面的消息显示到控制台上:
Thisapplicationdoesnothavepermissiontowritetothedisk.
PermissionSetAttribute与表示所需权限集的名称的Name值连接起来。
GenericIdentity对象和一个更专用的WindowsIdentity对象;前者可用于大多数自定义登录方案,而后者可用于在希望应用程序依赖于Windows身份验证的情况中。此外,您还可以定义自己的标识类来封装自定义用户信息。
IIdentity接口定义用于访问名称和身份验证类型(如KerberosV5或NTLM)的属性。所有Identity类均实现IIdentity接口。Identity对象同执行当前线程所用的WindowsNT进程标记之间不需要有什么关系。但是,如果Identity对象是WindowsIdentity对象,则假定标识表示WindowsNT安全标记。
GenericPrincipal对象和WindowsPrincipal对象。您还可以定义自己的自定义主体类。
PrincipalPermission)表示特定主体类在运行时必须具有的标识和角色。以命令方式和声明方式进行的安全检查均可使用PrincipalPermission类。
若要以命令方式实现PrincipalPermission类,请创建该类的一个新实例,并用希望用户在访问代码时具有的名称和角色来初始化该实例。例如,以下代码以“"Joan"”身份和“"Teller"”角色对此对象的一个新实例进行初始化。
可使用PrincipalPermissionAttribute类,以声明方式创建一个类似的权限。以下代码以声明方式将身份初始化为“Joan”,并将角色初始化为“Teller”。
执行安全检查时,为成功完成检查,指定的身份和角色必须匹配。但是,创建PrincipalPermission对象时,可传递一个空的身份字符串以指示主体的身份可以是任意的。同样,传递一个空的角色字符串指示主体可以是任何角色的成员(或根本不属于任何角色)。对于声明的安全性,可通过省略两种属性之一来获得相同的效果。例如,下列代码使用PrincipalPermissionAttribute以声明方式指示主体可以具有任意名称,但必须具有出纳的角色。
pdf可采用下列方法之一对其进行安全检查:
使用命令式安全检查。
使用声明性安全检查。
直接访问Principal对象。
托管代码可使用命令式或声明式安全检查来确定以下内容:特定主体对象是否是已知角色的成员,是否具有已知的身份,或者是否表示一种角色中的一个已知身份。若要通过命令式或声明式安全性进行安全检查,必须对适当构造的PrincipalPermission对象生成一个安全请求。安全检查期间,公共语言运行库检查调用方的主体对象,确定其身份和角色是否与所请求的PrincipalPermission表示的身份和角色相匹配。如果主体对象不匹配,则引发SecurityException。(只检查当前线程的主体对象;PrincipalPermission类不会像代码访问权限那样导致产生堆栈遍历。)
此外,可以直接访问主体对象的值,并在不使用PrincipalPermission对象的情况下执行检查。在这种情况下,只需读取当前线程主体的值或使用IsInRole方法执行身份验证。
5.3.1如何:执行命令性安全检查
对于命令式请求,可以调用PrincipalPermission对象的Demand方法来确定当前主体对象是否表示指定的身份、角色或同时表示两者。假定有一个适当构造的PrincipalPermission对象(称为MyPrincipalPermission),可使用以下代码调用命令式请求。
下面的代码示例使用命令式检查确保GenericPrincipal同PrincipalPermission对象匹配。当应用程序域中的许多方法或其他程序集必须做出基于角色的决定时,命令式检查是有用的。尽管此示例极其简单,但它阐明了与基于角色的请求相关的行为。
如果用户键入1,则将创建访问PrivateInfo方法所需的主体和标识对象。如果用户键入任何其他字符,则不会创建主体和标识对象,并且在调用PrivateInfo方法时,会引发一个安全异常。如果当前线程与一个名称为MyUser、角色为Administrator的主体关联,则会出现下面的消息。
5.3.2执行声明式安全检查
PrincipalPermissionpdf的声明式请求的工作方式同代码访问权限的声明式请求相同。请求既可放在类级别上,也可放在个别方法、属性或事件上。如果声明式请求同时放在类级别和成员级别上,则成员上的声明式请求将覆盖(或取代)类级别上的请求。
下面的代码示例显示了前一节示例中的PrivateInfo方法经过修改后的版本。此版本使用声明式安全性。PrincipalPermissionAttribute定义当前线程在调用方法时必须具有的主体。仅用所需的名称和角色传递SecurityAction.Demand。
如果当前线程不包含正确的主体,此方法将引发安全异常。如果用户输入1,则将调用PrivateInfo方法,并将下面的消息显示到控制台。
Youhaveaccesstotheprivatedata!
5.3.3直接访问主体对象
尽管使用命令式和声明式请求调用基于角色的安全检查是检查和强制身份与角色成员条件的主要机制,但在某些情况下,您可能希望直接访问Principal对象及其关联的Identity对象来完成身份验证任务,而不必创建权限对象。例如,如果不希望验证失败时的默认行为是引发异常,则可能不想使用声明式或命令式请求。这时,可以在System.Threading.Thread类上使用静态CurrentPrincipal属性来访问Principal对象并调用其方法。
获取主体对象后,可以使用条件语句根据主体名来控制对代码的访问,如下面的代码示例所示。
还可通过在当前Principal对象上调用IsInRole方法,以编程方式检查角色成员条件,如以下代码实例所示。
当希望访问应用程序定义的Principal对象特定的行为时可以使用此方法。但是,在多数情况下,应使用PrincipalPermission类根据身份或角色成员条件来控制对代码的访问。
下面的代码示例将创建一个WindowsPrincipal对象和一个WindowsIdentity对象,将它们设置给当前用户,并根据Principal的值做出安全决策。此代码不以命令或声明的方式使用PrincipalPermission对象,而是根据主体对象的值做出访问决定。
如果当前用户是pdfMYDOMAIN\myuser,此程序将向控制台显示以下消息。
HelloMYDOMAIN\myuser,youareauthenticated!
但是,如果当前用户是任何其他用户,则程序将显示以下消息。
Goaway!Youarenotauthorized!
MyPrincipal.Identity.Name中的值显示代表授权帐户的域和用户名。请注意,在C#中,字符串"MYDOMAIN\myuser"以at符号(@)为前缀,这样就不会将反斜杠解释为转义符。尽管前面的示例使用WindowsIdentity对象,但仍可使用一般对象轻松产生类似的代码。只需创建一般对象的实例,向其传递所需的值,然后就可以在该对象中检查那些值。
练习:
1.oucreateamethodthatrunsbyusingthecredentialsoftheenduser.YouneedtouseMicrosoftWindows
groupstoauthorizetheuser.YoumustaddacodesegmentthatidentifieswhetherauserisinnamedClerk.Whichcodesegmentshouldyouuse?
A.WindowsIdentitycurrentUser=WindowsIdentity.GetCurrent();
foreach(IdentityReferencegrpincurrentUser.Groups)
{NTAccountgrpAccount=((NTAccount)grp.Translate(typeof(NTAccount)));
isAuthorized=grpAccount.Value.Equals(Environment.MachineName+@"\Clerk");
if(isAuthorized)break;}
B.WindowsPrincipalcurrentUser=(WindowsPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole("Clerk");
C.GenericPrincipalcurrentUser=(GenericPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole("Clerk");
D.WindowsPrincipalcurrentUser=(WindowsPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole(Environment.MachineName);
Answer:pdf
2.Youarewritingcodeforuserauthenticationandauthorization.Theusername,password,androlesarestoredin
yourapplicationdatastore.Youneedtoestablishausersecuritycontextthatwillbeusedforauthorization
checkssuchasIsInRole.Youwritethefollowingcodesegmenttoauthorizetheuser.
if(!TestPassword(userName,password))
thrownewException("couldnotauthenticateuser");
string[]userRolsesArray=LookupUserRoles(userName);
Youneedtocompletethiscodesothatitestablishestheusersecuritycontext.Whichcodesegmentshouldyou
use?
A.GenericIdentityident=newGenericIdentity(userName);
GenericPrincipalcurrentUser=newGenericPrincipal(ident,userRolesArray);
Thread.CurrentPrincipal=currentUser;
B.WindowsIdentityident=newWindowsIdentity(userName);
WindowsPrincipalcurrentUser=newWindowsPrincipal(ident);
Thread.CurrentPrincipal=currentUser;
C.NTAccountuserNTName=newNTAccount(userName);
GenericIdentityident=newGenericIdentity(userNTName.Value);
GenericPrincipalcurrentUser=newGenericPrincipal(ident,userRolesArray);
Thread.CurrentPrincipal=currentUser;
D.IntPtrtoken=IntPtr.Zero;token=LogonUserUsingInterop(userName,encryptedPassword);
WindowsImpersonationContextctx=WindowsIdentity.Impersonate(token);
Answer:A
3.Youaredevelopinganapplicationthatwillusecustomauthenticationandrole-basedsecurity.Youneedto
writeacodesegmenttomaketheruntimeassignanunauthenticatedprincipalobjecttoeachrunningthread.
Whichcodesegmentshouldyouuse?
A.AppDomaindomain=AppDomain.CurrentDomain;
domain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
B.AppDomaindomain=AppDomain.CurrentDomain;
domain.SetThreadPrincipal(newWindowsPrincipal(null));
C.AppDomaindomain=AppDomain.CurrentDomain;
domain.SetAppDomainPolicy(PolicyLevel.CreateAppDomainLevel());
D.AppDomaindomain=AppDomain.CurrentDomain;
domain.SetPrincipalPolicy(PrincipalPolicy.UnauthenticatedPrincipal);
Answer:D
4.YouarecreatinganassemblynamedAssembly1.Assembly1containsapublicmethod.Theglobalcache
containsasecondassemblynamedAssembly2.
YoumustensurethatthepublicmethodisonlycalledfromAssembly2.
Whichpermissionclassshouldyouuse?
A.GacIdentityPermission
B.PublisherIdentityPermission
C.DataProtectionPermission
D.StrongNameIdentityPermission
Answer:Dpdf
5.Youcreateamethodthatrunsbyusingthecredentialsoftheenduser.YouneedtouseMicrosoftWindows
groupstoauthorizetheuser.Youmustaddacodesegmentthatidentifieswhetherauserisinthelocalgroup
namedClerk.Whichcodesegmentshouldyouuse?
A.WindowsIdentitycurrentUser=WindowsIdentity.GetCurrent();
Foreach(IdentityReferencegrpincurrentUser.Groups)
{NTAccountgrpAccount=((NTAccount)grp.Translate(typeof(NTAccount)));
isAuthorized=grpAccount.Value.Equals(Environment.MachineName+@"\Clerk");
if(isAuthorized)break;}
B.WindowsPrincipalcurrentUser=(WindowsPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole("Clerk");
C.GenericPrincipalcurrentUser=(GenericPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole("Clerk");
D.WindowsPrincipalcurrentUser=(WindowsPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole(Environment.MachineName);
Answer:Bpdf
6.Youaredevelopingaclasslibrary.Portionsofyourcodeneedtoaccesssystemenvironmentvariables.You
needtoforcearuntimeSecurityExceptiononlywhencallersthatarehigherinthecallstackdonothavethe
necessarypermissions.Whichcallmethodshouldyouuse?
A.set.Demand();
B.set.Assert();
C.set.PermitOnly();
D.set.Deny();
Answer:A
1:FRAMEWORK安全性中的几个概念
1.1:安全权限
1.2:类型安全和安全性
1.3:安全策略
1.4:身份验证
2:代码访问安全之声明式安全性
3:代码访问安全之强制安全性
4:代码访问安全之请求权限
4.1:请求访问非托管代码的权限
4.2:通过使用RequestMinimum标志请求最小权限
4.3:通过使用RequestOptional标志请求可选权限
4.4:通过使用RequestRefuse标志拒绝授予权限
4.5:请求命名权限集的权限
5:基于角色的安全性
5.1:主体和标识对象
5.2:PrincipalPermission对象
5.3基于角色的安全检查
5.3.1如何:执行命令性安全检查
5.3.2执行声明式安全检查
5.3.3直接访问主体对象
1:FRAMEWORK安全性中的几个概念
1.1:安全权限
指三类权限,如下:代码访问权限,此权限表示对受保护资源的访问权或执行受保护操作的能力。
标识权限,此权限指示代码具有支持特定类型的标识的凭据。
例如,某个权限表示程序集必须具有的
基于角色的安全性权限,此权限提供一种机制来确定用户(或代表用户的代理)是具有特定标识还是指定角色的成员。
1.2:类型安全和安全性
当代码是类型安全时,运行库的安全性强制机制确保代码不会访问本机代码,除非它有访问本机代码的权限。所有非类型安全的代码必须通过传递的枚举成员1.3:安全策略
每当发生加载程序集的尝试时,运行库就使用安全策略确定授予程序集的权限。在检查了描述程序集标识的信息(称为证据)后,运行库使用安全策略决定代码的信任程度和由此授予程序集的权限。证据包括但不仅限于代码的出版商、它的站点以及它的区域。安全策略还确定授予应用程序域的权限。1.4:身份验证
目前使用的身份验证机制种类繁多,其中许多都可以同.NETFramework基于角色的安全性一起使用。一些最常用的机制包括基本、简要、Passport、操作系统(如NTLM或Kerberos)或应用程序定义的机制。2:代码访问安全之声明式安全性
下面的代码段说明声明式语法,该语法用于请求代码的调用方拥有名为MyPermission的自定义权限。此权限是假设的自定义权限,在.NETFramework中并不存在。在此示例中,声明式调用直接放在类定义之前,指定将此权限应用到类级别。向该属性传递一个SecurityAction.Demand结构来指定调用方必须拥有此权限才能运行。[MyPermission(SecurityAction.Demand,Unrestricted=true)] publicclassMyClass { publicMyClass() { //Theconstructorisprotectedbythesecuritycall. } publicvoidMyMethod() { //Thismethodisprotectedbythesecuritycall. } publicvoidYourMethod() { //Thismethodisprotectedbythesecuritycall. } }
3:代码访问安全之强制安全性
下面的代码段说明请求代码的调用方拥有名为MyPermission的自定义权限时所用的强制性语法。此权限是假设的自定义权限,在.NETFramework中并不存在。在MyMethod中创建MyPermision的一个新实例,使用安全性调用仅保护此方法。publicclassMyClass{ publicMyClass(){ } publicvoidMyMethod(){ //MyPermissionisdemandedusingimperativesyntax. MyPermissionPerm=newMyPermission(); Perm.Demand(); //Thismethodisprotectedbythesecuritycall. } publicvoidYourMethod(){ //Thismethodisnotprotectedbythesecuritycall. } }
4:代码访问安全之请求权限
请求权限会通知运行库应用程序正常运行需要哪些权限,或具体不需要哪些权限。例如,如果应用程序在不使用独立存储的情况下向本地硬盘进行写入,则应用程序必须拥有下表描述权限请求的类型。
最小权限(
可选的权限(
拒绝的权限(
对内置权限集执行上述任何请求(
对XML编码的权限集执行上述任何请求(
4.1:请求访问非托管代码的权限
下面的示例说明如何请求权限来访问非托管代码。注意,它使用//Therequestisplacedattheassemblylevel. usingSystem.Security.Permissions; [assembly:SecurityPermissionAttribute(SecurityAction.RequestMinimum,Flags=SecurityPermissionFlag.UnmanagedCode)] namespaceMyNamespace{ usingSystem; usingSystem.Runtime.InteropServices; publicclassMyClass{ publicMyClass(){ } publicvoidMyMethod(){ //Performinteroperationwithunmanagedcodehere. } } }
如果上面的代码未接收到具有UnmanagedCode标志的SecurityPermission,则运行库将引发一个
4.2:通过使用RequestMinimum标志请求最小权限
下面的示例使用RequestMinimum标志请求//Therequestisplacedattheassemblylevel. usingSystem.Security.Permissions; [assembly:FileIOPermission(SecurityAction.RequestMinimum,Unrestricted=true)] namespaceMyNamespace{ usingSystem; usingSystem.Security; //Thehypotheticalclasslogisinthisnamespace. usingLogNameSpace; publicclassMyClass{ publicMyClass(){ } publicstaticvoidMain(string[]args){ //Putanycodethatrequiresoptionalpermissionsinthetryblock. try{ LogMyLog=newLog(); MyLog.MakeLog(); Console.WriteLine("TheLoghasbeencreated."); } //Catchthesecurityexceptionandinformtheuserthatthe //applicationwasnotgrantedFileIOPermission. catch(SecurityException){ Console.WriteLine("Thisapplicationdoesnothavepermissiontowritetothedisk."); } } } }
如果上面的代码有足够的权限,它会创建日志文件并将下面的消息显示到控制台上:
如果从共享位置运行该代码,并且本地安全设置不允许此类代码拥有FileIOPermission,则不会授予该代码足够的权限,并显示下面的消息:
4.3:通过使用RequestOptional标志请求可选权限
与使用RequestMinimum标志相比,如果应用程序没有接收到使用RequestOptional标志请求的所有权限,则您的应用程序将执行,当该应用程序试图访问受保护的资源时,将引发SecurityException。如果您使用此类型的请求,必须使您的代码能够捕捉在未授予代码可选权限的情况下引发的任何异常。
下面的示例使用SecurityAction.RequestOptional标志请求FileIOPermission,间接拒绝所有其他权限。此示例假定在LogNameSpace中存在一个假设的类Log。Log类包含可在本地计算机上创建新的日志文件的MakeLog方法。此应用程序创建Log类的一个新实例,并在try块中执行MakeLog方法。使用catch关键字,它可以截获引发的任何SecurityException并显示消息。
//Therequestisplacedattheassemblylevel. usingSystem.Security.Permissions; [assembly:FileIOPermission(SecurityAction.RequestOptional,Unrestricted=true)] namespaceMyNamespace{ usingSystem; usingSystem.Security; //Thehypotheticalclasslogisinthisnamespace. usingLogNameSpace; publicclassMyClass{ publicMyClass(){ } publicstaticvoidMain(string[]args){ //Putanycodethatrequiresoptionalpermissionsinthetryblock. try{ LogMyLog=newLog(); MyLog.MakeLog(); Console.WriteLine("TheLoghasbeencreated."); } //Catchthesecurityexceptionandinformtheuserthatthe //applicationwasnotgrantedFileIOPermission. catch(SecurityException){ Console.WriteLine("Thisapplicationdoesnothavepermissiontowritetothedisk."); } } } }
如果上面的代码有足够的权限,它会创建日志文件并将下面的消息显示到控制台上:
如果从共享位置运行该代码,并且本地安全设置不允许此类代码拥有FileIOPermission
4.4:通过使用RequestRefuse标志拒绝授予权限
如果担心您的代码会被恶意地用来访问系统资源,则可以请求永远不向它授予相应的权限。例如,浏览文件中的数据但从不修改数据的应用程序可能拒绝任何文件写入权限。在发生bug或恶意攻击时,此代码不会损坏它操作的数据。RequestRefuse允许将大量权限作为可选的权限来请求,同时确保某些特定的权限不在授予之列。
下面的示例使用RequestRefuse来拒绝来自公共语言运行库安全系统的FileIOPermission:
//Therequestisplacedattheassemblylevel. usingSystem.Security.Permissions; [assembly:FileIOPermission(SecurityAction.RequestRefuse,Unrestricted=true)] namespaceMyNameSpace { usingSystem; usingSystem.Security; usingSystem.Security.Permissions; usingSystem.IO; publicclassMyClass { publicMyClass() { } publicstaticintMain(string[]args) { //Creationofthelogisattemptedinthetryblock. try { StreamWriterTextStream=newStreamWriter("Log.txt"); TextStream.WriteLine("ThisLogwascreatedon{0}",DateTime.Now); TextStream.Close(); Console.WriteLine("TheLogwascreated"); } //CatchtheSecurityexceptionandinformtheuserthatthe //applicationwasnotgrantedFileIOPermission. catch(SecurityException) { Console.WriteLine("Thisapplicationdoesnothavepermissiontowritetothedisk."); } return0; } } }
上面的示例没有被授予创建文件的权限,将生成安全性异常。捕捉语句截获异常,同时应用程序将下面的消息显示到控制台上:
4.5:请求命名权限集的权限
您可以不请求单个权限(使用RequestMinimum、RequestOptional或RequestRefuse),而请求下面的任何内置权限集:Nothing、Execution、FullTrust、Internet、LocalIntranet和SkipVerification。您不能请求自定义的命名权限集或Everything可修改的内置权限集,因为它们表示的权限可能会变化。下面的示例说明请求命名权限集的权限的语法。它将一个//Theattributeisplacedattheassemblylevel. usingSystem.Security.Permissions; [assembly:PermissionSetAttribute(SecurityAction.RequestMinimum,Name="FullTrust")] namespaceMyNamespace { usingSystem; usingSystem.Runtime.InteropServices; publicclassMyClass { publicMyClass() { } publicvoidMyMethod() { //Performoperationsthatrequirepermissionshere. } } }
5:基于角色的安全性
5.1:主体和标识对象
标识对象
标识对象封装有关正在验证的用户或实体的信息。在最基本的级别上,标识对象包含名称和身份验证类型。名称可以是用户名或Windows帐户名,而身份验证类型可以是所支持的登录协议(如KerberosV5)或自定义值。.NETFramework定义了一个主体对象
主体对象表示代码运行时所在的安全上下文。实现基于角色的安全性的应用程序将基于与主体对象关联的角色来授予权限。同标识对象类似,.NETFramework提供5.2:PrincipalPermission对象
基于角色的安全性模型支持与代码访问安全性模型中的权限对象类似的权限对象。此对象(即若要以命令方式实现PrincipalPermission类,请创建该类的一个新实例,并用希望用户在访问代码时具有的名称和角色来初始化该实例。例如,以下代码以“"Joan"”身份和“"Teller"”角色对此对象的一个新实例进行初始化。
Stringid="Joan"; Stringrole="Teller"; PrincipalPermissionprincipalPerm=newPrincipalPermission(id,role);
可使用
[PrincipalPermissionAttribute(SecurityAction.Demand,Name="Joan",Role="Teller")]
执行安全检查时,为成功完成检查,指定的身份和角色必须匹配。但是,创建PrincipalPermission对象时,可传递一个空的身份字符串以指示主体的身份可以是任意的。同样,传递一个空的角色字符串指示主体可以是任何角色的成员(或根本不属于任何角色)。对于声明的安全性,可通过省略两种属性之一来获得相同的效果。例如,下列代码使用PrincipalPermissionAttribute以声明方式指示主体可以具有任意名称,但必须具有出纳的角色。
[PrincipalPermissionAttribute(SecurityAction.Demand,Role="Teller")]
5.3基于角色的安全检查
定义了标识和主体对象后,使用命令式安全检查。
使用声明性安全检查。
直接访问
托管代码可使用命令式或声明式安全检查来确定以下内容:特定主体对象是否是已知角色的成员,是否具有已知的身份,或者是否表示一种角色中的一个已知身份。若要通过命令式或声明式安全性进行安全检查,必须对适当构造的
此外,可以直接访问主体对象的值,并在不使用PrincipalPermission对象的情况下执行检查。在这种情况下,只需读取当前线程主体的值或使用IsInRole方法执行身份验证。
5.3.1如何:执行命令性安全检查
对于命令式请求,可以调用
MyPrincipalPermission.Demand();
下面的代码示例使用命令式检查确保
usingSystem; usingSystem.Security.Permissions; usingSystem.Security.Principal; usingSystem.Security; usingSystem.Threading; usingSystem.Security.Cryptography; publicclassMainClass { publicstaticintMain(string[]args) { Console.WriteLine("Enter'1'tousetheproperidentityor"+ "anyothercharactertousetheimproperidentity."); if(Console.ReadLine()=="1") { //Createagenericidentity. GenericIdentityMyIdentity=newGenericIdentity("MyUser"); //Createagenericprincipal. String[]MyString={"Administrator","User"}; GenericPrincipalMyPrincipal= newGenericPrincipal(MyIdentity,MyString); Thread.CurrentPrincipal=MyPrincipal; } PrivateInfo(); return0; } publicstaticvoidPrivateInfo() { try { //CreateaPrincipalPermissionobject. PrincipalPermissionMyPermission= newPrincipalPermission("MyUser","Administrator"); //Demandthispermission. MyPermission.Demand(); //Printsecretdata. Console.WriteLine( "\n\nYouhaveaccesstotheprivatedata!"); } catch(SecurityExceptione) { Console.WriteLine(e.Message); } } }
如果用户键入1,则将创建访问PrivateInfo方法所需的主体和标识对象。如果用户键入任何其他字符,则不会创建主体和标识对象,并且在调用PrivateInfo方法时,会引发一个安全异常。如果当前线程与一个名称为MyUser、角色为Administrator的主体关联,则会出现下面的消息。
Youhaveaccesstotheprivatedata!
5.3.2执行声明式安全检查
下面的代码示例显示了前一节示例中的PrivateInfo方法经过修改后的版本。此版本使用声明式安全性。
[PrincipalPermissionAttribute(SecurityAction.Demand,Name="MyUser",Role="User")]
publicstaticvoidPrivateInfo()
{
//Printsecretdata.
Console.WriteLine("\n\nYouhaveaccesstotheprivatedata!");
}
如果当前线程不包含正确的主体,此方法将引发安全异常。如果用户输入1,则将调用PrivateInfo方法,并将下面的消息显示到控制台。
5.3.3直接访问主体对象
尽管使用命令式和声明式请求调用基于角色的安全检查是检查和强制身份与角色成员条件的主要机制,但在某些情况下,您可能希望直接访问
获取主体对象后,可以使用条件语句根据主体名来控制对代码的访问,如下面的代码示例所示。
WindowsPrincipalMyPrincipal=
(WindowsPrincipal)Thread.CurrentPrincipal;
if(MyPrincipal.Identity.Name=="fred")
//Permitaccesstosomecode.
还可通过在当前Principal对象上调用IsInRole方法,以编程方式检查角色成员条件,如以下代码实例所示。
//Getthecurrentidentity.
WindowsIdentityMyIdent=WindowsIdentity.GetCurrent();
//Createaprincipal.
WindowsPrincipalMyPrincipal=newWindowsPrincipal(MyIdent);
//Checktheroleusingastring.
if(MyPrincipal.IsInRole(@"BUILTIN\Administrators"))
{
Console.WriteLine("Youareanadministrator.");
}
else
{
Console.WriteLine("Youarenotanadministrator.");
}
//Checktheroleusinganenumeration.
if(MyPrincipal.IsInRole(WindowsBuiltInRole.Administrator))
{
Console.WriteLine("Youareanadministrator.");
}
else
{
Console.WriteLine("Youarenotanadministrator.");
}
当希望访问应用程序定义的Principal对象特定的行为时可以使用此方法。但是,在多数情况下,应使用
下面的代码示例将创建一个
usingSystem;
usingSystem.Security.Permissions;
usingSystem.Security.Policy;
usingSystem.Security.Principal;
usingSystem.Threading;
publicclassClass1
{
publicstaticintMain(string[]args)
{
//SetprincipalpolicytogetaWindowsPrincipal
//asthecurrentprincipalsoyouhavepermissiontoget
//currentuserinformation.
AppDomain.CurrentDomain.SetPrincipalPolicy(
PrincipalPolicy.WindowsPrincipal);
//Getthecurrentprincipalandputitintoaprincipalobject.
WindowsPrincipalmyPrincipal=(Thread.CurrentPrincipal
asWindowsPrincipal);
//Checkthenameandseeiftheuserisauthenticated.
if(myPrincipal.Identity.Name.Equals(@"MYDOMAIN\myuser")
&&myPrincipal.Identity.IsAuthenticated.Equals(true))
{
Console.WriteLine("Hello{0},youareauthenticated!",
myPrincipal.Identity.Name.ToString());
}
else
{
Console.WriteLine("Goaway!Youarenotauthorized!");
}
//UseIsInRoletodeterminetheroleofthecurrentuser.
ArraywbirFields=Enum.GetValues(typeof(WindowsBuiltInRole));
foreach(objectroleNameinwbirFields)
{
try
{
Console.WriteLine("{0}?{1}.",roleName,
myPrincipal.IsInRole((WindowsBuiltInRole)roleName));
}
catch(Exception)
{
Console.WriteLine("{0}:CouldnotobtainroleforthisRID.",
roleName);
}
}
return0;
}
}
如果当前用户是
HelloMYDOMAIN\myuser,youareauthenticated!
但是,如果当前用户是任何其他用户,则程序将显示以下消息。
Goaway!Youarenotauthorized!
MyPrincipal.Identity.Name中的值显示代表授权帐户的域和用户名。请注意,在C#中,字符串"MYDOMAIN\myuser"以at符号(@)为前缀,这样就不会将反斜杠解释为转义符。尽管前面的示例使用WindowsIdentity对象,但仍可使用一般对象轻松产生类似的代码。只需创建一般对象的实例,向其传递所需的值,然后就可以在该对象中检查那些值。
练习:
1.oucreateamethodthatrunsbyusingthecredentialsoftheenduser.YouneedtouseMicrosoftWindows
groupstoauthorizetheuser.YoumustaddacodesegmentthatidentifieswhetherauserisinnamedClerk.Whichcodesegmentshouldyouuse?
A.WindowsIdentitycurrentUser=WindowsIdentity.GetCurrent();
foreach(IdentityReferencegrpincurrentUser.Groups)
{NTAccountgrpAccount=((NTAccount)grp.Translate(typeof(NTAccount)));
isAuthorized=grpAccount.Value.Equals(Environment.MachineName+@"\Clerk");
if(isAuthorized)break;}
B.WindowsPrincipalcurrentUser=(WindowsPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole("Clerk");
C.GenericPrincipalcurrentUser=(GenericPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole("Clerk");
D.WindowsPrincipalcurrentUser=(WindowsPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole(Environment.MachineName);
Answer:
2.Youarewritingcodeforuserauthenticationandauthorization.Theusername,password,androlesarestoredin
yourapplicationdatastore.Youneedtoestablishausersecuritycontextthatwillbeusedforauthorization
checkssuchasIsInRole.Youwritethefollowingcodesegmenttoauthorizetheuser.
if(!TestPassword(userName,password))
thrownewException("couldnotauthenticateuser");
string[]userRolsesArray=LookupUserRoles(userName);
Youneedtocompletethiscodesothatitestablishestheusersecuritycontext.Whichcodesegmentshouldyou
use?
A.GenericIdentityident=newGenericIdentity(userName);
GenericPrincipalcurrentUser=newGenericPrincipal(ident,userRolesArray);
Thread.CurrentPrincipal=currentUser;
B.WindowsIdentityident=newWindowsIdentity(userName);
WindowsPrincipalcurrentUser=newWindowsPrincipal(ident);
Thread.CurrentPrincipal=currentUser;
C.NTAccountuserNTName=newNTAccount(userName);
GenericIdentityident=newGenericIdentity(userNTName.Value);
GenericPrincipalcurrentUser=newGenericPrincipal(ident,userRolesArray);
Thread.CurrentPrincipal=currentUser;
D.IntPtrtoken=IntPtr.Zero;token=LogonUserUsingInterop(userName,encryptedPassword);
WindowsImpersonationContextctx=WindowsIdentity.Impersonate(token);
Answer:A
3.Youaredevelopinganapplicationthatwillusecustomauthenticationandrole-basedsecurity.Youneedto
writeacodesegmenttomaketheruntimeassignanunauthenticatedprincipalobjecttoeachrunningthread.
Whichcodesegmentshouldyouuse?
A.AppDomaindomain=AppDomain.CurrentDomain;
domain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
B.AppDomaindomain=AppDomain.CurrentDomain;
domain.SetThreadPrincipal(newWindowsPrincipal(null));
C.AppDomaindomain=AppDomain.CurrentDomain;
domain.SetAppDomainPolicy(PolicyLevel.CreateAppDomainLevel());
D.AppDomaindomain=AppDomain.CurrentDomain;
domain.SetPrincipalPolicy(PrincipalPolicy.UnauthenticatedPrincipal);
Answer:D
4.YouarecreatinganassemblynamedAssembly1.Assembly1containsapublicmethod.Theglobalcache
containsasecondassemblynamedAssembly2.
YoumustensurethatthepublicmethodisonlycalledfromAssembly2.
Whichpermissionclassshouldyouuse?
A.GacIdentityPermission
B.PublisherIdentityPermission
C.DataProtectionPermission
D.StrongNameIdentityPermission
Answer:D
5.Youcreateamethodthatrunsbyusingthecredentialsoftheenduser.YouneedtouseMicrosoftWindows
groupstoauthorizetheuser.Youmustaddacodesegmentthatidentifieswhetherauserisinthelocalgroup
namedClerk.Whichcodesegmentshouldyouuse?
A.WindowsIdentitycurrentUser=WindowsIdentity.GetCurrent();
Foreach(IdentityReferencegrpincurrentUser.Groups)
{NTAccountgrpAccount=((NTAccount)grp.Translate(typeof(NTAccount)));
isAuthorized=grpAccount.Value.Equals(Environment.MachineName+@"\Clerk");
if(isAuthorized)break;}
B.WindowsPrincipalcurrentUser=(WindowsPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole("Clerk");
C.GenericPrincipalcurrentUser=(GenericPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole("Clerk");
D.WindowsPrincipalcurrentUser=(WindowsPrincipal)Thread.CurrentPrincipal;
isAuthorized=currentUser.IsInRole(Environment.MachineName);
Answer:Bpdf
6.Youaredevelopingaclasslibrary.Portionsofyourcodeneedtoaccesssystemenvironmentvariables.You
needtoforcearuntimeSecurityExceptiononlywhencallersthatarehigherinthecallstackdonothavethe
necessarypermissions.Whichcallmethodshouldyouuse?
A.set.Demand();
B.set.Assert();
C.set.PermitOnly();
D.set.Deny();
Answer:A
相关文章推荐
- C#笔记32:FRAMEWORK安全性之代码访问安全和角色安全
- C#安全性之代码访问安全和角色安全小记
- FRAMEWORK安全性之代码访问安全和角色安全
- C#强化系列文章九:代码访问安全性使用
- C# 全过程用户权限实现策论 (3.权限与代码安全访问)
- C#使用安全权限验证 ADO.NET 代码访问
- 【转】C#强化系列文章九:代码访问安全性使用
- C#强化系列文章九:代码访问安全性使用
- C#强化系列文章九:代码访问安全性使用
- .NET 技术FAQ(七)-----代码访问安全性
- 在C#代码中设置Http访问代理服务器
- SQL新建登录帐户,并为新帐户建立安全帐户,并授予访问数据库的角色
- C#基础之unsafe code(不安全代码)
- 【实用可测】C++ Https访问不安全证书服务器代码
- c#之如何安全的跨线程访问控件
- C# 不安全代码
- 编写高质量代码改善C#程序的157个建议——建议140:使用默认的访问修饰符
- 这些年让我值得骄傲的数据库访问层代码,C#
- .net C# 调用 XFire发布的Webservice 安全访问控制
- 一个基于角色的WEB 安全访问控制系统