Loi 25 pour les développeurs : ce que la loi demande à votre code.
La Loi 25 se traduit en travail de développement concret. Les paramètres de confidentialité partent de la valeur la plus protectrice (article 9.1). Les formulaires ne recueillent que le nécessaire (article 5). Les données sont détruites une fois la fin accomplie (article 23), exportées en format structuré sur demande (article 27), et chaque incident va au registre (article 3.8).
Faits vérifiés le
La loi, article par article, en travail de développement
Les numéros d’article sont ceux de la Loi sur la protection des renseignements personnels dans le secteur privé, telle que modifiée par la Loi 25, lue sur LégisQuébec le 2026-10-29. La conception de chaque élément est la nôtre : la loi fixe l’obligation, pas le schéma. Ce n’est pas un avis juridique.
| Article | Ce qu’il dit, en bref | Ce que vous bâtissez |
|---|---|---|
| 9.1 † | Les paramètres assurent « par défaut » « le plus haut niveau de confidentialité » | Des valeurs par défaut protectrices dans les colonnes, et un test qui les vérifie |
| 4, 5, 9 † | Déterminer les fins d’abord; ne recueillir que le nécessaire; en cas de doute, le renseignement est réputé non nécessaire | Un schéma de validation strict, une fin par champ |
| 12, 14 † | Utiliser seulement pour la fin de la collecte; un consentement spécifique, par fin, valide le temps nécessaire | Une table des consentements par fin et par version du libellé |
| 23, 3.2 † | Détruire ou anonymiser une fois la fin accomplie; une politique encadre la conservation et la destruction | Une purge planifiée et une rétention des sauvegardes assez courte pour finir le travail |
| 27, 3.3 † | Remettre ce que la personne a fourni « dans un format technologique structuré et couramment utilisé » | Un point de terminaison d’exportation en JSON |
| 3.5 à 3.8 † | Diminuer les risques, aviser si le préjudice est sérieux, tenir un registre des incidents | Une table des incidents, et l’habitude de la remplir |
| 10, 5 † | Des mesures de sécurité raisonnables selon la sensibilité; ne recueillir que le nécessaire | Des journaux avec des identifiants, pas de noms, de courriels ni de corps de requête |
| 17 † | Évaluer avant qu’un renseignement quitte le Québec, y compris vers un fournisseur qui le conserve | Une carte des données qui liste chaque endroit où vit une copie |
† Les détails sur les concurrents proviennent des pages publiées par chaque fournisseur aux dates indiquées; les prix et forfaits changent, vérifiez auprès du fournisseur avant de décider. 9.1 (2026-10-29) · 4, 5, 9 (2026-10-29) · 12, 14 (2026-10-29) · 23, 3.2 (2026-10-29) · 27, 3.3 (2026-10-29) · 3.5 à 3.8 (2026-10-29) · 10, 5 (2026-10-29) · 17 (2026-10-29)
La confidentialité par défaut (article 9.1)
Si votre produit a des paramètres de confidentialité, ils doivent assurer « par défaut » « le plus haut niveau de confidentialité, sans aucune intervention de la personne concernée ». Dans le code, c’est une valeur par défaut de colonne. Un profil public au départ, ou un partage activé d’office, est fautif dès le premier jour pour chaque utilisateur qui n’ouvre jamais la page des paramètres.
L’article 9.1 exclut les paramètres des témoins de connexion, qui relèvent d’autres règles. L’article 8.1 ajoute une règle voisine : si vous utilisez une technologie qui permet d’identifier, de localiser ou de profiler une personne, vous devez l’en informer au préalable et lui dire comment activer ces fonctions. Concevez la localisation et le profilage en adhésion volontaire.
Mettez les valeurs par défaut dans la base de données, pas seulement dans le formulaire, pour qu’un client d’API ou un script d’amorçage obtienne la même chose. Puis testez-les :
CREATE TABLE privacy_settings (
user_id bigint PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
profile_public boolean NOT NULL DEFAULT false,
show_in_search boolean NOT NULL DEFAULT false,
share_location boolean NOT NULL DEFAULT false, -- art. 8.1 : sur adhésion
personalized_ads boolean NOT NULL DEFAULT false -- profilage : sur adhésion
);
-- test : un nouveau compte a les paramètres les plus protecteurs sans rien toucher
test("les nouveaux comptes sont privés (art. 9.1)", async () => {
const user = await createUser({ email: "a@example.com" });
const s = await db.one("SELECT * FROM privacy_settings WHERE user_id = $1", [user.id]);
expect(s).toMatchObject({
profile_public: false, show_in_search: false,
share_location: false, personalized_ads: false,
});
});Ne recueillir que le nécessaire (articles 4, 5 et 9)
L’article 4 exige de déterminer les fins avant la collecte. L’article 5 permet de ne recueillir « que les renseignements nécessaires aux fins déterminées avant la collecte ». L’article 9 interdit de refuser un service parce qu’une personne refuse de fournir un renseignement dont le service n’a pas besoin, et précise qu’en cas de doute, un renseignement personnel est réputé non nécessaire.
Notez la fin à côté de chaque champ, rendez facultatif tout ce qui n’est pas strictement nécessaire et rejetez les champs que vous n’avez pas demandés au lieu de les enregistrer. Un schéma strict fait les trois :
import { z } from "zod";
export const Signup = z.object({
email: z.string().email(), // fin : connexion, avis de compte
name: z.string().min(1).max(100), // fin : factures
phone: z.string().max(30).optional(), // non nécessaire au service : jamais obligatoire (art. 9)
// ni date de naissance ni adresse : rien de ce que nous faisons n'en a besoin (art. 5)
}).strict(); // les champs inconnus sont rejetés, pas stockésDes consentements que vous pouvez démontrer (articles 12 et 14)
L’article 12 limite l’utilisation aux fins de la collecte, avec des exceptions comme une fin compatible ou la prévention de la fraude; la prospection commerciale n’est jamais une fin compatible. L’article 14 exige un consentement « donné à des fins spécifiques », « demandé à chacune de ces fins », présenté distinctement s’il est écrit, et valide seulement le temps nécessaire. L’article 8 donne le droit de le retirer.
Un seul booléen marketing_ok ne démontre rien de tout cela. Consignez chaque fin séparément, avec le libellé que la personne a vu et la date, et vérifiez l’enregistrement avant chaque utilisation :
CREATE TABLE consents (
id bigserial PRIMARY KEY,
user_id bigint NOT NULL REFERENCES users(id) ON DELETE CASCADE,
purpose text NOT NULL, -- 'infolettre', 'partage_partenaire_x'
text_version text NOT NULL, -- le libellé accepté
granted_at timestamptz NOT NULL DEFAULT now(),
expires_at timestamptz, -- art. 14 : seulement le temps nécessaire
withdrawn_at timestamptz -- art. 8 : un retrait est une mise à jour, pas une suppression
);
-- avant d'envoyer l'infolettre
SELECT u.email
FROM users u
JOIN consents c ON c.user_id = u.id
WHERE c.purpose = 'infolettre'
AND c.withdrawn_at IS NULL
AND (c.expires_at IS NULL OR c.expires_at > now());Conservation et destruction (articles 23 et 3.2)
Article 23 : lorsque les fins sont accomplies, l’entreprise « doit le détruire ou l’anonymiser », sous réserve d’un délai de conservation prévu par une autre loi (les dossiers fiscaux, par exemple). L’article 3.2 exige des politiques de gouvernance qui encadrent la conservation et la destruction des renseignements. Écrivez le délai dans la politique, puis faites-le appliquer par une tâche.
« Anonymisé » a un sens strict à l’article 23 : il doit être raisonnable de prévoir que le renseignement « ne permet plus, de façon irréversible, d’identifier directement ou indirectement » la personne, selon des critères fixés par règlement. Remplacer un courriel par son empreinte SHA-256 n’y arrive pas : quiconque a le courriel peut recalculer la même empreinte. Dans le doute, supprimez.
Les sauvegardes gardent les lignes supprimées jusqu’à leur propre expiration : leur rétention fait donc partie de votre délai de destruction. Chez Canner, vous réglez l’intervalle et la durée de conservation de chaque sauvegarde (canner db backup-policy <name> --enable --every 24 --keep 7), chaque sauvegarde est chiffrée avec sa propre clé, et supprimer une sauvegarde détruit cette clé sur-le-champ. Une requête planifiée peut faire la purge chaque nuit :
-- chaque nuit; politique : 24 mois après la dernière connexion, sauf conservation légale
DELETE FROM users
WHERE last_seen_at < now() - interval '24 months'
AND legal_hold = false;
-- les lignes liées (privacy_settings, consents, orders.user_id) utilisent ON DELETE CASCADE
-- ou sont gardées avec user_id à NULL quand une autre loi exige le documentUne exportation dans un format structuré (articles 27 et 3.3)
L’article 27 donne à la personne le droit d’obtenir une copie de ses renseignements. Son troisième alinéa, en vigueur depuis septembre 2024, ajoute qu’un renseignement informatisé « recueilli auprès du requérant, et non pas créé ou inféré » à partir de ses renseignements, lui est communiqué sur demande « dans un format technologique structuré et couramment utilisé », à moins de difficultés pratiques sérieuses. L’article 3.3 exige que les nouveaux systèmes le permettent. L’article 32 vous donne 30 jours pour répondre.
La loi ne nomme aucun format : JSON ou CSV conviennent. Attention à la portée : ce que la personne vous a fourni (profil, messages, fichiers, commandes), pas vos cotes ou segments inférés. Ceux-ci restent visés par le droit d’accès général du premier alinéa, mais pas par la règle du format.
app.get("/account/export", requireUser, async (req, res) => {
const id = req.user.id;
const [profile, orders, consents] = await Promise.all([
db.one("SELECT email, name, phone, created_at FROM users WHERE id = $1", [id]),
db.any("SELECT id, placed_at, items, total FROM orders WHERE user_id = $1", [id]),
db.any("SELECT purpose, text_version, granted_at, withdrawn_at FROM consents WHERE user_id = $1", [id]),
]);
res.setHeader("Content-Disposition", 'attachment; filename="mes-donnees.json"');
res.json({ exported_at: new Date().toISOString(), profile, orders, consents });
});Un registre des incidents (articles 3.5 à 3.8)
L’article 3.6 définit l’incident de confidentialité : l’accès, l’utilisation ou la communication non autorisés par la loi, ou la perte d’un renseignement personnel ou toute autre atteinte à sa protection. Un agent de soutien qui ouvre un dossier sans raison, c’en est un. Un compartiment laissé public aussi.
L’article 3.5 exige des mesures raisonnables pour diminuer les risques et, s’il y a un risque de préjudice sérieux, un avis avec diligence à la Commission d’accès à l’information et aux personnes concernées. L’article 3.8 exige un registre de tous les incidents, sérieux ou non, dont une copie est transmise à la Commission sur demande. Un règlement peut déterminer la teneur du registre; consultez-le avant de fixer les colonnes. Une table est un bon point de départ :
CREATE TABLE confidentiality_incidents (
id bigserial PRIMARY KEY,
detected_at timestamptz NOT NULL,
occurred_from timestamptz,
occurred_to timestamptz,
description text NOT NULL, -- ce qui s'est passé, comment on l'a découvert
info_types text[] NOT NULL, -- '{courriel,historique_commandes}'
persons_affected integer,
serious_injury_risk boolean NOT NULL, -- art. 3.7 : sensibilité, conséquences, probabilité
cai_notified_at timestamptz, -- art. 3.5, si sérieux
persons_notified_at timestamptz,
measures_taken text NOT NULL,
recorded_by text NOT NULL
);Des journaux sans renseignements personnels (articles 5 et 10)
Les journaux sont des renseignements personnels que vous conservez, souvent plus longtemps et avec un accès plus large que la base de données. L’article 10 exige des mesures de sécurité raisonnables selon la sensibilité et la quantité des renseignements; l’article 5, de ne recueillir que le nécessaire. Journalisez un identifiant d’utilisateur, pas le nom ni le courriel, et jamais le corps des requêtes ni les en-têtes d’autorisation.
Gardez aussi les renseignements personnels hors des URL : une chaîne de requête comme ?email= se retrouve dans chaque journal d’accès en chemin. Chez Canner, le journal des requêtes consigne la méthode, le chemin, le statut et la durée, sans adresse IP du client, sans en-têtes ni corps. Avant toute écriture, les valeurs qui ressemblent à des identifiants de connexion, les valeurs des variables d’environnement sensibles, les adresses courriel et les adresses IP publiques sont retirées. Les journaux de requêtes et de l’appli sont conservés 7 jours, les échantillons d’erreurs 90 jours (documentation des journaux). Ce filtre ne reconnaît pas un nom ou un diagnostic que votre appli écrit : masquez à la source.
import pino from "pino";
export const log = pino({
redact: {
paths: [
"req.headers.authorization", "req.headers.cookie",
"user.email", "user.name", "user.phone", "body",
],
censor: "[masqué]",
},
});
log.info({ userId: user.id, action: "export_requested" }); // un identifiant, pas la personneOù vivent les données et les sauvegardes (article 17)
L’article 17 exige une EFVP et une entente écrite avant de communiquer un renseignement personnel à l’extérieur du Québec, et son troisième alinéa applique la même règle quand un fournisseur hors Québec le recueille, l’utilise ou le conserve pour vous. Le critère, c’est le Québec, pas le Canada : une région à Toronto compte.
L’hébergeur est rarement la seule copie. Le suivi des erreurs, le service de courriel, l’analytique, les API d’IA et les sauvegardes hors site gardent chacun quelque chose. Tenez une carte des données dans le dépôt, à côté du code qui ajoute les intégrations, pour qu’une demande de tirage qui ajoute une destination mette aussi la carte à jour :
// privacy/data-map.ts — revue à chaque nouvelle intégration
export const DATA_MAP = [
{ system: "Postgres + sauvegardes", holds: "toutes les données de compte", where: "Montréal (Canner)", s17: "non requise" },
{ system: "Copie hors site", holds: "vidages chiffrés", where: "compartiment de votre choix", s17: "requise si hors Québec" },
{ system: "Courriel transactionnel", holds: "courriel, nom", where: "vérifier le fournisseur", s17: "évaluer" },
{ system: "Suivi des erreurs", holds: "traces de pile", where: "vérifier le fournisseur", s17: "évaluer; masquer les RP avant" },
] as const;Questions
Que veut dire la confidentialité par défaut selon la Loi 25?
Article 9.1 : si un produit ou un service technologique a des paramètres de confidentialité, ils doivent assurer par défaut le plus haut niveau de confidentialité, sans intervention de la personne. En pratique, chaque paramètre part de sa valeur la plus protectrice. Les paramètres des témoins de connexion sont exclus de cet article.
Dois-je supprimer les données des sauvegardes quand un utilisateur est supprimé?
La loi exige la destruction ou l’anonymisation une fois les fins accomplies (article 23); elle ne parle pas des sauvegardes. Notre lecture : réglez une rétention des sauvegardes assez courte pour qu’une suppression soit complète dans un délai connu, écrivez ce délai dans votre politique de conservation, et ne restaurez pas d’utilisateurs supprimés en production.
Quel format pour une exportation de portabilité selon la Loi 25?
« Un format technologique structuré et couramment utilisé » (article 27). La loi ne nomme aucun format; JSON ou CSV conviennent. La règle vise les renseignements recueillis auprès de la personne, pas ceux que vous avez créés ou inférés.
Le hachage d’un renseignement personnel, est-ce de l’anonymisation?
Généralement non. L’article 23 exige que le renseignement ne permette plus, de façon irréversible, d’identifier la personne directement ou indirectement. L’empreinte d’un courriel se recalcule par quiconque a le courriel : c’est au mieux un renseignement dépersonnalisé, que l’article 12 traite encore comme un renseignement personnel.
Si j’héberge chez Canner, ai-je encore besoin d’une EFVP?
Pour le système que vous bâtissez, oui : l’article 3.3 s’applique peu importe l’hébergeur. Pour l’hébergement lui-même, aucune évaluation de l’article 17 n’est requise, puisque les données restent au Québec. Les intégrations qui envoient des données ailleurs en exigent une.
À lire aussi
- GuideL’Énoncé de souveraineté numérique du Québec
- BlogueL'alternative canadienne à AWS — par accident
- BloguePourquoi nous avons quitté une flotte de serveurs virtualisés pour notre propre matériel
- BlogueComment bâtir une pile 100 % canadienne
- BlogueUne fin de semaine du mauvais côté de la chaîne
- BlogueServices partagés Canada veut une infonuagique souveraine. Qu'est-ce que ça prendrait vraiment?
Bâtissez au Québec dès le premier commit.
Chez Canner, les applis, Postgres, les fichiers, les journaux et les sauvegardes restent à Montréal. Commencez gratuitement.