Sécurité

Quand Mail.Send devient un risque systémique, et comment le contenir

Étude de cas : reprendre le contrôle d'une permission Graph que Microsoft ne scope pas assez finement.

Équipe GraphWarden·20 avril 2026·9 min de lecture

Le moment où ça dérape

Mardi, 14h17. Une application de relances clients, déployée depuis trois ans dans une firme de services professionnels de 800 employés, maintenue par un fournisseur externe, branchée sur le CRM interne, se met à envoyer des courriels. Beaucoup de courriels.

En vingt minutes, 12 000 messages partent depuis la boîte facturation@firme.ca vers la liste complète des clients actifs et passés. Sujet : « Mise à jour importante concernant votre dossier ». Corps du message : un lien vers un domaine que personne ne reconnaît, pointant vers une page de collecte d'identifiants maquillée aux couleurs de la firme.

La clé applicative venait d'être compromise sur un poste de développeur du fournisseur.

L'application avait la permission Mail.Send en application permission. Du point de vue de Microsoft Graph, tout était conforme : jeton valide, permission accordée, appels bien formés. Exchange Online a fait son travail. Les 12 000 messages ont été livrés, signés d'une boîte légitime de la firme, à des clients qui font confiance à cette adresse depuis des années.

Le coût de l'incident n'est pas dans les 12 000 courriels. Il est dans les clients qui vont cliquer, dans l'obligation de notification sous la Loi 25, dans l'enquête ordinale, et dans les mois de reconstruction de réputation qui suivent.

Pourquoi Microsoft ne bloque pas ce scénario

Mail.Send en permission applicative est tenant-wide par conception. L'application peut envoyer au nom de n'importe qui, depuis n'importe quelle boîte, vers n'importe quel destinataire, à n'importe quelle fréquence. C'est conforme à la documentation, et c'est le problème.

Microsoft offre bien un mécanisme de scoping : les ApplicationAccessPolicy dans Exchange Online. En pratique :

  • La configuration se fait en PowerShell, souvent en dehors du périmètre de l'admin qui gère l'application.
  • Le scoping se limite aux boîtes sources autorisées via un groupe de sécurité.
  • Rien ne contrôle les destinataires, le contenu, le volume, le contexte temporel, ni l'identité de l'appelant.
  • L'audit se résume à « l'application a envoyé un courriel ».
  • Modifier la policy en production comporte un risque opérationnel non négligeable.

Autrement dit : Microsoft vous donne un interrupteur binaire, l'app peut ou ne peut pas envoyer depuis cette boîte. Ce qu'il manque, c'est un pare-feu applicatif. Le même constat s'applique à toute la famille des permissions Graph à haut privilège, comme on le détaillait dans l'angle mort des permissions Graph.

Ce que l'incident aurait donné avec un périmètre en place

Reprenons le scénario, cette fois avec les appels sendMail routés à travers un proxy de gouvernance entre l'application et Graph. Le trafic est inchangé côté Microsoft. Chaque appel est évalué contre un jeu de règles déclaratives avant d'être relayé.

Modèle de base : default-deny. Tout appel qui ne correspond à aucune règle d'allow est bloqué et journalisé. Les règles qui suivent ajoutent des autorisations explicites, cumulatives, et toutes doivent passer pour qu'un appel soit transmis à Graph.

Règle 1 — Qui a le droit d'envoyer ?

- name: "Relances — caller authorized"
  endpoint_pattern: "/users/*/sendMail"
  http_methods: [POST]
  conditions:
    all_of:
      - caller_identity:
          application_id: "app-relances-clients"
      - object_attribute:
          path: "userPrincipalName"
          pattern: "^facturation@firme\\.ca$"
  action: allow

Si une autre application tente d'appeler sendMail depuis cette boîte, ou si l'app de relances tente d'envoyer depuis une autre boîte, la requête est rejetée avant d'atteindre Graph.

Règle 2 — Destinataires bornés au périmètre métier

- name: "Relances — only known client domains"
  endpoint_pattern: "/users/*/sendMail"
  conditions:
    all_of:
      - domain:
          field: "message.toRecipients[].emailAddress.address"
          source: "custom_attribute:client_domains_whitelist"
  action: allow

La liste des domaines clients est maintenue dynamiquement depuis le CRM. L'envoi vers un domaine absent de la liste active, y compris des domaines grand public ou arbitraires, est bloqué. L'attaquant ne peut plus détourner la boîte légitime pour toucher des destinataires massifs.

Règle 3 — Limite de débit

- name: "Relances — rate limit safety net"
  endpoint_pattern: "/users/*/sendMail"
  conditions:
    all_of:
      - rate_limit:
          max: 200
          window_seconds: 3600
          scope: "caller_identity"
  action: allow

200 envois par heure, calibré sur le volume réel des relances. Au-delà, l'application est automatiquement coupée. Les 12 000 courriels du scénario initial deviennent 200, un incident contenu au lieu d'une crise.

Règle 4 — Contenu filtré

- name: "Relances — no external links in body"
  endpoint_pattern: "/users/*/sendMail"
  conditions:
    any_of:
      - regex_match:
          field: "message.body.content"
          pattern: "https?://(?!firme\\.ca|portail-clients\\.firme\\.ca)"
  action: block

Toute URL externe non whitelistée dans le corps du message déclenche un blocage. Le pattern classique d'exfiltration, qui consiste à détourner une boîte légitime pour envoyer des liens de phishing, devient structurellement impossible.

Règle 5 — Fenêtre temporelle

- name: "Relances — business hours only"
  endpoint_pattern: "/users/*/sendMail"
  conditions:
    all_of:
      - time_window:
          timezone: "America/Montreal"
          days: ["Mon", "Tue", "Wed", "Thu", "Fri"]
          hours: "08:00-18:00"
  action: allow

Une app de relances clients n'a aucune raison légitime d'envoyer à 2h du matin un dimanche. L'activité hors fenêtre devient un signal d'alerte fort et bloquant.

Ces cinq règles font partie de la grammaire complète des règles déclaratives de GraphWarden, qui couvre identité appelante, attributs de l'objet, contenu, débit, fenêtre temporelle et contexte réseau.

Voir la documentation des règles →

Ce que ça change pour le décideur

Sans couche de gouvernance, le risque Mail.Send est structurellement porté par l'application, donc par le fournisseur, donc par un contrat, donc par une clause de responsabilité qui se plaide après incident.

Avec une couche de gouvernance, le risque est porté par une règle déclarative : lisible par un analyste en sécurité, auditable en temps réel, modifiable sans redéploiement applicatif, et conservant une trace indépendante du fournisseur.

Concrètement, trois bascules se produisent.

  1. Le périmètre se déplace. Le contrôle n'est plus dans le code que le fournisseur maintient, mais dans une configuration que le client possède.
  2. L'audit devient exploitable. Chaque appel sendMail, bloqué, filtré ou autorisé, génère un événement structuré consultable hors du tenant Microsoft. En cas d'incident, la preuve n'est pas à reconstituer depuis les logs Exchange.
  3. La réponse à incident passe de jours à minutes. Couper un abus revient à modifier une règle. Pas de ticket fournisseur, pas de fenêtre de maintenance, pas de redéploiement.

Pour une organisation tenue au secret professionnel, cette bascule a une conséquence directe sur la défense en cas d'enquête ordinale ou de poursuite civile : on passe de « nous avons fait confiance au fournisseur » à « nous avons appliqué un contrôle technique documenté et auditable ».

Les limites qu'il faut nommer

Cette approche ne fonctionne que si l'application appelle Graph à travers le proxy. Si l'application conserve ses propres identifiants Microsoft en direct, elle peut contourner la couche de gouvernance.

C'est pour ça que le modèle de confiance compte autant que le filtrage. Dans une architecture bien posée, l'application ne détient jamais les secrets Graph réels. Elle reçoit un identifiant de proxy, inutilisable ailleurs que derrière la couche de gouvernance. Le vrai secret reste dans le vault du client, accessible uniquement au proxy via Managed Identity. Le contournement devient structurellement impossible, pas juste contractuellement interdit.

Deuxième limite : la latence. Un appel sendMail transitant par le proxy ajoute typiquement 20 à 50 ms. Invisible pour du transactionnel, à discuter pour du bulk massif, rarement un enjeu sur des volumes de relances clients ou de notifications métier.

Ce qu'il faut retenir

Mail.Send en permission applicative n'est pas une mauvaise conception de Microsoft. C'est une permission légitime dont le scoping a été laissé à l'écosystème. Exchange Online fournit un interrupteur. L'écosystème doit fournir le pare-feu.

Pour toute organisation dont la réputation repose sur la confiance de ses clients, firmes professionnelles, institutions financières, organismes publics, ISV distribuant des applications M365, la question n'est plus si une application va dériver, mais quand, et avec quelle ampleur. Mettre une couche de gouvernance entre les applications et Graph réduit l'ampleur d'un incident d'un ordre de grandeur.

C'est exactement pour ça que GraphWarden existe.

GraphWarden est développé au Québec, hébergé en région canadienne, et conçu pour les environnements où la conformité Loi 25 et le contrôle applicatif Microsoft Graph sont des exigences non-négociables.