mirror of
https://github.com/electron/electron.git
synced 2026-10-28 11:44:46 +08:00
docs: document preload code cache threat model (#54654)
Explain why SetFromRenderer stores the renderer-produced blob unverified and that the (BrowserContext, ProcessLock, id) scope is the security boundary. Claude-Session: https://claude.ai/code/session_018A4Mt59Nr4h4WSFjyNNkxb Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
6b48d9813b
commit
cba5248898
2 files changed
+18
-1
No files matched your search
@@ -254,6 +254,21 @@ void SetFromRenderer(content::RenderFrameHost* rfh,
|
||||
// first launch) just writes identical bytes twice; harmless and rare, and
|
||||
// SetFromRenderer() is UI-thread serialized so it's not a data race.
|
||||
Entry entry;
|
||||
// Security note: |blob| is renderer-produced and stored without
|
||||
// verification. This is intentional and matches Chromium's
|
||||
// GeneratedCodeCache (content/browser/renderer_host/code_cache_host_impl.cc).
|
||||
// The served-preload and source hash checks above only decide whether this
|
||||
// write is accepted and which source hash it is filed under. They do not,
|
||||
// and cannot, prove |blob| was compiled from that source (V8's CachedData
|
||||
// source check is length-only). Containment comes from the key instead:
|
||||
// entries are scoped to (BrowserContext, ProcessLock, preload id), with the
|
||||
// lock taken from the browser-side RenderProcessHost (see ScopeForFrame()),
|
||||
// so a write is only ever served back to frames in the same BrowserContext
|
||||
// whose process has an equal ProcessLock. A compromised renderer can
|
||||
// therefore only replace bytecode for its own principal's sandboxed preload,
|
||||
// whose capabilities it already holds. Persistence across restarts is the
|
||||
// accepted cost, as in Chromium. Serving an entry across a different
|
||||
// BrowserContext or ProcessLock WOULD be a security bug.
|
||||
entry.source_hash = hash_it->second;
|
||||
entry.blob.assign(blob.begin(), blob.end());
|
||||
|
||||
|
||||
@@ -35,7 +35,9 @@ std::string IdForWebPreferencesPreload(const base::FilePath& path);
|
||||
// origin keying, sandboxing, guest and cross-origin-isolation status,
|
||||
// storage partition). Entries are only ever served back within an equal
|
||||
// scope, so a blob produced by one principal's renderer is never handed to
|
||||
// another. |dir| is empty for off-the-record contexts (memory tier only).
|
||||
// another. Scope is the security boundary for cached blobs; see the note in
|
||||
// SetFromRenderer(). |dir| is empty for off-the-record contexts (memory tier
|
||||
// only).
|
||||
struct Scope {
|
||||
Scope();
|
||||
Scope(const Scope&);
|
||||
|
||||
Reference in new issue
Block a user