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. 获取当前对象的 DACLdwResult = 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 合并到 ACLdwResult = SetEntriesInAclW(1, &ea, pCurrentDacl, &pNewDacl);if (dwResult != ERROR_SUCCESS) {std::wcerr << L"SetEntriesInAcl 失败,错误码: " << dwResult << std::endl;LocalFree(pSD);return FALSE;}// 4. 应用新的 DACLdwResult = 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 |
|
写入文件数据 |
|
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. 根据标志添加对应的能力 SIDif (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(NULL, 1, 0, &cbAttributeListSize);siex.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,cbAttributeListSize);if (!siex.lpAttributeList) {return FALSE;}InitializeProcThreadAttributeList(siex.lpAttributeList, 1, 0, &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(),NULL, NULL, FALSE,EXTENDED_STARTUPINFO_PRESENT,NULL, NULL,(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. 获取容器的 SIDPSID 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.1hostsFile << "\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. 配置防火墙:阻止所有出站,仅允许特定 IPFirewallManager 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 无法访问 |
检查回环豁免配置 |
夜雨聆风