CEF框架接入DoH最佳实践
本文档介绍如何在CEF框架中接入DoH。
概述
CEF(Chromium Embedded Framework)是一个基于Chromium的开源框架,广泛应用于需要嵌入Web浏览功能的桌面应用程序中。在CEF框架的应用场景中,尤其是在涉及敏感数据交互或高安全性要求的环境中,传统DNS解析方式可能无法满足安全需求。DoH通过HTTPS加密通道传输DNS查询和响应,能够有效防止中间人攻击、DNS劫持以及流量嗅探,从而提升应用的安全性和用户隐私保护能力。通过将DoH集成到CEF框架中,开发者可以在不改变原有应用逻辑的前提下,显著提升嵌入式浏览器的安全性和用户体验。
前置条件
启用DoH,请参考DoH开启与使用。
配置域名解析范围:
如选择使用“域名列表中的域名”,建议在域名列表中增加“google.com”用于后续Chromium内核通过DoH接入点可用性校验。
如选择使用“所有域名”,则无需其他操作。
CEF版本要求:建议使用115或更高版本(对应Chromium 115+)
接入步骤
1. 配置CEF应用缓存路径
本方案需要主动配置应用级的缓存路径,若未主动设置缓存路径而使用默认路径,其他CEF应用可能污染本应用缓存,导致DoH配置失效或被滥用。
1.1. 通过CefSettings配置
在初始化CEF时,可以通过CefSettings
对象来设置缓存路径,同时确保有权限访问此缓存路径,以下为示例代码:
void ConfigureCachePath(CefSettings& settings) {
// 设置根缓存路径
std::string cache_path;
#if defined(OS_MAC)
cache_path = std::string("/Users/") + getenv("USER") + "/Library/Application Support/YourAppName/cache";
#elif defined(OS_WIN)
cache_path = std::string(getenv("LOCALAPPDATA")) + "\\YourAppName\\cache";
#else
cache_path = std::string(getenv("HOME")) + "/.cache/YourAppName";
#endif
CefString(&settings.root_cache_path) = cache_path;
CefString(&settings.cache_path) = cache_path;
}
// 在main函数中使用
int main(int argc, char* argv[]) {
CefSettings settings;
// 其他参数配置
// ...
ConfigureCachePath(settings);
// 初始化CEF
CefInitialize(main_args, settings, app.get(), nullptr);
// ...
}
1.2. 通过命令行参数配置
可以通过命令行参数来设置缓存路径,以下为示例代码:
std::string GetDefaultCachePath() {
std::string cache_path;
#if defined(OS_MAC)
cache_path = std::string("/Users/") + getenv("USER") + "/Library/Application Support/YourAppName/cache";
#elif defined(OS_WIN)
cache_path = std::string(getenv("LOCALAPPDATA")) + "\\YourAppName\\cache";
#else
cache_path = std::string(getenv("HOME")) + "/.cache/YourAppName";
#endif
return cache_path;
}
void OnBeforeCommandLineProcessing(
const CefString& process_type,
CefRefPtr<CefCommandLine> command_line) override {
std::string cache_path = GetDefaultCachePath();
if (!cache_path.empty()) {
command_line->AppendSwitchWithValue("root-cache-path", cache_path);
}
}
2. 通过Preferences配置DoH
使用CefPreferenceManager
来配置DoH,以下为示例代码:
void UpdateDnsOverHttpsTemplate(const std::string& new_template) {
// 获取全局的 CefPreferenceManager 实例
CefRefPtr<CefPreferenceManager> pref_manager =
CefPreferenceManager::GetGlobalPreferenceManager();
// 设置DoH模板
CefRefPtr<CefValue> template_value = CefValue::Create();
template_value->SetString(new_template);
// 设置DoH模式为"secure"
CefRefPtr<CefValue> mode_value = CefValue::Create();
mode_value->SetString("secure");
// 应用设置
CefString error;
pref_manager->SetPreference("dns_over_https.mode", mode_value, error);
pref_manager->SetPreference("dns_over_https.templates", template_value, error);
}
// 在应用初始化时调用
void OnContextInitialized(CefRefPtr<ClientAppBrowser> app) override {
// 在此处更换为自己的专属DoH接入点
UpdateDnsOverHttpsTemplate("https://1xxxx3.aliyunhttpdns.com/dns-query");
}
3. 实现自动降级机制
CEF支持以下DoH模式:
secure
: 强制使用DoH,不允许回退到普通DNS。automatic
: 自动模式,允许在DoH失败时回退到普通DNS。off
: 关闭DoH。
为了提高应用的可用性,可以实现自动降级机制:在DoH解析失败时自动切换到本地DNS。以下是一个实现示例:
void ClientHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) {
// 检测DNS相关错误
if (errorCode == ERR_NAME_NOT_RESOLVED ||
errorCode == ERR_NAME_RESOLUTION_FAILED) {
LOG(ERROR) << "DNS resolution failed for URL: " << failedUrl.ToString()
<< ". Error code: " << errorCode
<< ". Switching to Local DNS...";
// 获取全局preference manager
CefRefPtr<CefPreferenceManager> pref_manager =
CefPreferenceManager::GetGlobalPreferenceManager();
// 切换到automatic模式,允许使用本地DNS
CefRefPtr<CefValue> new_mode = CefValue::Create();
new_mode->SetString("automatic");
CefString error;
if (pref_manager->SetPreference("dns_over_https.mode", new_mode, error)) {
// 重试加载失败的页面
frame->LoadURL(failedUrl);
return;
}
}
// 处理其他错误...
}