Le blog sécurité de FireAI

Par FireAI Security & Research Team · Publié

Exécution d'un LLM local sur Apple Silicon pour le triage de sécurité

Exécution d'un LLM local sur Apple Silicon pour le triage de sécurité

Un modèle qui classe une connexion réseau comme digne d'intérêt ou non, fonctionnant sur le même Mac qui a établi la connexion, change trois choses à la fois : ce qui quitte la machine, ce qu'elle coûte pour fonctionner et si elle continue de fonctionner lorsque le réseau lui-même est suspect. Ces trois éléments sont plus importants pour un outil de sécurité que pour la plupart des autres utilisations d'un modèle de langage. C'est pourquoi cet article porte spécifiquement sur le cas de l'appareil plutôt que sur l'appel d'une API.

Pourquoi sur l'appareil, spécifiquement pour le triage

Envoyer une ligne de journal de connexion à un modèle cloud à des fins de classification signifie transmettre, pour chaque décision, quelle application sur votre Mac communique avec quel hôte, sur quel port, à l'heure actuelle. Rien de tout cela n'est un secret comme l'est un mot de passe, mais c'est exactement le genre de métadonnées qu'une configuration soucieuse de la sécurité tente de minimiser le partage, et un outil dont le seul objectif est de décider ce que votre Mac est autorisé à envoyer ailleurs a une raison évidente de ne pas dépendre lui-même de l'envoi de quelque chose ailleurs pour chaque décision qu'il prend. L'exécution du modèle localement supprime entièrement cette dépendance : la ligne de journal ne quitte jamais l'appareil, car il n'y a aucun appel réseau dans le chemin de décision.

La deuxième raison est la continuité. Une étape de tri basée sur le cloud n'est disponible que dans la mesure où votre connexion Internet et l'API du fournisseur sont exactement les éléments qui pourraient être dégradés ou délibérément coupés lors d'un incident réel. Un modèle exécuté dans la mémoire locale continue de répondre avec le réseau en panne, le Wi-Fi désactivé ou une compromission suspectée en cours sur le même lien que l'appel API aurait utilisé.

MLX : le propre framework de tableaux d'Apple

MLX est un framework de baies construit par l'équipe de recherche en apprentissage automatique d'Apple spécifiquement pour le silicium Apple, avec les API Python, C++, C et Swift. Sa propre documentation décrit un modèle de mémoire unifiée comme choix de conception déterminant : les tableaux de MLX vivent dans la mémoire partagée et les opérations sur ceux-ci peuvent s'exécuter sur n'importe quel périphérique pris en charge - CPU ou GPU - sans que les poids du modèle ne soient d'abord copiés entre des pools de mémoire distincts. Cela compte concrètement pour un ordinateur portable : une machine à GPU discret doit copier les poids d'un modèle via un bus dans la mémoire GPU avant de pouvoir calculer quoi que ce soit, ce qui coûte du temps et double l'empreinte mémoire ; sur l'architecture de mémoire unifiée d'un Mac, le CPU et le GPU partagent déjà la même mémoire physique, il n'y a donc rien à copier.

mlx-lm, le package compagnon pour exécuter des modèles de langage sur MLX, s'installe avec une seule commande et fournit un modèle par défaut prêt à l'emploi :

Configuration MLX
pip install mlx-lm
# runs the default model (mlx-community/Llama-3.2-3B-Instruct-4bit)
mlx_lm.generate --prompt "How tall is Mt Everest?"
# or name a specific quantized model from the mlx-community hub
mlx_lm.generate --model mlx-community/Mistral-7B-Instruct-v0.3-4bit --prompt "..."
# interactive chat session instead of a single prompt
mlx_lm.chat
# convert and quantize a model yourself
mlx_lm.convert --model mistralai/Mistral-7B-Instruct-v0.3 -q

lama.cpp : l'option portable

llama.cpp est le plus ancien et le plus largement porté des deux, écrit en C/C++ sans aucun runtime Python requis au moment de l'inférence. Son propre README indique directement la position du projet sur ce matériel : le silicium Apple est traité comme, selon les termes du projet, "un citoyen de première classe - optimisé via les frameworks ARM NEON, Accelerate et Metal", et sa documentation de construction confirme que sur macOS, le backend Metal GPU est activé par défaut, avec un indicateur au moment de la construction pour le désactiver si vous souhaitez spécifiquement une inférence CPU uniquement.

llama.cpp construire et exécuter
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
cmake -B build
cmake --build build --config Release
# Metal is on by default on macOS; add -DGGML_METAL=OFF to the first
# command above if you need to force CPU-only inference
llama cli -hf ggml-org/Qwen3.5-0.8B-GGUF
# or run it as a local server instead of a one-shot command
llama serve -hf ggml-org/Qwen3.5-0.8B-GGUF

llama.cpp fonctionne à partir de fichiers GGUF, un format de modèle de fichier unique qui regroupe les poids quantifiés et tout le nécessaire pour les exécuter ; la commande ci-dessus en extrait un directement d'un référentiel Hugging Face par son nom. MLX, en revanche, se rapproche d’un flux de travail Python natif, convertissant et quantifiant les modèles dans son propre format à l’avance. Ni l’un ni l’autre n’est strictement meilleur : llama.cpp est celui à privilégier si vous souhaitez un seul binaire compilé sans dépendance Python, MLX est celui à privilégier si vous construisez déjà le reste de votre pipeline en Python et souhaitez un accès de première classe au modèle de mémoire unifiée d’Apple à partir de là.

Quantification : ce que vous échangez réellement contre un modèle plus petit

La quantification stocke le poids de chaque modèle dans moins de bits que les 16 ou 32 bits avec lesquels le modèle a été formé, réduisant ainsi à la fois le fichier sur le disque et la mémoire nécessaire à son exécution, au détriment de la qualité de sortie. La propre documentation de quantification de llama.cpp répertorie les chiffres exacts en bits par poids pour chaque schéma qu'il prend en charge, ce qui est une manière plus précise de raisonner sur le compromis que le raccourci habituel de « 4 bits » ou « 8 bits » :

Extrait de la documentation de l'outil de quantification de llama.cpp.
Famille de régimeExemples de schémasBits par poids
Précision totale (référence)F1616,0
Presque sans perteQ8_08h50
Milieu de gamme de meilleure qualitéQ5_K_S / Q5_K_M5,57 - 5,70
Qualité et taille équilibréesQ4_K_S / Q4_K_M4,67 - 4,89
Plus petit, plus de pertesQ3_K_S / Q3_K_M / Q3_K_L15h64 - 16h30
Compression extrêmeIQ2_XXS ... IQ2_M2h00 - 2h93

La multiplication du nombre de paramètres d'un modèle par son nombre de bits par poids donne une estimation approximative de la mémoire pour les poids seuls : un modèle de 7 milliards de paramètres à 4,89 bits par poids de Q4_K_M a besoin d'environ 7 000 000 000 × 4,89 ÷ 8 octets, soit environ 4,3 Go, avant de prendre en compte la fenêtre contextuelle et les activations intermédiaires, qui en ajoutent davantage en fonction de la quantité de texte que vous fournissez. ça. Il s'agit d'un calcul à partir du chiffre de bits par poids cité, et non d'un nombre publié directement par l'un ou l'autre projet, et l'utilisation réelle de la mémoire sera plus élevée une fois qu'une invite réelle et son contexte seront chargés. Les conseils pratiques qui découlent du tableau sont simples : pour une tâche telle que la classification d'une ligne de journal de connexion à la fois, où l'entrée est courte et la sortie requise est un petit verdict structuré, un modèle de classe Q4_K_M est très souvent le bon choix - sensiblement plus petit et plus rapide que Q8_0 ou F16, sans tomber au niveau de compression extrême où la qualité de sortie se dégrade plus fortement.

Aucun des deux projets ne publie d'exigence de mémoire par configuration Mac, donc l'approche pratique consiste à revenir en arrière à partir de l'estimation ci-dessus et à laisser une réelle marge : macOS lui-même, votre navigateur et tout ce qui est en cours d'exécution ont tous également besoin d'une mémoire unifiée, et un modèle qui ne correspond à peine à rien d'autre ouvert s'échangera ou se bloquera au moment où vous passerez à une autre application. Sur un Mac avec une quantité modeste de mémoire unifiée, cela plaide pour rester vers l'extrémité la plus petite du tableau ci-dessus – quelques milliards de paramètres à Q4_K_M plutôt qu'un modèle beaucoup plus grand avec la même quantification – et réserver les options plus grandes et de plus haute précision aux machines ayant de la mémoire à revendre. Pour une tâche de classification étroite comme celle sur laquelle porte cet article, un modèle plus petit posé à une question précise a tendance à être à la fois plus rapide et, en pratique, plus cohérent qu'un modèle plus large posé à une question vague.

Les deux projets sont en cours de développement actif, et la comparaison honnête porte moins sur « ce qui est le meilleur » que « ce qui correspond à votre pipeline ». llama.cpp se compile en un seul binaire sans aucun runtime Python requis au moment de l'inférence, ce qui est important si vous souhaitez l'intégrer dans un autre logiciel sans expédier un interpréteur Python à côté. MLX suppose que vous travaillez déjà avec Python (ou Swift, pour lequel il fournit également des liaisons) et le récompense par une intégration plus étroite dans la propre pile d'apprentissage automatique d'Apple et le modèle de mémoire unifiée décrit ci-dessus. Un outil de sécurité construit comme une application macOS autonome, ce qui est la situation dans laquelle se trouve FireAI lui-même, se situe plus près de l'extrémité llama.cpp de ce spectre ; un cahier de recherche explorant quel modèle d'invite fonctionne le mieux se trouve plus près de l'extrémité MLX.

Un modèle d'invite pour trier une connexion

Plus la question que vous posez à un petit modèle local est précise, plus sa réponse est fiable. Pour une seule connexion, cela signifie lui donner exactement les domaines qu'un évaluateur humain examinerait – rien de plus, rien de déduit – et demander un verdict structuré plutôt qu'une prose de forme libre :

modèle d'invite de triage (à titre indicatif, non testé sur aucun ensemble de données)
System: You review one outbound network connection at a time. You are
given only the fields listed below. Do not assume anything not stated.
Respond with exactly two lines: a verdict (allow, ask, or block) and a
one-sentence reason a non-expert could understand.

Connection:
  app: UpdaterHelper.app
  code_signature: unsigned
  destination: 91.203.xxx.xxx:4444
  protocol: TCP
  threat_feed_hit: none
  first_seen: yes (no prior rule for this app)

Verdict:

Les champs qui comptent sont ceux qu'une vérification de signature de code et un flux de menace peuvent réellement produire sans deviner : si le binaire est signé et par qui, où il essaie de se connecter et sur quel port, si cette destination apparaît sur un flux de menace, et si c'est la première fois que cette application tente de se connecter. Demander une sortie fixe sur deux lignes, plutôt qu'une explication ouverte, rend le résultat plus facile à enregistrer, plus facile à comparer sur des milliers de connexions et beaucoup plus difficile à compléter pour le modèle avec un langage de couverture qui semble faire autorité sans rien dire de vérifiable.

De toute façon, un petit modèle ignorera parfois le format demandé : trois lignes au lieu de deux, une mise en garde supplémentaire, un mot de verdict qui n'est pas l'un des trois que vous avez demandés. Traitez cela comme un problème d'ingénierie, pas de modélisation : validez le résultat par rapport à la forme exacte que vous attendez, et si cela ne correspond pas, demandez à nouveau ou revenez au verdict le plus sûr (demander, c'est-à-dire montrer à un humain) plutôt que d'essayer d'analyser une réponse plus lâche. Une étape de tri qui échoue en toute sécurité sur une sortie mal formée est bien plus utile qu'une étape qui produit occasionnellement une ligne d'apparence sûre mais impossible à analyser et la supprime silencieusement.

Exécutez ce modèle sur une journée complète de tentatives de connexion plutôt qu'une à la fois et la forme de la charge de travail change : la plupart des connexions proviennent d'applications avec une règle existante et n'atteignent jamais le modèle du tout, un plus petit nombre sont véritablement vues pour la première fois et obtiennent un verdict, et seule une fraction de ces verdicts sont autre chose qu'une autorisation de routine. Le véritable travail du modèle, à ce stade, n’est pas d’être un expert en sécurité ; il s’agit de réduire une longue liste de connexions observées pour la première fois au petit sous-ensemble qu’une personne doit réellement examiner, ce qui est un objectif beaucoup plus réalisable pour un modèle de quelques milliards de paramètres que ne le serait un jugement de sécurité ouvert.

Où ça va mal

  • Un petit modèle local peut produire une raison sûre, bien écrite et totalement fausse. Rien dans le fait de fonctionner localement ne change ce risque ; cela ne change que là où l'erreur se produit, pas si elle peut se produire.
  • Les fenêtres contextuelles sont limitées et un journal long et bruyant ne rentre pas. Résumer ou pré-filtrer avant que le modèle ne voie les données introduit son propre point d'échec : vous pouvez perdre la ligne qui comptait avant que le modèle n'ait la chance de l'examiner.
  • Le modèle ne voit que ce que contient la ligne de journal. Il ne peut pas voir le trafic crypté, il ne peut pas vérifier qu'un flux de menace est à jour et il ne peut pas connaître votre intention : une connexion à partir d'un outil que vous venez d'installer volontairement semble identique à celle d'un outil dont vous n'avez jamais entendu parler.
  • Un verdict n'est pas une action. Rien ici ne devrait bloquer, supprimer ou autoriser silencieusement une connexion à lui seul ; une personne doit toujours voir la raison et la confirmer, et pouvoir annuler l'appel si le modèle s'est trompé.

Ce dernier point n’est pas une limitation spécifique à un petit modèle quantifié exécuté sur un ordinateur portable : c’est vrai pour toute décision de sécurité automatisée, locale ou cloud, petit ou grand modèle. L’intérêt de l’exécuter localement réside dans ce qu’il supprime de l’équation (une dépendance au réseau, une facture récurrente, un tiers recevant les métadonnées de votre connexion), et non dans l’affirmation selon laquelle cela supprime le besoin d’un humain dans la boucle.

Où FireAI correspond à ce modèle

FireAI, le pare-feu de HisnLabs pour Mac, propose quelque chose construit sur la même idée, dans une portée plus étroite qu'un modèle de chat à usage général : un modèle local facultatif, un téléchargement supplémentaire d'environ 1,5 Go, qui fonctionne entièrement sur Mac et examine les connexions des applications qui n'ont pas encore de règle. Il nécessite macOS 14 ou version ultérieure sur Apple Silicon, applique localement les flux de menaces publiques plutôt que de les comparer à un service distant et affiche la raison de sa décision dans la même invite d'autorisation qu'il utilise pour vous demander d'autoriser ou de refuser la connexion - chacune de ces décisions devient une règle visible et modifiable, et chacune d'entre elles peut être annulée. Il vaut la peine d’être précis sur ce que cela est et ce n’est pas : il ne s’agit pas du modèle de chat ouvert, comportant quelques milliards de paramètres, décrit dans cet article, et ce n’est pas un antivirus ou un VPN – il effectue un travail de classification restreint, localement, et laisse le dernier appel à celui qui lit l’invite.

Le rôle de FireAI et de HisnLabs

FireAI’s own connection reviewer is this exact bet, made narrower still: an optional, roughly 1.5 GB on-device model, macOS 14 and up, Apple silicon only, reviewing one thing (should this unknown app reach this destination) with a visible reason and an undo button — and, like the triage pattern in this article, it still needs a person to confirm anything consequential.

FireAI est le produit de HisnLabs : un pare-feu IA embarqué pour Mac. Il montre en langage clair chaque connexion faite par vos applications et vous laisse décider ce qui sort de votre Mac — son IA fonctionne en local, votre trafic ne nous est jamais envoyé, ni à personne d’autre. L’équipe de recherche en sécurité de HisnLabs est celle qui garde ces décisions fiables : elle recense les domaines de simple télémétrie face à ceux d’un vrai service, suit le pays et le réseau derrière une connexion, et entraîne le modèle embarqué (la fonction Autopilot) sur du trafic réel, sans que rien ne quitte votre Mac.

Vous pouvez lire les choix techniques qui s’y trouvent, ou essayer FireAI pendant 17 jours, sur FireAI, par HisnLabs.

Sources