Skip to content
Table of contents6 sections · tap to jump
  1. What's Actually Phoning Home: A Network Audit of Local LLM Runtimes
  2. The Open Door on Localhost: DNS Rebinding and the Loopback Myth
  3. Dirty Weights: Supply-Chain Integrity and the Pickle Problem
  4. Where Your Prompts Actually Live: Disk, Swap, and Log Hygiene
  5. Shared Silicon: GPU Memory, Multi-Tenancy, and Side Channels
  6. The Hardening Playbook: Controls That Actually Close the Gaps
The Local Illusion: The Real Security Risks of Running a Local LLM

ArticlesecurityDeep read

The Local Illusion: The Real Security Risks of Running a Local LLM

BitByteCore Security DeskAug 9, 20266 min

Running an LLM on your own hardware means the math is local — not that your data is safe. Five concrete places it still leaks or gets altered — unauthenticated localhost ports, pickle-based weights, swap files, telemetry, and shared GPU memory — and how to close each one.

A deep read — the full picture, with the receipts.

Signaldefinitive2independent sources

Running a large language model on your own hardware feels like a fortress of solitude. The weights sit on your disk, the tokens come out of your own GPU, and nothing leaves the building. That feeling is mostly true — and the "mostly" is where you get hurt.

"Local" describes where the math runs. It does not describe your threat model. The moment you pull a model off the internet, open a port so an app can talk to it, and write a conversation to disk, you have re-created most of the exposure you were trying to escape — just with fewer people watching. This piece walks the actual path your data takes when you run Ollama, LM Studio, llama.cpp, or Jan, and points at the specific places it can leak or be tampered with. Five of them, plus the controls that actually close each gap.

What's Actually Phoning Home: A Network Audit of Local LLM Runtimes#

The first myth to kill is that "local" means "offline." It doesn't, not by default.

Start with the runtime itself. Ollama is actually one of the better-behaved tools here: it does not ship your prompts anywhere, and it has no usage telemetry turned on by default. What it does do is check for updates — the macOS and Windows desktop builds contact Ollama's servers to compare version numbers and can auto-download a new build. (The Linux install has no auto-updater; you re-run the script by hand, so nothing is quietly phoning home.) It also reaches out, obviously, when you pull a model from the registry. None of that is sinister, but it is network traffic, and "it checked for an update" is a very different security statement from "nothing left the machine."

The leakier culprits are usually the libraries stacked around the model. Hugging Face's transformers and huggingface_hub will, by default, contact the Hub to resolve the latest revision of a model or tokenizer and to emit basic usage telemetry — which is exactly why HF_HUB_OFFLINE=1 and HF_HUB_DISABLE_TELEMETRY=1 exist. If you have bolted a vector database onto your setup for retrieval, watch it too: ChromaDB, a common choice, ships anonymized product telemetry on by default. Each of these is individually defensible, and collectively it means a "local" retrieval stack can be talking to three or four external endpoints before you have typed a word.

The takeaway is not "these tools are spyware." It is that offline is a configuration you have to choose and then verify — with a firewall rule or a packet capture — not a property you get for free by running things on your own hardware.

The Open Door on Localhost: DNS Rebinding and the Loopback Myth#

Local runtimes expose an HTTP API so your apps can talk to them. Ollama listens on 127.0.0.1:11434; LM Studio and llama.cpp's server listen on ports of their own. Binding to loopback feels safe — 127.0.0.0/8 is, by definition, reachable only from the same machine — so most of these servers ship with no authentication at all. Anything that can reach the port gets full access to the model, whatever context is loaded, and, in Ollama's case, the ability to pull or delete models.

"Anything that can reach the port" is a bigger set than it looks. Two ways in.

First, DNS rebinding. A web page you visit runs JavaScript in your browser, which is already on your machine. Normally the same-origin policy stops that script from talking to localhost. DNS rebinding defeats it: the attacker's domain first resolves to their real server, then re-resolves to 127.0.0.1, and the browser — still thinking it is talking to the origin it trusts — happily forwards requests to your local LLM server. No firewall rule helps, because the traffic originates from your own browser. This is not hypothetical; unauthenticated local model servers have been demonstrated as rebinding targets.

Second, and more common, self-inflicted exposure. To reach Ollama from a phone or another laptop, people set OLLAMA_HOST=0.0.0.0. That binds the still-unauthenticated API to every network interface, and if the box has a public IP or a forwarded port, the API is now on the open internet. Internet-wide scans routinely turn up exposed, unauthenticated Ollama endpoints that anyone can send prompts to, read models from, or delete models on.

The correct mental model: a local model server is an unauthenticated service. Treat the port the way you would treat an open database port, because that is what it is.

Dirty Weights: Supply-Chain Integrity and the Pickle Problem#

You download a model — a Llama, Qwen, DeepSeek, Gemma, or Mistral checkpoint — from Hugging Face, the Ollama registry, or a random GitHub release. What did you actually just run?

The sharp risk here is not poisoned training data — you are running inference, not training — it is the file format. Classic PyTorch checkpoints (.bin, .pt, .ckpt) are Python pickle objects, and unpickling is arbitrary code execution by design: torch.load on a malicious checkpoint can run whatever the author embedded, with your user's permissions, the instant you load it. This is not exotic. Malicious pickle-based models have been caught on the Hugging Face Hub, which is why the Hub scans uploads for it and why the safetensors format exists — it stores only tensors, with no executable payload, so loading it cannot run code. If a model is offered as both .bin and .safetensors, take the safetensors.

The GGUF format used by llama.cpp, Ollama, and LM Studio is also data-only, which is the right design — but "data-only" shifts the risk from intended code execution to parser bugs. A crafted GGUF file can trip memory-safety flaws in the loader; such bugs have been found and patched in these parsers. So "it's just weights" is not the same as "it's safe to load."

Underneath all of this is a plain integrity question: almost none of these downloads are cryptographically signed to a publisher you trust. You are relying on the distribution platform not being compromised and not serving you a swapped file. A hash published next to the model only helps if the attacker did not also control the page that lists the hash. Verify the checksum the maintainer publishes, prefer signed releases where they exist, and be honest that for most local models today the trust root is "the Hub didn't get owned."

Where Your Prompts Actually Live: Disk, Swap, and Log Hygiene#

Everything you type has to land somewhere. When you chat with a local model through a front-end — Open WebUI, LM Studio, Jan, GPT4All, AnythingLLM — that conversation is almost always persisted so you can scroll back to it. The question is where, and in what state.

In practice, most of these apps write your history to a SQLite database or JSON files under your home or app-data directory, in plaintext. System prompts, every message, and any documents you attached for retrieval sit there readable by anything running as your user — another app, a malicious dependency in some unrelated project, a backup agent quietly syncing your home folder to the cloud. "It's on my machine" and "it's protected" are not the same claim.

Two more places data lands that people forget. Swap: when memory gets tight, the OS pages RAM out to disk, and fragments of prompts, context, and model activations can end up written to the swap file or partition in cleartext — persisting well after the process has exited, unless swap is encrypted. And logs: verbose or debug logging in a server or gateway will cheerfully record full prompts and completions to a file that has none of the access controls you would want around your actual conversations. If you turned on debug logging to chase a bug and forgot, your transcript is now in a logfile.

Shared Silicon: GPU Memory, Multi-Tenancy, and Side Channels#

"Local" quietly assumes the machine is yours alone. Plenty of the time it isn't — a shared workstation, a family PC, a rented cloud GPU, a container on a host you share with strangers. The moment the hardware is shared, you inherit its isolation weaknesses.

The GPU is the sharp edge, because GPU memory has historically not been zeroed between users the way modern operating systems handle CPU memory. LeftoverLocals (CVE-2023-4969), disclosed by Trail of Bits in January 2024, is the canonical example: on affected Apple, AMD, Qualcomm, and Imagination GPUs, a program could read the local memory left behind by another process's GPU kernels — and the researchers built a proof-of-concept that reconstructed another user's LLM responses across process and container boundaries from roughly ten lines of code. Nvidia's and Arm's GPUs were not affected, and vendors shipped fixes, but the lesson generalizes: GPU memory isolation is younger and thinner than the CPU and OS isolation you take for granted, and a container boundary is not automatically a GPU boundary.

On a shared box the risks stack: other users or processes may be able to observe your model sitting in VRAM, side-channel attacks can leak information through shared caches and timing, and "the VM is isolated" says nothing about the physical GPU that VM is passing through. If your threat model includes the other tenants of the machine, "local" has bought you very little on its own.

The Hardening Playbook: Controls That Actually Close the Gaps#

None of this means don't run models locally. It means treat the setup like the small production system it is. Concretely:

  • Lock down the network. Decide whether the box should reach the internet at all, then enforce it with a firewall — UFW or nftables on Linux, pf or Little Snitch on macOS, with per-app egress rules — rather than trusting each tool's defaults. Keep model servers bound to 127.0.0.1; never set 0.0.0.0 on an untrusted network, and if you must expose the API, put an authenticating reverse proxy in front of it. Set HF_HUB_OFFLINE=1 and disable library telemetry once your models are pulled.
  • Verify what you load, and prefer safe formats. Choose safetensors or GGUF over pickle-based .bin/.pt checkpoints. Verify the maintainer's published SHA-256 against what you downloaded — not MD5, which is collision-broken and useless for tamper detection — and prefer signed releases where a publisher offers them.
  • Encrypt the disk, and mind the leftovers. Turn on full-disk encryption (FileVault, LUKS, or BitLocker) so history, model caches, and swap are not sitting in cleartext, and enable encrypted swap specifically. Turn off debug logging in front-ends and gateways once you are done debugging, and clear chat history you do not need to keep.
  • Isolate the process. Run the runtime in a container (Docker or Podman) with --network none for pure offline inference and Linux capabilities dropped — that is real namespace isolation, not the same thing as an old chroot. On shared or multi-tenant hardware, keep GPU drivers patched (that is how LeftoverLocals-class bugs get closed) and do not assume a VM or container isolates you at the GPU level.

Running a model on your own hardware is a real privacy win — it takes the single biggest actor, the cloud provider, out of the loop. But "local" is a statement about where computation happens, not a security guarantee. The data still travels a path: off the internet onto your disk, through an unauthenticated port, into GPU memory, and back out to a logfile. Secure the path, not the feeling. Assume the port is exposed, assume the weights are untrusted until verified, assume the disk remembers — and configure accordingly.

Sources

  1. NIST AI 100-2 E2025 — Adversarial Machine Learning: A Taxonomy and Terminology of Attacks and Mitigationscsrc.nist.gov
  2. NIST — AI Risk Management Frameworknist.gov

Ask about this article

Answered only from this piece — the AI never invents.

React
ShareXLinkedInBluesky

More in securityMore in security

Discussion