Skip to content

Blynk auth token leaks into HA logs and registries (debug URLs, exception messages, unique_id) #43

Description

@brian-thecrmphd

The integration leaks the Blynk auth token (the credential entered at setup) into Home Assistant logs and persistent storage through four distinct paths. Found while doing log forensics on a live install (v1.0.6, current master matches).

1. Token logged in cleartext twice per poll at DEBUG — and DEBUG is force-enabled

blynk_service.py builds the request URL with the token as a query parameter and logs it verbatim:

url = self._get_request_url(f'external/api/get', params) + f"&{pin}"   # params = {'token': self.token}
_LOGGER.debug(f"Request URL: {url}")          # line 39, and again at line 66

Because blynk_service.py, climate.py, and entity.py all call _LOGGER.setLevel(logging.DEBUG) at module import (line 7 here), these lines are emitted on every install regardless of the user's logger: configuration defaults. With the coordinator's 60s poll interval, the token lands in the log ring buffer ~2,880 times/day, and from there into every log export, ha core logs capture, and diagnostics bundle. (Side effect: the DEBUG volume — ~30 lines/poll — can flush the entire HAOS log buffer, wiping unrelated log history.)

2. Token leaks at ERROR level when the Blynk endpoint is unreachable

blynk_service.py calls bare requests.get(url) with no exception handling. When dashboard.windmillair.com is unreachable (DNS failure, outage, offline LAN), requests.exceptions.ConnectionError's message embeds the full URL — token included:

HTTPConnectionPool(host='...'): Max retries exceeded with url: /external/api/get?token=<TOKEN>&V1 (...)

That propagates to coordinator.py:37-38:

_LOGGER.error(f"Error fetching data: {err}")
raise UpdateFailed(f"Error fetching data: {err}")

So even a user who has suppressed the DEBUG flood with logger: overrides still gets the token at ERROR level once per poll for as long as the endpoint is down (verified empirically on 1.0.6). The UpdateFailed message also surfaces in the config-entry "not ready" INFO lines.

3. Token persisted in unique_id and device identifiers

entity.py:

self._attr_unique_id = f"{DOMAIN}_{coordinator.blynk_service.token}_{entity_description.key}"
identifiers={(DOMAIN, self.unique_id)},

The raw token is written into core.entity_registry and core.device_registry, so it ships inside every HA backup and registry/diagnostics export. HA core also prints unique_ids in some of its own log lines (e.g. the duplicate-unique-ID error), which the integration cannot redact.

4. (Minor) requests.get has no timeout=

Not a leak, but a hung connection blocks the executor thread indefinitely. requests never times out by default.

Suggested fixes

  • Remove the three module-level _LOGGER.setLevel(logging.DEBUG) calls — let users opt into debug via logger:.

  • Never log the full request URL; log the endpoint/pin only, or redact the token (note the URL carries the percent-encoded token via urlencode, so redaction should cover both token and quote_plus(token)).

  • Wrap the requests.get calls in try/except requests.exceptions.RequestException and re-raise with the token redacted from the message (raise ... from None so the chained traceback doesn't reintroduce it). This is what I've patched locally and it cleanly closes path 2:

    try:
        response = requests.get(url, timeout=10)
    except requests.exceptions.RequestException as err:
        raise Exception(f"Request failed getting pin {pin}: {redact(err)}") from None
  • Derive unique_id from something non-secret — e.g. a hash of the token or the config entry id (entry.entry_id) — with a registry migration for existing installs.

  • Users affected by 1–3 should regenerate their token in the Windmill dashboard after upgrading, since existing logs/backups already contain it.

Happy to open a PR for any subset of this if useful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions