Découvrez comment accéder aux polices installées localement sur l'utilisateur et obtenir des informations de bas niveau à leur sujet avec l'API Local Font Access.
Publié le 24 août 2020
Polices Web sécurisées
Si vous développez pour le Web depuis un certain temps, vous vous souvenez peut-être des polices Web dites sécurisées.
Ces polices sont connues pour être disponibles sur presque toutes les instances des systèmes d'exploitation les plus utilisés
(à savoir Windows, macOS, les distributions Linux les plus courantes, Android et iOS). Au début des années 2000,
Microsoft a même lancé une
initiative
intitulée TrueType core fonts for the Web (Polices de base TrueType pour le Web) qui proposait ces polices en téléchargement sans frais dans le
but que "lorsque vous consultez un site Web qui les spécifie, vous voyez les pages exactement comme le
concepteur du site l'a prévu". Oui, cela inclut les sites définis dans
Comic Sans MS. Voici à quoi pourrait ressembler une
pile de polices Web sécurisées classique (avec la police
sans-serif
comme solution de repli ultime) :
body {
font-family: Helvetica, Arial, sans-serif;
}
Polices Web
L'époque où les polices Web sécurisées étaient vraiment importantes est révolue. Aujourd'hui, nous disposons de
polices Web, dont certaines sont
même des polices variables que nous pouvons ajuster davantage en modifiant les valeurs des
différents axes exposés. Vous pouvez utiliser des polices Web en déclarant un
@font-face bloc au début du code CSS,
qui spécifie le ou les fichiers de police à télécharger :
@font-face {
font-family: 'FlamboyantSansSerif';
src: url('flamboyant.woff2');
}
Vous pouvez ensuite utiliser la police Web personnalisée en spécifiant le
font-family, comme d'habitude :
body {
font-family: 'FlamboyantSansSerif';
}
Polices locales comme vecteur d'empreinte
La plupart des polices Web proviennent du Web. Toutefois, il est intéressant de noter que la
src propriété de la @font-face
déclaration, en plus de la
url()
fonction, accepte également une
local()
fonction. Cela permet de charger des polices personnalisées (surprise !) localement. Si l'utilisateur a installé
FlamboyantSansSerif sur son système d'exploitation, la copie locale sera utilisée au lieu d'
être téléchargée :
@font-face {
font-family: 'FlamboyantSansSerif';
src: local('FlamboyantSansSerif'), url('flamboyant.woff2');
}
Cette approche fournit un mécanisme de repli intéressant qui permet d'économiser de la bande passante. Malheureusement, sur Internet, nous ne pouvons pas avoir de belles choses. Le problème avec la fonction local() est qu'elle peut être
utilisée de manière abusive pour l'empreinte du navigateur. Il s'avère que la liste des polices installées par un utilisateur peut être assez
identifiante. De nombreuses entreprises ont leurs propres polices d'entreprise qui sont installées sur les ordinateurs portables de leurs employés. Par exemple, Google possède une police d'entreprise appelée Google Sans.
Un pirate informatique peut essayer de déterminer pour quelle entreprise travaille une personne en testant l'existence d'un grand nombre de polices d'entreprise connues, telles que Google Sans. Il tente de rendre le texte défini dans ces polices sur un canevas et de mesurer les glyphes. Si les glyphes correspondent à la forme connue de la police d'entreprise, le pirate informatique a trouvé une correspondance. Si les glyphes ne correspondent pas, le pirate informatique sait qu'une police de remplacement par défaut a été utilisée, car la police d'entreprise n'a pas été installée. Pour en savoir plus sur cette attaque et d'autres attaques d'empreinte de navigateur, consultez l' étude de Laperdix et al.
Outre les polices d'entreprise, la simple liste des polices installées peut être identifiante. La situation avec ce vecteur d'attaque est devenue si mauvaise que l'équipe WebKit a récemment décidé d' "inclure [dans la liste des polices disponibles] uniquement les polices Web et les polices fournies avec le système d'exploitation, mais pas les polices installées localement par l'utilisateur". (Et me voici, avec un article sur l'octroi d'un accès aux polices locales.)
L'API Local Font Access
Le début de cet article vous a peut-être mis de mauvaise humeur. Ne pouvons-nous vraiment pas avoir de belles choses ? Ne vous inquiétez pas. Nous pensons que nous pouvons, et peut-être que tout n'est pas désespéré. Mais tout d'abord, laissez-moi répondre à une question que vous vous posez peut-être.
Pourquoi avons-nous besoin de l'API Local Font Access alors qu'il existe des polices Web ?
Les outils de conception et graphiques de qualité professionnelle ont toujours été difficiles à fournir sur le Web. L'un des principaux obstacles était l'impossibilité d'accéder à la variété complète des polices conçues et suggérées par des professionnels que les concepteurs ont installées localement. Les polices Web permettent certains cas d'utilisation de publication, mais ne permettent pas d'accéder par programmation aux formes de glyphes vectoriels et aux tables de polices utilisées par les rasteriseurs pour afficher les contours des glyphes. Il n'existe pas non plus de moyen d'accéder aux données binaires d'une police Web.
- Les outils de conception doivent accéder aux octets de police pour effectuer leur propre implémentation de mise en page OpenType et permettre aux outils de conception de s'intégrer à des niveaux inférieurs, pour des actions telles que l'application de filtres ou de transformations vectoriels aux formes de glyphes.
- Les développeurs peuvent avoir des piles de polices héritées pour leurs applications qu'ils apportent sur le Web. Pour utiliser ces piles, ils ont généralement besoin d'un accès direct aux données de police, ce que les polices Web ne fournissent pas.
- Certaines polices ne sont peut-être pas concédées sous licence pour être diffusées sur le Web. Par exemple, Linotype possède une licence pour certaines polices qui n'inclut que l'utilisation sur ordinateur.
L'API Local Font Access tente de résoudre ces problèmes. Elle se compose de deux parties :
- Une API d'énumération de polices, qui permet aux utilisateurs d'accorder l'accès à l'ensemble complet des polices système disponibles.
- À partir de chaque résultat d'énumération, la possibilité de demander un accès au conteneur SFNT de bas niveau (orienté octet) qui inclut les données de police complètes.
Prise en charge des navigateurs
Comment utiliser l'API Local Font Access ?
Détection de fonctionnalités
Pour vérifier si l'API Local Font Access est compatible, utilisez :
if ('queryLocalFonts' in window) {
// The Local Font Access API is supported
}
Énumérer les polices locales
Pour obtenir la liste des polices installées localement, vous devez appeler window.queryLocalFonts(). La
première fois, cela déclenche une invite d'autorisation que l'utilisateur peut accepter ou refuser. Si l'utilisateur
autorise l'interrogation de ses polices locales, le navigateur renvoie un tableau contenant les données des polices
que vous pouvez parcourir. Chaque police est représentée par un objet FontData avec les propriétés family
(par exemple, "Comic Sans MS"), fullName (par exemple, "Comic Sans MS"), postscriptName (par
exemple, "ComicSansMS"), et style (par exemple, "Regular").
// Query for all available fonts and log metadata.
try {
const availableFonts = await window.queryLocalFonts();
for (const fontData of availableFonts) {
console.log(fontData.postscriptName);
console.log(fontData.fullName);
console.log(fontData.family);
console.log(fontData.style);
}
} catch (err) {
console.error(err.name, err.message);
}
Si vous n'êtes intéressé que par un sous-ensemble de polices, vous pouvez également les filtrer en fonction des noms PostScript
en ajoutant un paramètre postscriptNames.
const availableFonts = await window.queryLocalFonts({
postscriptNames: ['Verdana', 'Verdana-Bold', 'Verdana-Italic'],
});
Accéder aux données SFNT
L'accès SFNT complet est disponible via la méthode blob() de l'objet
FontData. SFNT est un format de fichier de police qui peut contenir d'autres polices, telles que PostScript,
TrueType, OpenType, Web Open Font Format (WOFF) et autres.
try {
const availableFonts = await window.queryLocalFonts({
postscriptNames: ['ComicSansMS'],
});
for (const fontData of availableFonts) {
// `blob()` returns a Blob containing valid and complete
// SFNT-wrapped font data.
const sfnt = await fontData.blob();
// Slice out only the bytes we need: the first 4 bytes are the SFNT
// version info.
// Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
const sfntVersion = await sfnt.slice(0, 4).text();
let outlineFormat = 'UNKNOWN';
switch (sfntVersion) {
case '\x00\x01\x00\x00':
case 'true':
case 'typ1':
outlineFormat = 'truetype';
break;
case 'OTTO':
outlineFormat = 'cff';
break;
}
console.log('Outline format:', outlineFormat);
}
} catch (err) {
console.error(err.name, err.message);
}
Démo
Vous pouvez voir l'API Local Font Access en action dans la
démo. N'oubliez pas de consulter également le
code source. La démo
présente un élément personnalisé appelé <font-select> qui
implémente un sélecteur de polices locales.
Considérations liées à la confidentialité
L'autorisation "local-fonts" semble fournir une surface hautement empreinte. Toutefois,
les navigateurs sont libres de renvoyer ce qu'ils souhaitent. Par exemple, les navigateurs axés sur l'anonymat peuvent choisir
de ne fournir qu'un ensemble de polices par défaut intégrées au navigateur. De même, les navigateurs ne sont pas tenus
de fournir les données de table exactement telles qu'elles apparaissent sur le disque.
Dans la mesure du possible, l'API Local Font Access est conçue pour n'exposer que les informations nécessaires pour activer les cas d'utilisation mentionnés. Les API système peuvent produire une liste de polices installées non pas dans un ordre aléatoire ou trié, mais dans l'ordre d'installation des polices. Le fait de renvoyer exactement la liste des polices installées fournie par une telle API système peut exposer des données supplémentaires qui peuvent être utilisées pour l'empreinte, et les cas d'utilisation que nous souhaitons activer ne sont pas facilités par la conservation de cet ordre. Par conséquent, cette API nécessite que les données renvoyées soient triées avant d'être renvoyées.
Sécurité et autorisations
L'équipe Chrome a conçu et implémenté l'API Local Font Access en utilisant les principes de base définis dans Contrôler l'accès aux fonctionnalités puissantes de la plate-forme Web, y compris le contrôle des utilisateurs, la transparence et l'ergonomie.
Contrôle des utilisateurs
L'accès aux polices d'un utilisateur est entièrement sous son contrôle et ne sera pas autorisé, sauf si l'
"local-fonts" autorisation, telle qu'elle est répertoriée dans le
registre des autorisations, est accordée.
Transparence
Le fait qu'un site ait obtenu l'accès aux polices locales de l'utilisateur sera visible dans la fiche d'informations du site.
Persistance des autorisations
L'autorisation "local-fonts" sera conservée entre les rechargements de page. Elle peut être révoquée via la
fiche Informations sur le site.
Commentaires
L'équipe Chrome souhaite connaître votre expérience avec l'API Local Font Access.
Parlez-nous de la conception de l'API
Y a-t-il quelque chose dans l'API qui ne fonctionne pas comme prévu ? Ou y a-t-il des méthodes ou des propriétés manquantes dont vous avez besoin pour mettre en œuvre votre idée ? Vous avez une question ou un commentaire sur le modèle de sécurité ? Signalez un problème de spécification dans le dépôt GitHub correspondant ou ajoutez vos commentaires à un problème existant.
Signaler un problème lié à l'implémentation
Avez-vous trouvé un bug dans l'implémentation de Chrome ? Ou l'implémentation est-elle différente de la spécification ?
Signalez un bug sur new.crbug.com. Veillez à inclure autant de détails que possible,
des instructions simples pour la reproduction et saisissez Blink>Storage>FontAccess dans la zone Composants.
Soutenir l'API
Prévoyez-vous d'utiliser l'API Local Font Access ? Votre soutien public aide l'équipe Chrome à hiérarchiser les fonctionnalités et montre aux autres fournisseurs de navigateurs à quel point il est essentiel de les prendre en charge.
Envoyez un tweet à @ChromiumDev avec le hashtag
#LocalFontAccess et indiquez
nous où et comment vous l'utilisez.
Liens utiles
- Présentateur
- Brouillon de spécification
- Bug Chromium pour l'énumération des polices
- Bug Chromium pour l'accès à la table des polices
- Entrée ChromeStatus
- Dépôt GitHub
- Examen TAG
- Position des normes Mozilla
Remerciements
La spécification de l'API Local Font Access a été modifiée par Emil A. Eklund, Alex Russell, Joshua Bell et Olivier Yiptong. Cet article a été examiné par Joe Medley, Dominik Röttsches et Olivier Yiptong.