WordPress / WordPress/two-factor

Privacy: register a personal data exporter and eraser

Open
#954 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
825
Forks
187
Avg merge
2d 11h
Merged PRs (30d)
17

Description

Two Factor doesn't hook into the export or erasure tools

#869 added suggested privacy policy text in 0.17.0. That covers telling people what Two Factor stores.

WordPress also has two tools for acting on that: Personal Data Export and Personal Data Erasure, both added in 4.9.6. This plugin does not hook into either one. So when an admin runs an export or an erasure request, nothing from Two Factor shows up, even though it keeps authentication records for every user.

The same question is about to land on a lot of other plugins — see below.

What Two Factor stores per user

Meta key Holds
_two_factor_enabled_providers Enabled methods
_two_factor_provider Primary method
_two_factor_totp_key TOTP secret
_two_factor_totp_last_successful_login Replay-protection timestamp
_two_factor_backup_codes Hashed backup codes
_two_factor_email_token Hashed email login code
_two_factor_email_token_timestamp When that code was sent
_two_factor_nonce Login nonce
_two_factor_last_login_failure Rate-limit timestamp
_two_factor_failed_login_attempts Failed attempt counter
_two_factor_password_was_reset Compromised-password reset flag

What the export should say

An export file gets emailed around and sits in downloads folders. It should say what Two Factor holds without handing over anything useful to an attacker:

  • Enabled methods and primary method: the values as stored
  • TOTP: say it is set up, never include the secret
  • Backup codes: say how many are left, never the codes or their hashes
  • Email login code: say when it was sent, do not include the stored hash
  • Failed attempts and last failure time: as stored

The question: erase the credentials, or keep them?

First, what the tool actually does. Erasing personal data is not deleting the account. Core registers one eraser, wp_register_comment_personal_data_eraser, and it anonymizes comment metadata. Nothing in the flow deletes the user, ends their sessions, or notifies anyone. When the request finishes, the account still exists and still logs in.

So: a request comes in, the account survives it, and 2FA is still switched on. What should Two Factor's eraser do?

Option A: erase the short-lived records, keep the credentials.

Delete the login nonce, the failed attempt counter, the rate-limit timestamp, the password-reset flag, the pending email code, and the TOTP replay timestamp. Keep the enabled methods, the TOTP secret, and the backup codes, and report them through items_retained with a message saying so.

Option B: erase everything.

A request to erase means erase. If 2FA stops working, that is the requester's call, not Two Factor's.

Why Option A is best

Two Factor's eraser gets an email address. It cannot tell which of two situations it is in:

  1. The admin is also deleting the account.

  2. The admin is not deleting the account, which is the default.

The decision to retain some data or delete it all only matters in the second situation, when the user account is not to be deleted. In the first, wp_delete_user() removes every usermeta row Two Factor owns, so both options end the same way. In the second, it never runs and nothing else clears those rows, so if Option B is followed (personal data erasure required = erase everything), this quietly weakens the security of an active user account. The user still may log in, now with only a password. The second factor is gone without notification to anyone.

Other plugins will face the same question

Over in the Plugin Check repo, @masteradhoc's issues WordPress/plugin-check#1251 and WordPress/plugin-check#1252 ask for checks that warn developers of any plugin storing personal data if it has not registered callbacks on the wp_privacy_personal_data_exporters and wp_privacy_personal_data_erasers filters. Both checks have implementations open in WordPress/plugin-check#1292 and WordPress/plugin-check#1293. A third, WordPress/plugin-check#1291, covers the privacy policy text this plugin already added in #869. If they land, a lot of plugin authors are going to sit down and write an eraser, many of them for the first time.

The obvious way to write one is to delete every meta key you own. For most plugins that is fine. For anything holding a credential it is not, and 2FA is the clearest case of it.

Why items_retained fits

It is the field for exactly this case: data you are keeping, disclosed plainly rather than kept quietly.

Core registers the filters, providers contribute their own data and keys

Two_Factor_Core registers both privacy filters, wp_privacy_personal_data_exporters and wp_privacy_personal_data_erasers, and handles its own meta keys. Providers add their own export data and eraser keys through two optional methods on Two_Factor_Provider, privacy_export_data() and privacy_eraser_user_meta_keys(). That follows the existing uninstall_user_meta_keys() pattern, so no provider-specific code ends up in Two_Factor_Core. Core also exposes format_privacy_timestamp() so providers render dates the same way.

Branch: https://github.com/dknauss/two-factor/tree/add/privacy-exporter-eraser

It has 15 tests covering both the exporter and the eraser. Three of them take the whole exported payload and check that the TOTP secret, the generated backup codes, and the stored email token hash appear nowhere in it.

@masteradhoc, since #869 was yours, this looks like your call.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the add/privacy-exporter-eraser branch and inspect Two_Factor_Core, Two_Factor_Provider, and the 15 privacy tests mentioned in the issue. Review the exporter and eraser callbacks, provider extension methods, and timestamp formatting, then confirm the tests cover the specified retained data and exclude secrets from exported output.

Written by the indexing model from the issue text.

Assessment

Tech stack
php, wordpress
Domain
authentication, backend, security
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.