CEF框架接入DoH最佳实践

本文档介绍如何在CEF框架中接入DoH。

1. 前言

CEF(Chromium Embedded Framework)是一个基于Chromium的开源框架,广泛应用于需要嵌入Web浏览功能的桌面应用程序中。在CEF框架的应用场景中,尤其是在涉及敏感数据交互或高安全性要求的环境中,传统DNS解析方式可能无法满足安全需求。DoH通过HTTPS加密通道传输DNS查询和响应,能够有效防止中间人攻击、DNS劫持以及流量嗅探,从而提升应用的安全性和用户隐私保护能力。通过将DoH集成到CEF框架中,开发者可以在不改变原有应用逻辑的前提下,显著提升嵌入式浏览器的安全性和用户体验。

2. 前提条件

CEF框架接入 DoH 前,请您确保已经完成配置DoH服务

说明
  • CEF 会使用内置的探测域名 google.com 对 DoH 可用性检查,在探测完成前,DoH 被视为“未确定可用”,为保证 DoH 服务正常工作,请确保 google.com 域名添加到解析列表中或允许所有域名解析

  • CEF版本要求:建议使用115或更高版本(对应Chromium 115+)

3 接入步骤

CEF框架接入DoH主要包含三个核心步骤:首先需要配置独立的应用级缓存路径以避免配置冲突;然后通过Preferences API设置DoH模板和模式以启用加密DNS服务;最后实现自动降级机制以保障服务的稳定性。以下将详细介绍每个步骤的具体实现方法。

3.1 配置CEF应用缓存路径

本方案需要主动配置应用级的缓存路径。若未主动设置缓存路径而使用默认路径,其他CEF应用可能会污染本应用缓存,导致DoH配置失效或被覆盖。

在初始化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);
    // ...
}

3.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.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;
    }
  }
  // 处理其他错误...
}

4. 总结

通过本文档介绍的步骤,您可以成功在CEF框架中集成DNS over HTTPS (DoH)功能,从而显著提升应用程序的安全性和用户隐私保护能力。配置后可通过网络抓包查看 HTTPS 请求、检查 DNS 日志或进行网络故障测试来验证 DoH 接入成功。推荐使用 automatic 模式并配置多个 DoH 服务器以确保生产环境的稳定性。