Cacher son URL de connexion dans les emails RGPD de WordPress (et un peu plus)

Nous le savons, WordPress est un outil formidable qui propose des √©volutions toujours appr√©ciables (ou pas). La derni√®re en date, ne d√©roge pas √† la r√®gle surtout quand elle concerne la conformit√© RGPD¬†ūüėɬ†
Si vous n’avez pas encore fait le tour du sujet, voici un article de monsieur Jb Audras :¬†WordPress version 4.9.6/7 : de nouveaux outils pour l‚Äôapplication du RGPD, un autre article concernant WooCommerce :¬†WooCommerce 3.4 GDPR featureset la liste des choses √† venir :¬†Proposed Privacy Roadmap¬†
C’est quand m√™me cool tout √ßa !

Comme toutes nouvelles fonctionnalit√©s, m√™me si celle-ci facilite bien la vie aux administrateurs gr√Ęce √† une interface soign√©e, l’envers du d√©cor pour les d√©veloppeurs est un peu moins r√©jouissant :

  1. WordPress ne fournit aucune aide pour enregistrer une demande venant de votre site. Pourquoi WP le fait-il pour les commentaires et pas avec ça finalement ? Je vous rassure, la communauté a déjà pensé à ce point : GDPR Data Request Form
  2. La page ¬ę¬†Politique de confidentialit√©¬†¬Ľ est consid√©r√©e comme uniquement √©ditable par les administrateurs :¬†https://boiteaweb.fr/manage-privacy-options-roles-gdpr-105235.html
  3. Lorsqu’un utilisateur WP demande la suppression de ses donn√©es, son compte n’est ni supprim√©, ni anonymis√© et ses contenus lui sont toujours associ√©s (vaste sujet hein).
  4. Tout le reste… Exemple le ticket¬†44354¬†et ceux list√©s dedans.

Mais le plus probl√©matique pour moi √† ce moment est¬†la divulgation de l’URL de connexion dans un email ¬ę¬†public¬†¬Ľ.¬†
Voici ce que WordPress envoye aux visiteurs (n’importe qui) qui vont effectuer une demande d’export :

Bonjour,

Une demande a été faite afin d’effectuer les actions suivantes sur votre compte :

Exporter les données

Pour confirmer cela, veuillez cliquer sur le lien suivant :
https://www.monsite.test/wp-admin/wp-login.php?action=confirmaction&request_id=289&confirm_key=8Gq7SGilB2blScR7owW4

Si vous ne souhaitez pas effectuer cette action, vous pouvez ignorer et supprimer cet e-mail en tout sécurité.

Cet e-mail a été envoyé à xavier@monsite.test.

Cordialement,
L’équipe de monsite_test
https://www.monsite.test

Quel malheur de voir appara√ģtre son URL de connexion et cela m√™me si on utilise une extension pour la cacher¬†ūüė•¬†
En attendant que les extentions concern√©es (ex.:¬†Move Login) se mettent √† jour et pour ceux qui n’utilisent pas d’extension, voici comment j’ai abord√© le sujet/probl√®me :

Avant toutes choses, nous allons demander √† WP de stocker les archives d’exports ailleurs que dans le r√©pertoire par d√©faut¬†wp-personal-data-exports. C’est un peu parano mais apr√®s tout ce sont des donn√©es bien plus sensibles que les images de notre site.

/**
 * Returns the directory used to store personal data export files.
 *
 * @return string Exports directory.
 */
function thistle_privacy_exports_dir() {
    $upload_dir  = wp_upload_dir();
    $exports_dir = trailingslashit( $upload_dir['basedir'] ) . 'gprd-exports/';

    return apply_filters( 'thistle_privacy_exports_dir', $exports_dir );
}

/**
 * Returns the URL of the directory used to store personal data export files.
 *
 * @return string Exports directory URL.
 */
function thistle_privacy_exports_url() {
    $upload_dir  = wp_upload_dir();
    $exports_url = trailingslashit( $upload_dir['baseurl'] ) . 'gprd-exports/';

    return apply_filters( 'thistle_privacy_exports_url', $exports_url );
}

/**
 * Overrides some default WP choices:
 * - the directory used to store personal data export files.
 * - the URL of the directory used to store personal data export files.
 */
add_filter( 'wp_privacy_exports_dir', 'thistle_privacy_exports_dir' );
add_filter( 'wp_privacy_exports_url', 'thistle_privacy_exports_url' );

Evidemment, vous pouvez remplacer¬†gprd-exports¬†par ce que vous voulez ūüėČ

Maintenant, le plus important, nous allons demander √† WP d’enregistrer deux alias :

  1. le premier pour cacher¬†wp-login.php. Ce qui signifie qu’en nous rendant √† cette adresse¬†https://www.monsite.test/donnes-personnelles/, nous verrons notre page de confirmation d’action RGPD.
  2. le second pour cacher le dossier o√Ļ sont stock√©es les archives des donn√©es export√©es. Les archives stock√©es dans le r√©pertoire¬†gprd-exports¬†seront donc aussi accessibles sur l’URL¬†https://www.monsite.test/donnes-personnelles/export/*.zip.
/**
 *
 *
 * @return string
 */
function thistle_gprd_make_link_relative( $link ) {
  return ltrim( wp_make_link_relative( $link ), '/' );
}

/**
 * Returns the public URL which will be used by the users to confirm
 * their action about their personal data.
 *
 * @return string
 */
function thistle_privacy_confirm_public_url() {
  $confirm_url = home_url( 'donnees-personnelles/' );

  return apply_filters( 'thistle_privacy_confirm_public_url', $confirm_url );
}

/**
 * Returns the public URL which will be used by the users to download
 * their personal data export file.
 *
 * @return string
 */
function thistle_privacy_exports_public_url() {
  $exports_url = trailingslashit( thistle_privacy_confirm_public_url() ) . 'export/';

  return apply_filters( 'thistle_privacy_exports_public_url', $exports_url );
}

/**
 * Adds rewrite rules to hide :
 *  - the URL of login page
 *  - the URL of the directory used to store personal data export files.
 *
 * for non-registered users. It's a bit paranoiac, but if you have activated a plugin
 * to hide/move your login page, you will not want to display it in a public email ;)
 *
 * @param void
 * @return void
 */
function thistle_rewrite_privacy_urls() {
    // Export URL
    $exports_rel_url = thistle_gprd_make_link_relative( thistle_privacy_exports_public_url() );
    $wp_privacy_exports_rel_url = thistle_gprd_make_link_relative( wp_privacy_exports_url() );

    add_rewrite_rule( $exports_rel_url . '([^/]+)\.zip$', $wp_privacy_exports_rel_url . '$1.zip', 'top' );

    // Confirm URL
    $confirm_rel_url = thistle_gprd_make_link_relative( thistle_privacy_confirm_public_url() );
    $wp_privacy_confirm_rel_url = thistle_gprd_make_link_relative( site_url() . '/wp-login.php' );

    add_rewrite_rule( $confirm_rel_url . '?$', $wp_privacy_confirm_rel_url, 'top' );
}
add_action( 'init', 'thistle_rewrite_privacy_urls' );

Ces r√®gles concid√©r√©es comme¬†non_wp_rules¬†(car elles ne concernent pas¬†index.php) iront s’√©crire dans votre fichier¬†.htaccess¬†entre¬†# BEGIN WordPress¬†et¬†# END WordPress¬†comme ci-dessous :

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteRule ^donnees-personnelles/export/([^/]+)\.zip$ /wp-content/uploads/gprd-exports/$1.zip [QSA,L]
RewriteRule ^donnees-personnelles/?$ /wp-admin/wp-login.php [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

C’est bien joli d’avoir cette nouvelle URL¬†https://www.monsite.test/donnes-personnelles/¬†mais il va falloir imposer quelques r√®gles pour que les gens mal-intentionn√©s ne puissent pas y acc√©der directement et voir notre boite de connexion. De toute fa√ßon, il n’y a aucun int√©r√™t √† acc√©der √† cette page si ce n’est pas pour confirmer une action RGPD.

/**
 * Handles access to the new public URL which receives confirm actions from users for their personal data.
 *
 * Forbids direct or wrong formatted requests.
 *
 * @param void
 * @return void
 */
function thistle_handle_privacy_confirm_action() {
  $confirm_url_pattern = '/' . trim( thistle_gprd_make_link_relative( thistle_privacy_confirm_public_url() ), '/' );
  if ( mb_strpos( $_SERVER['REQUEST_URI'], $confirm_url_pattern ) !== 0 ) {
    return;
  }

  $wp_privacy_action = isset( $_REQUEST['action'], $_REQUEST['request_id'], $_REQUEST['confirm_key'] ) && ! empty( $_REQUEST['action'] ) && ! empty( $_REQUEST['request_id'] ) && ! empty( $_REQUEST['confirm_key'] );
  if ( ! $wp_privacy_action ) {
    wp_die( __( 'Invalid request.' ) );
  }

  if ( $_REQUEST['action'] != 'confirmaction' || ! intval( $_REQUEST['request_id'] ) ) {
    wp_die( __( 'Invalid request.' ) );
  }
}
add_filter( 'init', 'thistle_handle_privacy_confirm_action', 9 );

Avec ces quelques lignes, si quelqu’un arrive sur notre nouvelle URL sans faire une demande de confirmation sur une action RGPD valide, celui-ci sera en t√™te √† t√™te avec un message d’erreur.

Apr√®s tout √ßa, il ne reste plus qu’√† g√©rer l’essentiel :¬†remplacer les URLs¬†dans les emails envoy√©s par WordPress.

  1. l’URL de confirmation d’une action RGPD via le filtre¬†user_request_action_email_content.
  2. l’URL de t√©l√©chargement de l’archive contenant nos donn√©es personnelles via le filtre¬†user_request_action_email_content.
/**
 * Overrides the confirmation link into the email sent to the user
 * when an account action is attempted.
 *
 * @param string $email_text Text in the email.
 * @param array  $email_data {
 *     Data relating to the account action email.
 *
 *     @type WP_User_Request $request     User request object.
 *     @type string          $email       The email address this is being sent to.
 *     @type string          $description Description of the action being performed so the user knows what the email is for.
 *     @type string          $confirm_url The link to click on to confirm the account action.
 *     @type string          $sitename    The site name sending the mail.
 *     @type string          $siteurl     The site URL sending the mail.
 * }
 * @return string Text in the email.
 */
function thistle_override_privacy_confirm_url( $email_text, $email_data ) {
    $confirm_url_query = parse_url( $email_data['confirm_url'], PHP_URL_QUERY );
    $confirm_url = thistle_privacy_confirm_public_url() . '?' . $confirm_url_query;

    $email_text = str_replace( '###CONFIRM_URL###', esc_url_raw( $confirm_url ), $email_text );

    return $email_text;
}
add_filter( 'user_request_action_email_content', 'thistle_override_privacy_confirm_url', 10, 2 );

/**
 * Overrides the URL of the personal data export file into the email sent to the user.
 *
 * @param string $email_text Text in the email.
 * @param int    $request_id The request ID for this personal data export.
 * @return string
 */
function thistle_override_privacy_file_url( $email_text, $request_id ) {
    $export_file_url = get_post_meta( $request_id, '_export_file_url', true );
    $export_file_url = str_replace( wp_privacy_exports_url(), thistle_privacy_exports_public_url(), $export_file_url );

    $email_text = str_replace( '###LINK###', esc_url_raw( $export_file_url ), $email_text );

    return $email_text;
}
add_filter( 'wp_privacy_personal_data_email_content', 'thistle_override_privacy_file_url', 10, 2 );

Je pensais qu’avec ce ticket¬†44353¬†nous acc√®derions √† une fa√ßon plus ¬ę¬†simple¬†¬Ľ de proc√©der au remplacement de l’URL de confirmation mais non. Et les tickets¬†44376¬†et¬†44235¬†n’ont pas √©t√© plus chanceux‚Ķ

Pour finir, si vous avez envie d’aller plus loin avec la partie RGPD de WordPress pour rajouter vos donn√©es personnalis√©es aux exports par exemple, pensez √†¬†https://developer.wordpress.org/plugins/privacy/.

Voila, c’est fini¬†ūüėĀ