Skip to content

Bug 2007416 - wipe individual logins on DecryptionErrors #7343

Open
bendk wants to merge 2 commits intomozilla:mainfrom
bendk:push-myztqrztlsuu
Open

Bug 2007416 - wipe individual logins on DecryptionErrors #7343
bendk wants to merge 2 commits intomozilla:mainfrom
bendk:push-myztqrztlsuu

Conversation

@bendk
Copy link
Copy Markdown
Contributor

@bendk bendk commented Apr 28, 2026

First commit is prep work, without much changes.
Second commit is the actual change.

Pull Request checklist

  • Breaking changes: This PR follows our breaking change policy
    • This PR follows the breaking change policy:
      • This PR has no breaking API changes, or
      • There are corresponding PRs for our consumer applications that resolve the breaking changes and have been approved
  • Quality: This PR builds and tests run cleanly
    • Note:
      • For changes that need extra cross-platform testing, consider adding [ci full] to the PR title.
      • If this pull request includes a breaking change, consider cutting a new release after merging.
  • Tests: This PR includes thorough tests or an explanation of why it does not
  • Changelog: This PR includes a changelog entry in CHANGELOG.md or an explanation of why it does not need one
    • Any breaking changes to Swift or Kotlin binding APIs are noted explicitly
  • Dependencies: This PR follows our dependency management guidelines
    • Any new dependencies are accompanied by a summary of the due diligence applied in selecting them.

@bendk bendk force-pushed the push-myztqrztlsuu branch from 6c5a0c6 to e8513bf Compare April 28, 2026 16:41
named_params! {
":now_millis": now_ms,
":guid": id,
":sync_status": SyncStatus::Changed as u8,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

touch() now also updates the sync_status column. I needed this to make the tests pass, but it seems like the code should have always done that to me. Maybe we were just not using this method enough to notice?

Ok(Some(ServerTimestamp(millis)))
Ok(db
.get_meta::<i64>(schema::LAST_SYNC_META_KEY)?
.map(ServerTimestamp))
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This also needed changes to make the tests pass, because it seemed like the old code was not correct. Before this would panic if there was no metadata for LAST_SYNC_META_KEY, now it returns None.

self.execute("DELETE FROM loginsM WHERE guid=?", [guid])?;
// Delete the LAST_SYNC_META_KEY so that we resync the logins data next time
self.delete_meta(schema::LAST_SYNC_META_KEY)?;
tx.commit()?;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There's lots of incidental changes in this PR, but this is the main change.

@bendk bendk requested a review from mhammond April 28, 2026 16:46
Reworked how we handle `EncryptorDecryptor`:

* Don't pass `EncryptorDecryptor` to database functions, this is
  redundant because the DB has it's own enc_dec arc.
* Make `EncryptorDecryptor::decrypt_login` input a `LoginDb`.  This
  enables a future change where we wipe the login on decryption
  failures.

Most of this is just a pure refactor.  The one real change is that we
need to lock the store to get a `LoginDb` a bit more in the sync engine
code.
@bendk bendk force-pushed the push-myztqrztlsuu branch 2 times, most recently from 99137dd to aac821a Compare May 6, 2026 20:49
username: "cool_username".into(),
password: "hunter2".into(),
..Default::default()
})
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think I probably broke this test and I'm not sure exactly how to fix it or even run it (do I need to create a burner sync account for this?).

Is this still worthwhile to keep? It seems a bit redundant with the TPS tests. I can definitely fix this if it's providing value, but I want to make sure someone's using it before I do that.

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.

I don't think it's redundant with TPS because that's primarily just testing the existing desktop stuff, so the Rust components don't get much coverage - ie, this was intended to kinda be "TPS but for rust components" :) It uses the same auth as that fxa example, so should start an oauth flow and remember the account in .fxa-cli-credentials.json (although I don't recall how it manages 2 accounts!). It doesn't have much value as it's not run automatically etc, so in that regard it is even more like TPS (which also doesn't run in CI, so has questionable value, but we're also reluctant to just kill that because there is some value there)

I'd actually be surprised if it hasn't already gone stale, so I'd be fine with even just leaving a comment here explaining you think it might be broken and a reference to this PR, and we can have a longer discussion about it later.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right it was already stale before this. I tested this on main and it's also failing with:

thread 'main' (3767285) panicked at testing/sync-test/src/logins.rs:278:5:
assertion failed: c1.logins_store.delete(&l0id).expect("Delete should work")

I'm thinking we should leave the code as-is, create a ticket for it, and update the readme.

Copy link
Copy Markdown
Member

@mhammond mhammond left a comment

Choose a reason for hiding this comment

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

I'm still not entirely convinced wiping is the right thing to do here - I recall we started a discussion but don't remember it concluding - I think we agreed to rope in the credentials management team - did they say we should take this route? I'm fine with this if they did.

username: "cool_username".into(),
password: "hunter2".into(),
..Default::default()
})
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.

I don't think it's redundant with TPS because that's primarily just testing the existing desktop stuff, so the Rust components don't get much coverage - ie, this was intended to kinda be "TPS but for rust components" :) It uses the same auth as that fxa example, so should start an oauth flow and remember the account in .fxa-cli-credentials.json (although I don't recall how it manages 2 accounts!). It doesn't have much value as it's not run automatically etc, so in that regard it is even more like TPS (which also doesn't run in CI, so has questionable value, but we're also reluctant to just kill that because there is some value there)

I'd actually be surprised if it hasn't already gone stale, so I'd be fine with even just leaving a comment here explaining you think it might be broken and a reference to this PR, and we can have a longer discussion about it later.

@bendk
Copy link
Copy Markdown
Contributor Author

bendk commented May 7, 2026

I'm still not entirely convinced wiping is the right thing to do here - I recall we started a discussion but don't remember it concluding - I think we agreed to rope in the credentials management team - did they say we should take this route? I'm fine with this if they did.

I'm not sure either, but my memory is that we agreed this was an okay short-term step since it's the essentially the same behavior as when the canary check fails. Long-term, we should have the discussion with more teams and we may decide to go the other route: keep the DB row around and present it to the user as a login with missing data (or maybe some 3rd route). There actually is some urgency to this since the logins success rates are pretty low and most of the failures are decryption errors.

That said, I do think it's a good idea to get the OK from credential management on this. WYDT @jo?

BTW, here's the bugzilla bug for the longer-term decision.

@bendk bendk requested a review from jo May 7, 2026 13:03
Also:
  * Set sync_status in `LoginsDb::touch()`
  * Handle missing row for LAST_SYNC_META_KEY in `get_last_sync()`
@bendk bendk force-pushed the push-myztqrztlsuu branch from aac821a to aa3939e Compare May 7, 2026 13:11
@jo
Copy link
Copy Markdown
Contributor

jo commented May 7, 2026

Oh, I think that's a tricky issue.

I'd prefer to control that via a feature flag so we can enable it selectively. I'd be reluctant to have that enabled on the desktop for now.

We often have problems getting the key from the OSKeyStore as well. Here, with form autofill data, we had actually implemented a similar mechanism and had just removed it when we discovered it. In this case, if no key was returned, a new one was created and stored immediately, which actively reset the credit card data. With OSKeyStore, there are many different reasons why the key might not be available at the moment. https://sql.telemetry.mozilla.org/queries/113887 lists 29 different errors only for Darwin. While this all pertains to OSKeyStore, I can imagine that similar issues exist on iOS or Android, and that the keys may simply be temporarily unavailable under certain circumstances.

For the desktop, we have at least roughly considered various future backup strategies, partly for other reasons as well. One option is via FxA, with manual user interaction. Another possibility is backup codes (as with GH or 1Password) or the primary password.

In this context, I could also imagine that we’d at least wait to wipe the data until we’re syncing, right? That way, there’s a high probability the data will be restored immediately.

By the way, thanks a lot for the groundwork - I didn’t dare touch that back then :) It’s much cleaner this way.

So, to cut a long story short: if we enable this with a feature flag, which we’d initially activate only for Android and/or iOS, I’d be okay with that as an interim solution.

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