乐于分享
好东西不私藏

AppContainer 沙盒权限控制技术文档

AppContainer 沙盒权限控制技术文档

1. 概述

AppContainer 是 Windows 提供的一种应用程序隔离技术,通过将进程运行在受限的沙盒环境中,限制其对文件系统和网络的访问。本文档详细说明如何通过编程方式配置 AppContainer 的文件和网络访问权限。

2. 文件访问权限控制

2.1 核心概念

AppContainer 文件访问遵循 最小权限原则:

  • 默认拒绝:无法访问系统目录和其他应用数据

  • 私有目录:自动拥有 %LOCALAPPDATA%\Packages\<包名>\ 的完全控制权

  • 显式授权:通过 ACL(访问控制列表)授予额外权限

2.2 相关 API

2.2.1 获取/修改安全描述符

API

说明

GetNamedSecurityInfo

获取文件/目录的安全描述符

SetNamedSecurityInfo

设置文件/目录的安全描述符

GetSecurityInfo

获取对象的安全描述符

SetSecurityInfo

设置对象的安全描述符

2.2.2 ACL 操作 API

API

说明

SetEntriesInAcl

将 ACE 条目合并到 ACL 中

InitializeAcl

初始化一个新的 ACL

AddAccessAllowedAce

向 ACL 添加允许访问的 ACE

AddAccessDeniedAce

向 ACL 添加拒绝访问的 ACE

GetAclInformation

获取 ACL 信息

2.2.3 容器管理 API

API

说明

CreateAppContainerProfile

创建 AppContainer 配置文件

DeleteAppContainerProfile

删除 AppContainer 配置文件

DeriveAppContainerSidFromAppContainerName

根据名称获取容器 SID

2.3 代码示例:授予文件访问权限

#include<windows.h>#include<aclapi.h>#include<sddl.h>#include<iostream>#pragma comment(lib, "advapi32.lib")/* * 为 AppContainer 授予文件/目录访问权限 * @param pContainerSid   容器的 SID * @param lpszObjectPath  文件或目录路径 * @param dwAccessMask    访问权限掩码(如 GENERIC_READ | GENERIC_WRITE) * @param objectType      对象类型(SE_FILE_OBJECT) * @return TRUE 成功,FALSE 失败 */BOOL GrantAccessToFile(    PSID pContainerSid,    LPCWSTR lpszObjectPath,    DWORD dwAccessMask,    SE_OBJECT_TYPE objectType = SE_FILE_OBJECT) {    PSECURITY_DESCRIPTOR pSD = NULL;    PACL pCurrentDacl = NULL;    PACL pNewDacl = NULL;    DWORD dwResult = 0;    // 1. 获取当前对象的 DACL    dwResult = GetNamedSecurityInfoW(        lpszObjectPath,        objectType,        DACL_SECURITY_INFORMATION,        NULL,        NULL,        &pCurrentDacl,        NULL,        &pSD    );    if (dwResult != ERROR_SUCCESS) {        std::wcerr << L"GetNamedSecurityInfo 失败,错误码: " << dwResult << std::endl;        return FALSE;    }    // 2. 创建新的 ACE(访问控制项)    EXPLICIT_ACCESS_W ea = { 0 };    ea.grfAccessPermissions = dwAccessMask;    // 权限掩码    ea.grfAccessMode = GRANT_ACCESS;           // 授权模式    ea.grfInheritance = NO_INHERITANCE;        // 不继承    ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;   // 使用 SID 标识    ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;    ea.Trustee.ptstrName = (LPWSTR)pContainerSid;    // 3. 将 ACE 合并到 ACL    dwResult = SetEntriesInAclW(1, &ea, pCurrentDacl, &pNewDacl);    if (dwResult != ERROR_SUCCESS) {        std::wcerr << L"SetEntriesInAcl 失败,错误码: " << dwResult << std::endl;        LocalFree(pSD);        return FALSE;    }    // 4. 应用新的 DACL    dwResult = SetNamedSecurityInfoW(        (LPWSTR)lpszObjectPath,        objectType,        DACL_SECURITY_INFORMATION,        NULL,        NULL,        pNewDacl,        NULL    );    // 5. 清理资源    LocalFree(pSD);    if (pNewDacl) LocalFree(pNewDacl);    if (dwResult != ERROR_SUCCESS) {        std::wcerr << L"SetNamedSecurityInfo 失败,错误码: " << dwResult << std::endl;        return FALSE;    }    return TRUE;}/* * 为容器授予目录遍历权限(访问目录内文件的前提) */BOOL GrantDirectoryTraverseAccess(PSID pContainerSid, LPCWSTR lpszDirectoryPath){    // FILE_LIST_DIRECTORY 允许列出目录内容并遍历    return GrantAccessToFile(pContainerSid, lpszDirectoryPath, FILE_LIST_DIRECTORY);}

2.4 常用权限掩码

权限掩码

十六进制

说明

GENERIC_READ

0x80000000

读取权限

GENERIC_WRITE

0x40000000

写入权限

GENERIC_EXECUTE

0x20000000

执行权限

GENERIC_ALL

0x10000000

完全控制

FILE_READ_DATA

0x00000001

读取文件数据

FILE_WRITE_DATA

0x00000002

写入文件数据

FILE_APPEND_DATA

0x00000004

追加数据

FILE_LIST_DIRECTORY

0x00000001

列出目录内容

FILE_ADD_FILE

0x00000002

在目录中创建文件

3. 网络访问权限控制

3.1 核心概念

AppContainer 的网络访问通过 Capabilities(能力) 进行粗粒度控制:

┌─────────────────────────────────────────────────────────────┐│                    网络访问能力模型                           │├─────────────────────────────────────────────────────────────┤│  InternetClient          → 出站互联网访问                    ││  InternetClientServer    → 双向互联网访问(入站+出站)         ││  PrivateNetworkClientServer → 局域网访问                     ││  Loopback (特殊)         → 本地回环访问(默认禁止,需豁免)     │└─────────────────────────────────────────────────────────────┘

3.2 相关 API

3.2.1 容器创建 API

API

说明

CreateAppContainerProfile

创建容器时可传入能力列表

SECURITY_CAPABILITIES

定义容器的安全能力

SID_AND_ATTRIBUTES

能力 SID 及属性

3.2.2 进程启动 API

API

说明

CreateProcess

创建进程,需携带 EXTENDED_STARTUPINFO_PRESENT 标志

STARTUPINFOEX

扩展的启动信息,包含属性列表

PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES

属性类型,指定容器安全能力

InitializeProcThreadAttributeList

初始化属性列表

UpdateProcThreadAttribute

更新属性列表

DeleteProcThreadAttributeList

删除属性列表

3.3 代码示例:配置网络能力

#include<windows.h>#include<sddl.h>#include<vector>// 网络能力标志定义#define NET_CAP_INTERNET_OUT   0x01  // 互联网出站#define NET_CAP_PRIVATE        0x02  // 局域网访问#define NET_CAP_INTERNET_FULL  0x04  // 双向互联网/** * 创建带网络能力的 AppContainer * @param containerName   容器唯一名称 * @param displayName     显示名称 * @param dwNetCapabilities 网络能力标志组合 * @param ppSid           输出:容器 SID * @return TRUE 成功,FALSE 失败 */BOOL CreateContainerWithNetworkCapabilities(    LPCWSTR containerName,    LPCWSTR displayName,    DWORD dwNetCapabilities,    PSID* ppSid) {    std::vector<SID_AND_ATTRIBUTES> capabilities;    BYTE sidBuffer[SECURITY_MAX_SID_SIZE];    DWORD sidSize;    // 1. 根据标志添加对应的能力 SID    if (dwNetCapabilities & NET_CAP_INTERNET_OUT) {        sidSize = SECURITY_MAX_SID_SIZE;        if (CreateWellKnownSid(WinCapabilityInternetClientSid, NULL, sidBuffer, &sidSize)) {            SID_AND_ATTRIBUTES attr = { sidBuffer, SE_GROUP_ENABLED };            capabilities.push_back(attr);        }    }    if (dwNetCapabilities & NET_CAP_PRIVATE) {        sidSize = SECURITY_MAX_SID_SIZE;        if (CreateWellKnownSid(WinCapabilityPrivateNetworkClientServerSid, NULL, sidBuffer, &sidSize)) {            SID_AND_ATTRIBUTES attr = { sidBuffer, SE_GROUP_ENABLED };            capabilities.push_back(attr);        }    }    if (dwNetCapabilities & NET_CAP_INTERNET_FULL) {        sidSize = SECURITY_MAX_SID_SIZE;        if (CreateWellKnownSid(WinCapabilityInternetClientServerSid, NULL, sidBuffer, &sidSize)) {            SID_AND_ATTRIBUTES attr = { sidBuffer, SE_GROUP_ENABLED };            capabilities.push_back(attr);        }    }    // 2. 删除已存在的容器(可选)    DeleteAppContainerProfile(containerName);    // 3. 创建容器    HRESULT hr = CreateAppContainerProfile(        containerName,        displayName,        L"AppContainer with network capabilities",        capabilities.empty() ? NULL : capabilities.data(),        (DWORD)capabilities.size(),        ppSid    );    return SUCCEEDED(hr);}/** * 在 AppContainer 中启动进程 * @param pSid             容器 SID * @param lpExecutablePath 可执行文件路径 * @param lpCommandLine    命令行参数 * @return TRUE 成功,FALSE 失败 */BOOL LaunchProcessInContainer(    PSID pSid,    LPCWSTR lpExecutablePath,    LPCWSTR lpCommandLine) {    // 1. 构建安全能力结构    SECURITY_CAPABILITIES sc = { 0 };    sc.AppContainerSid = pSid;    sc.Capabilities = NULL;    sc.CapabilityCount = 0;    // 2. 初始化扩展启动信息    STARTUPINFOEXW siex = { sizeof(STARTUPINFOEXW) };    SIZE_T cbAttributeListSize = 0;    InitializeProcThreadAttributeList(NULL10, &cbAttributeListSize);    siex.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(        GetProcessHeap(),        HEAP_ZERO_MEMORY,        cbAttributeListSize    );    if (!siex.lpAttributeList) {        return FALSE;    }    InitializeProcThreadAttributeList(siex.lpAttributeList, 10, &cbAttributeListSize);    // 3. 添加安全能力属性    UpdateProcThreadAttribute(        siex.lpAttributeList,        0,        PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES,        &sc,        sizeof(sc),        NULL,        NULL    );    // 4. 启动进程    PROCESS_INFORMATION pi = { 0 };    std::wstring cmdLine = lpCommandLine ? lpCommandLine : lpExecutablePath;    BOOL bSuccess = CreateProcessW(        lpExecutablePath,        (LPWSTR)cmdLine.c_str(),        NULLNULL, FALSE,        EXTENDED_STARTUPINFO_PRESENT,        NULLNULL,        (LPSTARTUPINFOW)&siex,        &pi    );    // 5. 清理资源    if (bSuccess) {        CloseHandle(pi.hProcess);        CloseHandle(pi.hThread);    }    DeleteProcThreadAttributeList(siex.lpAttributeList);    HeapFree(GetProcessHeap(), 0, siex.lpAttributeList);    return bSuccess;}

3.4 网络能力对照表

能力 SID

常量名称

说明

访问范围

S-1-15-3-1

WinCapabilityInternetClientSid

出站互联网访问

任意外部 IP

S-1-15-3-2

WinCapabilityInternetClientServerSid

双向互联网访问

外部入站+出站

S-1-15-3-3

WinCapabilityPrivateNetworkClientServerSid

局域网访问

私有 IP 段(10.x, 172.16.x, 192.168.x)

特殊

LoopbackExemption

本地回环访问

127.0.0.1, localhost

4. 域名级网络访问控制(需配合防火墙)

4.1 问题说明

AppContainer 本身不支持按域名进行访问控制。如果需要精确控制特定域名的访问,必须借助 Windows 防火墙或网络过滤技术。

4.2 解决方案架构

┌─────────────────────────────────────────────────────────────────┐│                     域名访问控制方案                             │├─────────────────────────────────────────────────────────────────┤│                                                                 ││   AppContainer 进程                                             ││         │                                                       ││         ▼                                                       ││   ┌─────────────────────────────────────────────────────────┐  ││   │              网络出站请求                                 │  ││   └─────────────────────────────────────────────────────────┘  ││         │                                                       ││         ▼                                                       ││   ┌─────────────────────────────────────────────────────────┐  ││   │          Windows 防火墙 (WFP)                            │  ││   │   - 按用户 SID 过滤                                       │  ││   │   - 按 IP 地址过滤                                        │  ││   │   - 按端口过滤                                            │  ││   └─────────────────────────────────────────────────────────┘  ││         │                                                       ││         ▼                                                       ││   ┌─────────────────────────────────────────────────────────┐  ││   │          域名解析层(可选)                               │  ││   │   - DNS 劫持(hosts 文件)                                │  ││   │   - 本地 DNS 代理                                         │  ││   └─────────────────────────────────────────────────────────┘  ││                                                                 │└─────────────────────────────────────────────────────────────────┘

4.3 防火墙相关 API

API

头文件

说明

INetFwPolicy2

netfw.h

防火墙策略主接口

INetFwRule

netfw.h

防火墙规则对象

INetFwRules

netfw.h

防火墙规则集合

CoCreateInstance

objbase.h

创建 COM 对象实例

DeriveAppContainerSidFromAppContainerName

userenv.h

获取容器 SID

4.4 代码示例:防火墙规则配置

#include<windows.h>#include<netfw.h>#include<comdef.h>#include<iostream>#pragma comment(lib, "ole32.lib")#pragma comment(lib, "oleaut32.lib")/** * Windows 防火墙管理器 */class FirewallManager {private:    INetFwPolicy2* m_pPolicy;public:    FirewallManager() : m_pPolicy(NULL) {        CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);        CoCreateInstance(            __uuidof(NetFwPolicy2),            NULL,            CLSCTX_INPROC_SERVER,            __uuidof(INetFwPolicy2),            (void**)&m_pPolicy        );    }    ~FirewallManager() {        if (m_pPolicy) m_pPolicy->Release();        CoUninitialize();    }    /**     * 为 AppContainer 创建出站阻止规则     * @param ruleName       规则名称     * @param containerName  容器名称     * @param remoteIPs      要阻止的远程 IP 地址(支持 CIDR)     * @param remotePorts    要阻止的远程端口(可选)     * @return TRUE 成功,FALSE 失败     */    BOOL BlockOutboundTraffic(        LPCWSTR ruleName,        LPCWSTR containerName,        LPCWSTR remoteIPs,        LPCWSTR remotePorts = NULL) {        if (!m_pPolicy) return FALSE;        // 1. 获取容器的 SID        PSID pContainerSid = NULL;        HRESULT hr = DeriveAppContainerSidFromAppContainerName(containerName, &pContainerSid);        if (FAILED(hr)) {            return FALSE;        }        // 2. 转换 SID 为字符串        LPWSTR pSidString = NULL;        ConvertSidToStringSidW(pContainerSid, &pSidString);        FreeSid(pContainerSid);        if (!pSidString) return FALSE;        // 3. 创建防火墙规则        INetFwRule* pRule = NULL;        hr = CoCreateInstance(            __uuidof(NetFwRule),            NULL,            CLSCTX_INPROC_SERVER,            __uuidof(INetFwRule),            (void**)&pRule        );        if (FAILED(hr)) {            LocalFree(pSidString);            return FALSE;        }        // 4. 配置规则属性        pRule->put_Name(_bstr_t(ruleName));        pRule->put_Description(_bstr_t(L"AppContainer 网络访问限制"));        pRule->put_User(_bstr_t(pSidString));           // 仅对指定容器生效        pRule->put_Action(NET_FW_ACTION_BLOCK);         // 阻止        pRule->put_Direction(NET_FW_RULE_DIR_OUT);      // 出站        pRule->put_Protocol(NET_FW_IP_PROTOCOL_TCP);    // TCP 协议        if (remoteIPs) {            pRule->put_RemoteAddresses(_bstr_t(remoteIPs));        }        if (remotePorts) {            pRule->put_RemotePorts(_bstr_t(remotePorts));        }        LocalFree(pSidString);        // 5. 添加规则到防火墙        INetFwRules* pRules = NULL;        hr = m_pPolicy->get_Rules(&pRules);        if (SUCCEEDED(hr)) {            pRules->Remove(_bstr_t(ruleName));  // 删除旧规则            hr = pRules->Add(pRule);             // 添加新规则            pRules->Release();        }        pRule->Release();        return SUCCEEDED(hr);    }    /**     * 删除防火墙规则     */    BOOL RemoveRule(LPCWSTR ruleName){        if (!m_pPolicy) return FALSE;        INetFwRules* pRules = NULL;        HRESULT hr = m_pPolicy->get_Rules(&pRules);        if (SUCCEEDED(hr)) {            pRules->Remove(_bstr_t(ruleName));            pRules->Release();            return TRUE;        }        return FALSE;    }};

4.5 域名解析劫持(hosts 文件)

由于防火墙规则基于 IP 地址,对于域名的精确控制需要配合 DNS 劫持:

#include<fstream>#include<shlobj.h>#include<string>/** * 通过 hosts 文件阻止域名解析 * @param domains 要阻止的域名列表 * @return TRUE 成功,FALSE 失败 */BOOL BlockDomainsViaHosts(const std::vector<std::wstring>& domains){    // 获取 hosts 文件路径    wchar_t systemPath[MAX_PATH];    GetSystemDirectoryW(systemPath, MAX_PATH);    std::wstring hostsPath = std::wstring(systemPath) + L"\\drivers\\etc\\hosts";    // 打开 hosts 文件(追加模式)    std::ofstream hostsFile(hostsPath, std::ios::app);    if (!hostsFile.is_open()) {        return FALSE;    }    // 添加条目,将域名指向 127.0.0.1    hostsFile << "\n# AppContainer 阻止的域名\n";    for (const auto& domain : domains) {        std::string domainStr(domain.begin(), domain.end());        hostsFile << "127.0.0.1  " << domainStr << "\n";    }    hostsFile.close();    // 刷新 DNS 缓存    system("ipconfig /flushdns");    return TRUE;}

4.6 域名级访问控制完整流程

/** * 完整示例:创建只能访问特定域名的 AppContainer */voidCreateDomainRestrictedContainer(){    std::wstring containerName = L"MyApp.DomainRestricted";    std::wstring allowDomain = L"api.myapp.com";    std::vector<std::wstring> blockDomains = {        L"xxx.xxxx.com",    };    // 1. 创建容器(允许出站互联网)    PSID pSid = NULL;    CreateContainerWithNetworkCapabilities(        containerName.c_str(),        L"受限容器",        NET_CAP_INTERNET_OUT,  // 允许出站        &pSid    );    // 2. 解析允许的域名获取 IP(用于防火墙白名单)    // 注:实际应用中需要动态 DNS 解析    std::wstring allowedIPs = L"192.168.1.100/32";  // 示例    // 3. 配置防火墙:阻止所有出站,仅允许特定 IP    FirewallManager fw;    fw.BlockOutboundTraffic(        L"BlockAllOutbound",        containerName.c_str(),        L"*",           // 所有远程地址        NULL    );    // 4. 添加允许规则(优先级高于阻止)    // 注:需要额外添加允许规则,或使用白名单模式    // 5. 通过 hosts 文件阻止不需要的域名解析    BlockDomainsViaHosts(blockDomains);    // 6. 启动进程    LaunchProcessInContainer(pSid, L"C:\\Windows\\System32\\cmd.exe"NULL);}

5. 本地回环访问配置

5.1 问题说明

AppContainer 默认禁止访问 localhost (127.0.0.1),这是为了安全隔离。如果需要访问本地服务(如调试代理),需要单独配置豁免。

5.2 问题排查

问题

排查方向

文件访问被拒绝

检查 ACL 和完整性级别

网络无法连接

检查能力和防火墙规则

localhost 无法访问

检查回环豁免配置

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » AppContainer 沙盒权限控制技术文档

猜你喜欢

  • 暂无文章