TWebBrowser.Silent = TRUE 后为何仍弹出证书警告?
结论
TWebBrowser.Silent := True 不会关闭证书验证,也不会改变证书吊销检查策略。仅设置 Silent,无法保证这类安全提示不再出现。
要通过代码处理,应在浏览器宿主中接入 IHttpSecurity.OnSecurityProblem,由程序决定遇到证书问题时中止还是继续。实现前要先确定处理策略:不弹窗并中止访问,还是不弹窗且继续访问。
对于分发到大量客户端的程序,应优先修复证书或吊销信息访问问题。确实需要静默处理时,再实现宿主接口。
为什么设置 Silent 后仍会提示
TWebBrowser 封装的是 IE WebBrowser ActiveX 控件。页面导航和安全检查还涉及 MSHTML、URLMon、WinINet 及 Windows 证书验证组件。
Silent 是浏览器的界面控制属性,不能替代证书错误处理。官方文档说明,该属性会委托给顶层框架处理,没有框架时可能返回 E_FAIL。排查时,应确认属性是否设置成功,以及弹窗是否来自同一个浏览器实例。参考:IWebBrowser2::Silent
“无法获取此站点安全证书的吊销信息”表示客户端无法完成相应的吊销状态检查,不能据此认定证书已经被吊销。WinINet 为这两种情况定义了不同的错误码,需要分别处理:
ERROR_INTERNET_SEC_CERT_REV_FAILED,值为12057,表示吊销检查失败。ERROR_INTERNET_SEC_CERT_REVOKED,值为12170,表示证书已经被吊销。
即使代码传入的是 http://...,后续重定向、框架或页面资源仍可能访问 HTTPS 地址,触发证书检查。
为什么只有部分计算机会出现
先在出现问题的客户端上检查实际证书链,以及证书中指定的 CRL 或 OCSP 地址。
客户端之间可能存在以下差异:
- 部分网络无法访问吊销信息服务,例如访问被代理、防火墙或 DNS 配置阻断。
- 一些客户端已有有效的吊销信息缓存,另一些需要联网获取。
- 客户端使用的证书链、吊销检查策略或系统环境不同。
仅凭提示文字无法确定具体原因。Windows 证书链验证涉及吊销信息获取、缓存和超时处理,因此,同一站点在不同客户端上可能得到不同结果。参考:CertGetCertificateChain
如果能够控制服务端,应先确认其证书链配置正确,并确保客户端网络可以访问相关吊销信息服务。解决这些问题可以消除触发提示的原因,无须逐台修改 Internet Options。
通过宿主接口静默处理
IHttpSecurity.OnSecurityProblem 允许程序接管相关安全问题。桌面版接口文档规定了以下返回值:
| 返回值 | 含义 |
|---|---|
E_ABORT | 中止下载 |
S_FALSE | 显示警告对话框 |
RPC_E_RETRY | 继续或重试下载 |
在此回调中返回 S_FALSE 会要求显示警告,不能用它禁止弹窗。桌面版接口继续处理时应按文档返回 RPC_E_RETRY,不能照搬 Windows CE 示例中的 S_OK。参考:IHttpSecurity::OnSecurityProblem
必须通过浏览器宿主的服务查询机制,向 MSHTML 提供这些接口。仅在窗体中声明一个同名方法,浏览器不会自动调用它。参考:IHttpSecurity
示例:不弹窗,并中止有证书问题的访问
以下示例使用 Delphi 7 风格的派生控件接入接口。对进入该回调的安全问题,示例统一返回 E_ABORT,相应请求会失败,页面也不保证加载成功。
unit QuietWebBrowser;
interface
uses
Windows, ActiveX, UrlMon, SHDocVw;
type
TQuietWebBrowser = class(TWebBrowser, IServiceProvider,
IWindowForBindingUI, IHttpSecurity)
protected
function QueryService(const rsid, iid: TGUID;
out Obj): HRESULT; stdcall;
function GetWindow(const guidReason: TGUID;
out wnd): HRESULT; stdcall;
function OnSecurityProblem(
dwProblem: DWORD): HRESULT; stdcall;
end;
implementation
function TQuietWebBrowser.QueryService(
const rsid, iid: TGUID; out Obj): HRESULT;
begin
Pointer(Obj) := nil;
if IsEqualGUID(iid, IID_IHttpSecurity) or
IsEqualGUID(iid, IID_IWindowForBindingUI) then
Result := QueryInterface(iid, Obj)
else
Result := E_NOINTERFACE;
end;
function TQuietWebBrowser.GetWindow(
const guidReason: TGUID; out wnd): HRESULT;
begin
HWND(wnd) := Handle;
Result := S_OK;
end;
function TQuietWebBrowser.OnSecurityProblem(
dwProblem: DWORD): HRESULT;
begin
// 在这里记录 dwProblem,便于定位客户端差异。
Result := E_ABORT;
end;
end.
使用时,必须实际创建这个派生控件。例如,先在窗体中增加字段 FBrowser: TQuietWebBrowser,再创建实例:
procedure TForm1.FormCreate(Sender: TObject);
begin
FBrowser := TQuietWebBrowser.Create(Self);
FBrowser.Parent := Self;
FBrowser.Align := alClient;
FBrowser.Silent := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
FBrowser.Navigate('https://your-server.example/');
end;
仅把现有 WebBrowser1 强制转换成 TQuietWebBrowser,不会改变其实例类型,也不会安装这些接口。
不同 Delphi 版本的封装可能已经实现了 QueryService,或提供了对应的扩展事件。如果已有服务实现,应将上述分支合并进去,保留其他服务的处理。方法签名和是否需要 override,以所用版本的声明为准。
如果必须继续加载
如果业务明确允许在“吊销状态暂时无法确认”时继续访问,可以在经过验证的处理分支中返回 RPC_E_RETRY。这一处理不能用于所有证书问题,使用时应满足以下条件:
- 在出现问题的计算机上记录
dwProblem,确认实际错误类型,不能只根据弹窗文字假定它一定是12057。 - 例外只适用于预先限定并验证的目标站点,同时要考虑重定向、框架和其他 HTTPS 资源。
- 对证书已吊销、主机名不匹配、过期或不受信任等其他错误,仍应中止访问。
OnSecurityProblem 的参数只有错误码,无法单凭它核验当前请求的 URL 和证书,也不能直接用 LocationURL 判断当前失败请求的身份。
返回 RPC_E_RETRY 意味着接受相关安全问题并继续访问,会削弱对应检查的保护,也不会修复证书或网络故障。微软明确要求谨慎使用这一返回值。参考:OnSecurityProblem 安全说明
部署前需要确认的事项
应在实际出现弹窗的环境中,验证 QueryService 和 OnSecurityProblem 是否被调用。不同 IE/MSHTML 版本及安全提示路径可能存在差异。如果回调没有触发,仅增加这些方法不能说明问题已经解决。
这套接口只能处理进入相应宿主回调的安全问题,无法保证屏蔽所有浏览器或系统对话框。对于数千客户端的部署,至少要验证正常证书、吊销信息不可达、证书已吊销,以及重定向场景。
不宜为了单个控件直接改写用户级 Internet Options 或关闭全局吊销检查。这些修改会影响其他使用相关设置的程序,也可能受到组织策略约束。
备注:内容仅供参考。
