JavaScript验证服务器公钥哈希的可行性及证书链验证方案探讨
Great question, and I’m sorry to hear about those rough experiences with certificate hijacking—those are exactly the kind of edge cases that expose gaps in how we trust pre-installed certs and signed software. Let’s break down your questions and the feasible options available:
Can JavaScript verify a server’s public key or let users check its hash?
Yes, but with significant browser-specific limitations, plus some workarounds for cross-browser support:
1. Firefox’s getSecurityInfo (Direct, but Firefox-Only)
You’re right that Firefox’s non-standard navigator.getSecurityInfo() API is the only built-in way for JavaScript to directly access certificate chain details, including public key hashes. Here’s a quick example to fetch the server’s public key SHA-256 hash:
async function fetchServerPublicKeyHash() { try { // Only works in Firefox, requires HTTPS const securityInfo = await navigator.getSecurityInfo(); const serverCert = securityInfo.certificates[0]; // First cert is the server's own // Export public key as raw SPKI format const publicKeyRaw = await serverCert.publicKey.export({ type: 'spki', format: 'raw' }); // Generate SHA-256 hash const hashBuffer = await crypto.subtle.digest('SHA-256', publicKeyRaw); const hashHex = Array.from(new Uint8Array(hashBuffer)) .map(byte => byte.toString(16).padStart(2, '0')) .join(''); console.log('Server Public Key SHA-256:', hashHex); return hashHex; } catch (err) { console.error('Failed to retrieve security info:', err); return null; } }
Caveats: This is not a web standard, so it won’t work in Chrome, Edge, Safari, etc. Mozilla could also modify or remove it in future updates.
2. Cross-Browser Workarounds
Since standard JavaScript intentionally restricts low-level key access (to prevent malicious scripts from abusing certificate data), you’ll need application-layer hacks:
Pre-stored Trusted Hashes + Backend Validation: Embed a hardcoded list of trusted server public key hashes in your frontend. Then, add a backend endpoint (e.g.,
/.well-known/trusted-key-hash) that returns the current server’s public key hash. Your frontend can fetch this value and compare it to the pre-stored list.
Note: This only works if you trust that the backend hasn’t been compromised, and it won’t detect cases where a malicious root cert is forcing a man-in-the-middle attack (since the MITM would also tamper with the endpoint’s response).Manual User Verification: Display the server’s public key hash prominently on critical pages (e.g., login, banking transactions) and guide users to manually verify it via their browser’s certificate viewer:
- Click the lock icon in your address bar
- Select "Certificate" → "Details"
- Navigate to "Public Key" → "Fingerprint"
- Compare this value to the one shown on our page
This is the most reliable cross-browser method, but it’s not user-friendly and requires technical literacy.
Browser Extensions: Build a browser extension that accesses browser-specific certificate APIs (e.g., Chrome’s
chrome.webRequestto inspect certificates). The extension can auto-compare the server’s key hash to a trusted list and alert users to mismatches. However, this requires users to install the extension, which adds friction.
Can we verify the certificate chain has no malicious tampering via application-layer JS?
Unfortunately, this is extremely tricky for two main reasons:
- Browser Security Restrictions: Standard JS can’t access the full certificate chain or metadata about whether a root cert is pre-installed vs. user-added. Browsers hide this to prevent malicious scripts from fingerprinting users or exploiting trust gaps.
- Pre-Installed Malicious Certs: If an attacker has planted a trusted root cert (like the Fortigate case), the browser will treat the MITM’s certificate as valid, and JS has no way to detect this unless you can pre-validate the entire expected certificate chain.
Partial Solution (Firefox-Only)
Using getSecurityInfo, you can fetch the full certificate chain and compare each cert’s hash to a pre-stored list of trusted CAs/roots. If any cert in the chain isn’t on your trusted list, you can warn the user. But again, this only works in Firefox and requires maintaining an up-to-date list of trusted certs.
内容的提问来源于stack exchange,提问作者Charles Lohr

