Skip to content

Conversation

@leeland-turnkey
Copy link
Contributor

@leeland-turnkey leeland-turnkey commented Dec 19, 2025

Add Subresource Integrity (SRI) to export-and-sign frame

Summary

This PR adds Subresource Integrity (SRI) hashes to all loaded resources (JavaScript and CSS files) in the export-and-sign frame to prevent tampering. While the resources are currently in the same trust zone as the main HTML page, adding SRI now prepares the solution for future iframe integrity validation capabilities.

Background

The generated HTML page of the export-and-sign frame references several resources (JavaScript files and a CSS file). Without Subresource Integrity, these resources could potentially be tampered with, even if they're served from the same origin. While this is currently an informational issue since resources are first-party, SRI will become critical for solution soundness if validation of iframe integrity becomes possible in the future.

Changes

  • Added webpack-subresource-integrity plugin as a dev dependency
  • Configured webpack to automatically generate and inject SHA-384 integrity hashes for all JavaScript and CSS assets
  • Added crossOriginLoading: "anonymous" to webpack output configuration (required for SRI to function)

The plugin automatically:

  • Calculates SHA-384 hashes for all bundled assets during the build process
  • Injects integrity and crossorigin="anonymous" attributes into <script> and <link> tags in the generated HTML
  • Works seamlessly with html-webpack-plugin to add integrity attributes to all loaded resources

Implementation Details

  • Uses SHA-384 as the hash algorithm, which is the W3C recommended baseline for Subresource Integrity
  • SRI is only enabled in production builds (via the existing production mode check)
  • All resources now include integrity attributes like in index.html
image

References

@socket-security
Copy link

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedwebpack-subresource-integrity@​5.2.0-rc.110010010080100

View full report

@@ -1 +1 @@
<!doctype html><html class="no-js"><head><link rel="icon" type="image/svg+xml" href="./favicon.svg"/><meta charset="utf-8"/><title>Turnkey Export</title><meta name="viewport" content="width=device-width,initial-scale=1"/><meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self'; base-uri 'self'; object-src 'none'; form-action 'none'"><link href="/styles.e084a69a94c0575bc6ba.css" rel="stylesheet"></head><body><h2>Export Key Material</h2><p><em>This public key will be sent along with a private key ID or wallet ID inside of a new <code>EXPORT_PRIVATE_KEY</code> or <code>EXPORT_WALLET</code> activity</em></p><form><label>Embedded key</label> <input name="embedded-key" id="embedded-key" disabled="disabled"/> <button id="reset">Reset Key</button></form><br/><br/><br/><h2>Inject Key Export Bundle</h2><p><em>The export bundle comes from the parent page and is composed of a public key and an encrypted payload. The payload is encrypted to this document's embedded key (stored in local storage and displayed above). The scheme relies on <a target="_blank" href="https://datatracker.ietf.org/doc/rfc9180/">HPKE (RFC 9180)</a></em>.</p><form><label>Bundle</label> <input name="key-export-bundle" id="key-export-bundle"/> <button id="inject-key">Inject Bundle</button><br/><label>Key Format</label> <select id="key-export-format" name="key-export-format"><option value="HEXADECIMAL">Hexadecimal (Default)</option><option value="SOLANA">Solana</option></select><br/><label>Organization Id</label> <input name="key-organization-id" id="key-organization-id"/></form><br/><br/><h2>Inject Wallet Export Bundle</h2><p><em>The export bundle comes from the parent page and is composed of a public key and an encrypted payload. The payload is encrypted to this document's embedded key (stored in local storage and displayed above). The scheme relies on <a target="_blank" href="https://datatracker.ietf.org/doc/rfc9180/">HPKE (RFC 9180)</a></em>.</p><form><label>Bundle</label> <input name="wallet-export-bundle" id="wallet-export-bundle"/> <button id="inject-wallet">Inject Bundle</button><br/><label>Organization Id</label> <input name="wallet-organization-id" id="wallet-organization-id"/></form><br/><br/><h2>Sign Transaction</h2><p><em>Input a serialized transaction to sign.</em></p><form><label>Transaction</label> <input name="transaction-to-sign" id="transaction-to-sign"/> <button id="sign-transaction">Sign</button></form><br/><br/><h2>Sign Message</h2><p><em>Input a serialized message to sign.</em></p><form><label>Message</label> <input name="message-to-sign" id="message-to-sign"/> <button id="sign-message">Sign</button></form><br/><br/><h2>Message log</h2><p><em>Below we display a log of the messages sent / received. The forms above send messages, and the code communicates results by sending events via the <code>postMessage</code> API.</em></p><div id="message-log"></div><div id="key-div"></div><script defer="defer" src="/bundle.91d8af54dfd168462387.js"></script><script defer="defer" src="/bundle.e1173f1348c1733d7051.js"></script></body></html> No newline at end of file
<!doctype html><html class="no-js"><head><link rel="icon" type="image/svg+xml" href="./favicon.svg"/><meta charset="utf-8"/><title>Turnkey Export</title><meta name="viewport" content="width=device-width,initial-scale=1"/><meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self'; base-uri 'self'; object-src 'none'; form-action 'none'"><link href="/styles.e084a69a94c0575bc6ba.css" rel="stylesheet" integrity="sha384-uIrxQTbBoDAwjgotQ+GUHgbxFM2iajB5QKNa4WuL9wn/Ou+2383e3dM2FCWOAq9m" crossorigin="anonymous"></head><body><h2>Export Key Material</h2><p><em>This public key will be sent along with a private key ID or wallet ID inside of a new <code>EXPORT_PRIVATE_KEY</code> or <code>EXPORT_WALLET</code> activity</em></p><form><label>Embedded key</label> <input name="embedded-key" id="embedded-key" disabled="disabled"/> <button id="reset">Reset Key</button></form><br/><br/><br/><h2>Inject Key Export Bundle</h2><p><em>The export bundle comes from the parent page and is composed of a public key and an encrypted payload. The payload is encrypted to this document's embedded key (stored in local storage and displayed above). The scheme relies on <a target="_blank" href="https://datatracker.ietf.org/doc/rfc9180/">HPKE (RFC 9180)</a></em>.</p><form><label>Bundle</label> <input name="key-export-bundle" id="key-export-bundle"/> <button id="inject-key">Inject Bundle</button><br/><label>Key Format</label> <select id="key-export-format" name="key-export-format"><option value="HEXADECIMAL">Hexadecimal (Default)</option><option value="SOLANA">Solana</option></select><br/><label>Organization Id</label> <input name="key-organization-id" id="key-organization-id"/></form><br/><br/><h2>Inject Wallet Export Bundle</h2><p><em>The export bundle comes from the parent page and is composed of a public key and an encrypted payload. The payload is encrypted to this document's embedded key (stored in local storage and displayed above). The scheme relies on <a target="_blank" href="https://datatracker.ietf.org/doc/rfc9180/">HPKE (RFC 9180)</a></em>.</p><form><label>Bundle</label> <input name="wallet-export-bundle" id="wallet-export-bundle"/> <button id="inject-wallet">Inject Bundle</button><br/><label>Organization Id</label> <input name="wallet-organization-id" id="wallet-organization-id"/></form><br/><br/><h2>Sign Transaction</h2><p><em>Input a serialized transaction to sign.</em></p><form><label>Transaction</label> <input name="transaction-to-sign" id="transaction-to-sign"/> <button id="sign-transaction">Sign</button></form><br/><br/><h2>Sign Message</h2><p><em>Input a serialized message to sign.</em></p><form><label>Message</label> <input name="message-to-sign" id="message-to-sign"/> <button id="sign-message">Sign</button></form><br/><br/><h2>Message log</h2><p><em>Below we display a log of the messages sent / received. The forms above send messages, and the code communicates results by sending events via the <code>postMessage</code> API.</em></p><div id="message-log"></div><div id="key-div"></div><script defer="defer" src="/bundle.91d8af54dfd168462387.js" integrity="sha384-BpsgsxZgwoEd9n/Gcfo+14fVawrwhKX51HuHHNAYPXJ2L58Co6+JQLzsvhONHfQa" crossorigin="anonymous"></script><script defer="defer" src="/bundle.bbf0ace9cfd769cdff2c.js" integrity="sha384-Ebvn0zZamBJTlVLe2Ln4jil3edE9SzwXuUp++7i2LR8Nn/zXCX3/tGw2L2uHryXk" crossorigin="anonymous"></script></body></html> No newline at end of file
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you search for integrity here, you will see the sha384.

Copy link
Collaborator

@r-n-o r-n-o left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good to me assuming we do a round of careful testing in preprod. The nice thing is that we'll see really quickly whether this breaks or not: it either loads or it does not!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants