Skip to content

feat(http): add SPKI pinning extension (fix #3414) - #3539

Open
patlux wants to merge 14 commits into
tauri-apps:v2from
patlux:3414-http-spki-pinning
Open

patlux wants to merge 14 commits into
tauri-apps:v2from
patlux:3414-http-spki-pinning

Conversation

@patlux

@patlux patlux commented Aug 17, 2026

Copy link
Copy Markdown

Motivation

Implements the SPKI pinning request from #3414 on top of the native transport extension API in the preceding PR.

The JavaScript fetch API remains unchanged. Pin configuration is native Rust code and cannot be disabled by frontend request options.

Security behavior

  • Normal WebPKI certificate-chain, validity, signature, and hostname validation runs first.
  • The SHA-256 digest of the leaf certificate's DER SubjectPublicKeyInfo is then matched against exact-host pins.
  • Multiple pins per host support safe server-key rotation.
  • Every HTTPS host, including redirect targets, requires pins by default.
  • The client is HTTPS-only by default, preventing downgrade redirects.
  • allow_unpinned_hosts() and allow_http() are explicit native opt-ins.
  • Dangerous certificate and hostname settings are rejected while the extension is installed.
  • Failures are returned as structured extension errors through the unchanged plugin IPC API.

API

let pinning = tauri_plugin_http::SpkiPinning::new()
    .pin("api.example.com", "sha256/CURRENT_BASE64_SPKI_HASH")?
    .pin("api.example.com", "sha256/BACKUP_BASE64_SPKI_HASH")?;

tauri::Builder::default().plugin(
    tauri_plugin_http::Builder::new()
        .extension(pinning)
        .build(),
);

The implementation is behind the optional spki-pinning Cargo feature, so applications that do not use it do not receive its Rustls, X.509, and hashing dependencies.

Depends on #3538 and should be reviewed as a stacked change. The second commit contains the SPKI-specific delta; once #3538 merges, this PR automatically reduces to that commit.

@patlux
patlux requested a review from a team as a code owner August 17, 2026 17:25
@socket-security

socket-security Bot commented Aug 17, 2026 •

Copy link
Copy Markdown

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

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcargo/​rcgen@​0.13.29610093100100
Addedcargo/​x509-parser@​0.18.110010093100100

View full report

reqwest uses a preconfigured rustls config as-is and only sets ALPN on
configs it builds itself, so pinned requests always fell back to HTTP/1.1.
Pinning only trusted webpki-roots, so servers with a private or
self-signed CA could never be pinned and apps lost OS roots.
The verifier encoded the failure as marker text in rustls::Error::General
and map_transport_error searched the error chain's messages for it. Carry
a typed error in rustls::Error::Other and downcast it instead.
validate looked up the URL host verbatim while pins and the verifier strip
the trailing dot, so https://api.example.com./ was rejected as unpinned.
configure rebuilt the root store, WebPKI verifier and rustls config on
every request. Build it on first use and reset it when a builder method
changes the settings it depends on.
The README example used pins that fail to parse, and nothing told JS
callers that extension errors reject fetch with a plain object.
…LOWED

The https_only client stopped the redirect, but JS received reqwest's
plain error string instead of the structured pinning error.
Comment thread plugins/http/src/lib.rs
mod spki_pinning;

#[cfg(feature = "spki-pinning")]
pub use rustls;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

if rustls starts pumping out breaking changes i'll lose my fucking mind

Comment on lines +29 to +30
/// Configures the final request client after the plugin has applied its
/// timeout, redirect, proxy, cookie, and feature-gated TLS settings.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

do we want some policy like on https://docs.rs/tauri/latest/tauri/webview/struct.WebviewWindow.html#method.with_webview so we can upgrade reqwest in minors?

@github-actions

Copy link
Copy Markdown
Contributor

Package Changes Through d7d55ce

There are 25 changes which include barcode-scanner with patch, barcode-scanner-js with patch, biometric with minor, biometric-js with minor, clipboard-manager with minor, clipboard-manager-js with minor, deep-link with patch, deep-link-js with patch, fs with patch, fs-js with patch, notification with minor, notification-js with minor, opener with patch, opener-js with patch, geolocation with minor, geolocation-js with minor, http with minor, http-js with minor, single-instance with patch, sql with minor, sql-js with minor, torch with minor, torch-js with minor, updater with minor, updater-js with minor

Planned Package Versions

The following package releases are the planned based on the context of changes in this pull request.

package current next
api-example 2.0.55 2.0.56
api-example-js 2.0.51 2.0.52
deep-link-example-js 2.2.16 2.2.17
barcode-scanner 2.5.2 2.5.3
barcode-scanner-js 2.5.2 2.5.3
biometric 2.4.2 2.5.0
biometric-js 2.4.2 2.5.0
clipboard-manager 2.4.2 2.5.0
clipboard-manager-js 2.4.2 2.5.0
deep-link 2.6.1 2.6.2
deep-link-js 2.6.1 2.6.2
fs 2.7.0 2.7.1
fs-js 2.7.0 2.7.1
dialog 2.8.2 2.8.3
dialog-js 2.8.2 2.8.3
geolocation 2.4.2 2.5.0
geolocation-js 2.4.2 2.5.0
opener 2.7.1 2.7.2
opener-js 2.7.1 2.7.2
http 2.8.2 2.9.0
http-js 2.8.2 2.9.0
notification 2.6.0 2.7.0
notification-js 2.6.0 2.7.0
persisted-scope 2.4.1 2.4.2
single-instance 2.5.3 2.5.4
sql 2.5.0 2.6.0
sql-js 2.5.0 2.6.0
updater 2.13.3 2.14.0
updater-js 2.13.3 2.14.0
torch 2.0.0 2.1.0
torch-js 2.0.0 2.1.0

Add another change file through the GitHub UI by following this link.


Read about change files or the docs at github.com/jbolda/covector

@FabianLars

Copy link
Copy Markdown
Member

ngl this (or rather #3538) feels so over-engineered 😅 a whole extentions system sounds like you actually want users to just use reqwest themselves at this point.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants