热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

从执行上下文角度重新理解.NET(Core)的多线程编程[3]:安全上下文

在前两篇文章(《基于调用链的”参数”传递》和《同步上下文》)中,我们先后介绍了CallContext(IllogicalCallContext和LogicalCallContext

在前两篇文章(《基于调用链的”参数”传递》和《同步上下文》)中,我们先后介绍了CallContext(IllogicalCallContext和LogicalCallContext)、AsyncLocal和SynchronizationContext,它们都是线程执行上下文的一部分。本篇介绍的安全上下文(SecurityContext)同样是执行上下文的一部分,它携带了的身份和权限相关的信息决定了执行代码拥有的控制权限。

目录
一、SecurityContext
二、让代码在指定Windows账号下执行
三、抑制模拟账号的跨线程传播
四、利用Impersonation机制读取文件

SecurityContext类型表示的安全上下文主要携带两种类型的安全信息,一种是通过WindowsIdentity对象表示Windows认证身份,体现为SecurityContext类型的WindowsIdentity属性。如果采用Windows认证和授权,这个WindowsIdentity对象决定了当前代码具有的权限。SecurityContext类型的另一个属性返回的CompressedStack携带了调用堆栈上关于CAS(Code Access Security)相关的信息。。

public sealed class SecurityContext : IDisposable
{   
    ...
    internal WindowsIdentity WindowsIdentity { get; set; }
    internal CompressedStack CompressedStack { get; set; }
}

由于CAS在.NET Core和.NET 5中不再被支持,所以我们不打算对此展开讨论,所以本篇文章讨论的核心就是SecurityContext的WindowsIdentity属性返回的WindowsIdentity对象,这个对象与一种被称为Impersonation的安全机制。

二、让代码在指定Windows账号下执行

Windows进程总是在一个指定账号下执行,该账号决定了当前进程访问Windows资源(比如Windows文件系统)的权限。安全起见,我们一般会选择一个权限较低的账号(比如Network Service)。如果某些代码涉及的资源访问需要更高的权限,我们可以针对当前登录用户对应的Windows账号(如果采用Windows认证)或者是任意指定的Windows账号创建一个上下文,在此上下文中的代相当于在指定的Windows账号下执行,自然拥有了对应账号的权限。这种策略相当于模拟/假冒了(Impersonate)了指定账号执行了某种操作,所以我们将这种机制称为Impersonation。

我们通过一个简单的例子来演示一下Impersonation机制。我们首先编写了如下这个GetWindowsIdentity方法根据指定的账号和密码创建对应的WindowsIdentity对象。如代码片段所示,方法利用指定的用户名和密码调用了Win31函数LogonUser实施了登录操作,并领用返回的token创建代码登录用户的WindowsIdentity对象。

[DllImport("advapi32.dll")]
public static extern int LogonUser(string lpszUserName,
    string lpszDomain,
    string lpszPassword,
    int dwLogonType,
    int dwLogonProvider,
    ref IntPtr phToken);

public static WindowsIdentity GetWindowsIdentity(string username, string password)
{
    IntPtr token = IntPtr.Zero;
    var status = LogonUser(username, Environment.MachineName, password, 2, 0, ref token);
    if (status != 0)
    {
        return new WindowsIdentity(token);
    }
    throw new InvalidProgramException("Invalid user name or password");
}

我们编写了如下的代码来演示不同执行上下文中当前的Windows账号是什么,当前Windows账号对应的WindowsIdentity对象通过调用WindowsIdentity类型的静态方法GetCurrent获得。如代码片段所示,我们在程序初始化时打印出当前Windows账号。然后针对账号foobar(XU\foobar)创建了对应的模拟上下文(Impersonation Context),并在此上下文中打印出当前Windows账号。我们在模拟上下文中通过创建一个线程的方式执行了一个异步操作,并在异步线程中在此输出当前Windows账号。在模拟上下文终结之后,我们在此输出当前的Windows账号看看是否恢复到最初的状态。

class Program
{

    static void Main()
    {
        Console.WriteLine("Before impersonating: {0}", WindowsIdentity.GetCurrent().Name);
        using (GetWindowsIdentity(@"foobar", "password").Impersonate())
        {
            Console.WriteLine("Within Impersonation context: {0}", WindowsIdentity.GetCurrent().Name);
            new Thread(() => Console.WriteLine("Async thread: {0}", WindowsIdentity.GetCurrent().Name)).Start();
        }
        Console.WriteLine("Undo impersonation: {0}", WindowsIdentity.GetCurrent().Name);
        Console.Read();
    }
}

程序运行之后,控制台上会输出如下所示的结果。可以看出在默认情况下,模拟的Windows账号不仅在当前线程中有效,还会自动传递到异步线程中。

从执行上下文角度重新理解.NET(Core)的多线程编程[3]:安全上下文

三、抑制模拟账号的跨线程传播

通过上面的实例我们可以看出在默认情况下安全上下文携带的模拟Windows账号支持跨线程传播,但是有的时候这个机制是不必要的,甚至会代码安全隐患,在此情况下我们可以按照如下的当时调用SecurityContext的

class Program
{

    static void Main()
    {
        Console.WriteLine("Before impersonating: {0}", WindowsIdentity.GetCurrent().Name);
            using (GetWindowsIdentity(@"foobar", "password").Impersonate())
            {
                SecurityContext.SuppressFlowWindowsIdentity();
                Console.WriteLine("Within Impersonation context: {0}", WindowsIdentity.GetCurrent().Name);
                new Thread(() => Console.WriteLine("Async thread: {0}", WindowsIdentity.GetCurrent().Name)).Start();
            }
        Console.WriteLine("Undo impersonation: {0}", WindowsIdentity.GetCurrent().Name);
        Console.Read();
    }
}

再次执行修改后的程序会得到如下所示的输出结果,可以看出模拟的Windows账号(XU\foobar)并没有传递到异步线程中。
从执行上下文角度重新理解.NET(Core)的多线程编程[3]:安全上下文

四、利用Impersonation机制读取文件

访问当前账号无权访问的资源是Impersonation机制的主要应用场景,接下来我们就来演示一下基于文件访问的Impersonation应用场景。我们创建了一个文本文件d:\test.txt,并对其ACL进行如下的设置:只有Xu\foobar账号才具有访问权限。

从执行上下文角度重新理解.NET(Core)的多线程编程[3]:安全上下文

我们修改了上面的代码,将验证当前Windows账号的代码替换成验证文件读取权限的代码。

class Program
{

    static void Main()
    {
        Console.WriteLine("Before impersonating: {0}", CanRead() ? "Allowed" : "Denied");
        using (GetWindowsIdentity("foobar", "password").Impersonate())
        {
            Console.WriteLine("Within Impersonation context: {0}", CanRead() ? "Allowed" : "Denied");
            new Thread(() => Console.WriteLine("Async thread: {0}", CanRead() ? "Allowed" : "Denied")).Start();
        }
        Console.WriteLine("Undo impersonation: {0}", CanRead() ? "Allowed" : "Denied");
        Console.Read();
        bool CanRead()
        {
            var userName = WindowsIdentity.GetCurrent().Name;
            try
            {
                File.ReadAllText(@"d:\test.txt");
                return true;
            }
            catch
            {
                return false;
            }
        }
    }
}

如下所示程序执行后的输出结果,可以看出在文件只有在针对XU\foobar的模拟上下文中才能被读取。如果执行模拟WindowsIdentity的跨线程传播,异步线程也具有文件读取的权限(如图),否则在异步线程中也无法读取该文件(感兴趣的朋友可以自行测试一下)。

从执行上下文角度重新理解.NET(Core)的多线程编程[3]:安全上下文

从执行上下文角度重新理解.NET(Core)的多线程编程[1]:基于调用链的”参数”传递
从执行上下文角度重新理解.NET(Core)的多线程编程[2]:同步上下文
从执行上下文角度重新理解.NET(Core)的多线程编程[3]:安全上下文


推荐阅读
  • WPF项目学习.一
    WPF项目搭建版权声明:本文为博主初学经验,未经博主允许不得转载。一、前言记录在学习与制作WPF过程中遇到的解决方案。使用MVVM的优点是数据和视图分离,双向绑定,低耦合,可重用行 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • 本文介绍了如何在Spring框架中使用AspectJ实现AOP编程,重点讲解了通过注解配置切面的方法,包括方法执行前和方法执行后的增强处理。阅读本文前,请确保已安装并配置好AspectJ。 ... [详细]
  • C#实现文件的压缩与解压
    2019独角兽企业重金招聘Python工程师标准一、准备工作1、下载ICSharpCode.SharpZipLib.dll文件2、项目中引用这个dll二、文件压缩与解压共用类 ... [详细]
  • 我有一个从C项目编译的.o文件,该文件引用了名为init_static_pool ... [详细]
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 在项目需要国际化处理时,即支持多种语言切换的功能,通常有两种方案:单个包和多个包。本文将重点讨论单个包的实现方法。 ... [详细]
  • Java 初次编程练习
    任务要求:设计一个类,包含若干基本属性和至少两个方法(可以从日常生活场景中提取)。在类中实现两个具有不同参数的构造方法。另外,设计一个包含 main 方法的类,用于测试和应用上述类。此作业需编写并调试通过。 ... [详细]
  • 本文介绍了 Go 语言中的高性能、可扩展、轻量级 Web 框架 Echo。Echo 框架简单易用,仅需几行代码即可启动一个高性能 HTTP 服务。 ... [详细]
  • WCF类型共享的最佳实践
    在使用WCF服务时,经常会遇到同一个实体类型在不同服务中被生成为不同版本的问题。本文将介绍几种有效的类型共享方法,以解决这一常见问题。 ... [详细]
  • Cookie学习小结
    Cookie学习小结 ... [详细]
  • 多线程基础概览
    本文探讨了多线程的起源及其在现代编程中的重要性。线程的引入是为了增强进程的稳定性,确保一个进程的崩溃不会影响其他进程。而进程的存在则是为了保障操作系统的稳定运行,防止单一应用程序的错误导致整个系统的崩溃。线程作为进程的逻辑单元,多个线程共享同一CPU,需要合理调度以避免资源竞争。 ... [详细]
  • Java高并发与多线程(二):线程的实现方式详解
    本文将深入探讨Java中线程的三种主要实现方式,包括继承Thread类、实现Runnable接口和实现Callable接口,并分析它们之间的异同及其应用场景。 ... [详细]
  • Unity与MySQL连接过程中出现的新挑战及解决方案探析 ... [详细]
author-avatar
wInnIe小店
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有