{"id":7095,"date":"2026-08-31T14:09:05","date_gmt":"2026-08-31T14:09:05","guid":{"rendered":"https:\/\/technodidact.de\/?p=7095"},"modified":"2026-08-31T14:09:06","modified_gmt":"2026-08-31T14:09:06","slug":"die-leise-gefahr-im-smartphone-wie-ble-scanner-zur-ueberwachung-werden","status":"publish","type":"post","link":"https:\/\/technodidact.de\/en\/die-leise-gefahr-im-smartphone-wie-ble-scanner-zur-ueberwachung-werden\/","title":{"rendered":"Die leise Gefahr im Smartphone: Wie BLE-Scanner zur \u00dcberwachung werden"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Autor: DerSchneider<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Smartphone ist l\u00e4ngst zum st\u00e4ndigen Begleiter avanciert \u2013 ein multifunktionales Werkzeug, das uns vernetzt, informiert und unterh\u00e4lt. Doch w\u00e4hrend wir die Annehmlichkeiten der mobilen Konnektivit\u00e4t genie\u00dfen, l\u00e4uft im Hintergrund ein permanenter Austausch von Datenpaketen, der kaum Beachtung findet. Eine dieser Technologien, Bluetooth Low Energy (BLE), ist aus dem Alltag nicht mehr wegzudenken und erm\u00f6glicht eine Vielzahl von Anwendungen \u2013 vom kabellosen Kopfh\u00f6rer bis zum T\u00fcr\u00f6ffner. Dass diese allgegenw\u00e4rtige Funktechnologie jedoch nicht nur Komfort bietet, sondern auch ein bislang untersch\u00e4tztes Risiko f\u00fcr die Privatsph\u00e4re birgt, zeigen aktuelle Entwicklungen und Forschungsarbeiten. Was auf den ersten Blick wie ein harmloses Hilfsmittel zur Ger\u00e4teortung erscheint, entpuppt sich bei n\u00e4herer Betrachtung als potenzielles Werkzeug f\u00fcr eine fl\u00e4chendeckende \u00dcberwachung.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Der tr\u00fcgerische Schein der Anonymit\u00e4t<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Die eigentliche Herausforderung im Bereich des BLE-Schutzes liegt nicht in der Abschottung, sondern in der permanenten Sichtbarkeit. Ein modernes Smartphone sendet im aktiven Zustand kontinuierlich Signale aus, um sich mit seiner Umgebung zu vernetzen. Diese \u201eAdvertising Packets&#8220; sind notwendig, damit andere Ger\u00e4te es \u201esehen&#8220; und eine Verbindung anfragen k\u00f6nnen. In einer Forschungsumgebung wurde gezeigt, wie einfach es ist, diese Signale nicht nur zu empfangen, sondern sie auch systematisch auszuwerten.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ein vollst\u00e4ndiger BLE-Scanner als Beispiel<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Um die technische Umsetzung zu veranschaulichen, zeigt der folgende, vollst\u00e4ndige Code einen funktionsf\u00e4higen BLE-Scanner f\u00fcr den ESP32, wie er in vielen privaten Projekten zu finden ist. Die Kommentare im Code zeigen bereits die Richtung an, in die solche Systeme weiterentwickelt werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">cpp<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><em>\/*<\/em>\n * BLE Scanner f\u00fcr CYD mit Anwesenheits-Historie und E-Mail-Benachrichtigungen\n * \n * Features:\n * - Live-Anzeige aller Ger\u00e4te\n * - Historie der letzten 50 Erkennungen\n * - E-Mail bei neuem Ger\u00e4t\n * - E-Mail bei Ger\u00e4t verschwunden\n * - 24h \u00dcbersicht per E-Mail\n * - Touchscreen zum Umschalten zwischen Live-Ansicht und Historie\n *\/\n\n#include &lt;TFT_eSPI.h&gt;\n#include &lt;BLEDevice.h&gt;\n#include &lt;BLEUtils.h&gt;\n#include &lt;BLEScan.h&gt;\n#include &lt;BLEAdvertisedDevice.h&gt;\n#include &lt;WiFi.h&gt;\n#include &lt;ESP_Mail_Client.h&gt;\n#include &lt;XPT2046_Touchscreen.h&gt;  <em>\/\/ Touchscreen-Bibliothek<\/em>\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ \ud83d\udcdd WIFI &amp; E-MAIL KONFIGURATION<\/em>\n<em>\/\/ ============================================================<\/em>\nconst char* WIFI_SSID = \"DEIN_WLAN_NAME\";\nconst char* WIFI_PASS = \"DEIN_WLAN_PASSWORT\";\n\nconst char* EMAIL_ABSENDER = \"deine.email@gmail.com\";\nconst char* EMAIL_PASSWORT = \"dein_app_passwort\";\nconst char* EMAIL_EMPFANGER = \"empfaenger@email.de\";\nconst char* SMTP_SERVER = \"smtp.gmail.com\";\nint SMTP_PORT = 587;\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ \ud83d\udcdd DISPLAY KONFIGURATION<\/em>\n<em>\/\/ ============================================================<\/em>\nTFT_eSPI tft = TFT_eSPI();\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ \ud83d\udcdd TOUCH KONFIGURATION<\/em>\n<em>\/\/ ============================================================<\/em>\n#define TOUCH_CS 12\nXPT2046_Touchscreen ts(TOUCH_CS);\nuint16_t touchX, touchY;\nbool touchPressed = false;\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ \ud83d\udcdd DEINE PERS\u00d6NLICHE NAMENSLISTE<\/em>\n<em>\/\/ ============================================================<\/em>\nstruct BekanntesGeraet {\n  String mac;\n  String name;\n};\n\nBekanntesGeraet bekannteGeraete[] = {\n  {\"AA:BB:CC:DD:EE:01\", \"Mama\"},\n  {\"AA:BB:CC:DD:EE:02\", \"Papa\"},\n  {\"AA:BB:CC:DD:EE:03\", \"Alex\"},\n};\n\nint anzahlBekannt = sizeof(bekannteGeraete) \/ sizeof(bekannteGeraete[0]);\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ \ud83d\udcdd HISTORIE &amp; ANWESENHEIT<\/em>\n<em>\/\/ ============================================================<\/em>\nstruct HistorienEintrag {\n  String name;\n  String mac;\n  unsigned long zeitstempel;\n  int rssi;\n};\n\n#define MAX_HISTORIE 50\nHistorienEintrag historie[MAX_HISTORIE];\nint historieAnzahl = 0;\nint historieIndex = 0;\n\nstruct Anwesenheit {\n  String mac;\n  String name;\n  unsigned long letzteSichtung;\n  bool istAnwesend;\n  unsigned long anwesenheitsBeginn;\n  unsigned long gesamteAnwesenheit;\n};\n\n#define MAX_GERAETE 20\nAnwesenheit ger\u00e4te[MAX_GERAETE];\nint anzahlGer\u00e4te = 0;\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ \ud83d\udcdd GLOBALE VARIABLEN<\/em>\n<em>\/\/ ============================================================<\/em>\nBLEScan* pBLEScan;\nint scanTime = 3;\nint pauseTime = 2;\nbool zeigeHistorie = false;\nunsigned long letzteEmailZeit = 0;\nunsigned long tagStartZeit = 0;\nconst unsigned long TAG_IN_SEKUNDEN = 86400;\n\nSMTPSession smtp;\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ FUNKTION: MAC -&gt; Name<\/em>\n<em>\/\/ ============================================================<\/em>\nString getNameFuerMAC(String mac) {\n  mac.toUpperCase();\n  for (int i = 0; i &lt; anzahlBekannt; i++) {\n    String bekannteMAC = bekannteGeraete[i].mac;\n    bekannteMAC.toUpperCase();\n    if (bekannteMAC == mac) {\n      return bekannteGeraete[i].name;\n    }\n  }\n  return \"Unbekannt\";\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ FUNKTION: Historie hinzuf\u00fcgen<\/em>\n<em>\/\/ ============================================================<\/em>\nvoid fuegeHistorieHinzu(String name, String mac, int rssi) {\n  if (historieAnzahl &gt; 0) {\n    int letzterIndex = (historieIndex - 1 + MAX_HISTORIE) % MAX_HISTORIE;\n    if (historie[letzterIndex].mac == mac) {\n      historie[letzterIndex].zeitstempel = millis();\n      historie[letzterIndex].rssi = rssi;\n      return;\n    }\n  }\n  \n  historie[historieIndex].name = name;\n  historie[historieIndex].mac = mac;\n  historie[historieIndex].zeitstempel = millis();\n  historie[historieIndex].rssi = rssi;\n  \n  historieIndex = (historieIndex + 1) % MAX_HISTORIE;\n  if (historieAnzahl &lt; MAX_HISTORIE) {\n    historieAnzahl++;\n  }\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ FUNKTION: Anwesenheit aktualisieren<\/em>\n<em>\/\/ ============================================================<\/em>\nvoid aktualisiereAnwesenheit(String name, String mac, int rssi) {\n  unsigned long jetzt = millis() \/ 1000;\n  \n  for (int i = 0; i &lt; anzahlGer\u00e4te; i++) {\n    if (ger\u00e4te[i].mac == mac) {\n      if (!ger\u00e4te[i].istAnwesend) {\n        ger\u00e4te[i].istAnwesend = true;\n        ger\u00e4te[i].anwesenheitsBeginn = jetzt;\n        if (name != \"Unbekannt\") {\n          sendeEmail(\"\u2705 \" + name + \" ist da!\", \n                     name + \" wurde soeben erkannt.\\nRSSI: \" + String(rssi) + \" dBm\");\n        }\n      }\n      ger\u00e4te[i].letzteSichtung = jetzt;\n      return;\n    }\n  }\n  \n  if (anzahlGer\u00e4te &lt; MAX_GERAETE) {\n    ger\u00e4te[anzahlGer\u00e4te].mac = mac;\n    ger\u00e4te[anzahlGer\u00e4te].name = name;\n    ger\u00e4te[anzahlGer\u00e4te].letzteSichtung = jetzt;\n    ger\u00e4te[anzahlGer\u00e4te].istAnwesend = true;\n    ger\u00e4te[anzahlGer\u00e4te].anwesenheitsBeginn = jetzt;\n    ger\u00e4te[anzahlGer\u00e4te].gesamteAnwesenheit = 0;\n    anzahlGer\u00e4te++;\n    \n    if (name != \"Unbekannt\") {\n      sendeEmail(\"\ud83c\udd95 Neues Ger\u00e4t: \" + name,\n                 name + \" (\" + mac + \") wurde zum ersten Mal gesehen.\");\n    }\n  }\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ FUNKTION: Abwesenheit pr\u00fcfen<\/em>\n<em>\/\/ ============================================================<\/em>\nvoid pr\u00fcfeAbwesenheit() {\n  unsigned long jetzt = millis() \/ 1000;\n  unsigned long abwesenheitsSchwelle = 30;\n  \n  for (int i = 0; i &lt; anzahlGer\u00e4te; i++) {\n    if (ger\u00e4te[i].istAnwesend &amp;&amp; \n        (jetzt - ger\u00e4te[i].letzteSichtung) &gt; abwesenheitsSchwelle) {\n      ger\u00e4te[i].istAnwesend = false;\n      ger\u00e4te[i].gesamteAnwesenheit += (jetzt - ger\u00e4te[i].anwesenheitsBeginn);\n      \n      if (ger\u00e4te[i].name != \"Unbekannt\") {\n        sendeEmail(\"\u274c \" + ger\u00e4te[i].name + \" weg!\",\n                   ger\u00e4te[i].name + \" wurde nicht mehr gesehen.\\n\" +\n                   \"War \" + String(jetzt - ger\u00e4te[i].anwesenheitsBeginn) + \" Sekunden da.\");\n      }\n    }\n  }\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ FUNKTION: 24h \u00dcbersicht<\/em>\n<em>\/\/ ============================================================<\/em>\nvoid sendeTages\u00fcbersicht() {\n  String nachricht = \"\ud83d\udcca 24h Anwesenheits\u00fcbersicht\\n\\n\";\n  nachricht += \"Zeitraum: Letzte 24 Stunden\\n\";\n  nachricht += \"================================\\n\\n\";\n  \n  for (int i = 0; i &lt; anzahlGer\u00e4te; i++) {\n    if (ger\u00e4te[i].name != \"Unbekannt\") {\n      unsigned long gesamt = ger\u00e4te[i].gesamteAnwesenheit;\n      if (ger\u00e4te[i].istAnwesend) {\n        gesamt += (millis() \/ 1000 - ger\u00e4te[i].anwesenheitsBeginn);\n      }\n      \n      unsigned long stunden = gesamt \/ 3600;\n      unsigned long minuten = (gesamt % 3600) \/ 60;\n      \n      nachricht += \"\ud83d\udc64 \" + ger\u00e4te[i].name + \"\\n\";\n      nachricht += \"   Anwesend: \" + String(stunden) + \"h \" + String(minuten) + \"m\\n\";\n      nachricht += \"   Status: \" + String(ger\u00e4te[i].istAnwesend ? \"\ud83d\udfe2 Jetzt da\" : \"\ud83d\udd34 Nicht da\") + \"\\n\";\n      nachricht += \"   Letzte Sichtung: \" + String((millis() \/ 1000 - ger\u00e4te[i].letzteSichtung) \/ 60) + \" min her\\n\\n\";\n    }\n  }\n  \n  if (nachricht.length() &lt; 100) {\n    nachricht += \"Keine bekannten Ger\u00e4te erkannt.\\n\";\n    nachricht += \"Warte auf erste Sichtung...\\n\";\n  }\n  \n  sendeEmail(\"\ud83d\udcca T\u00e4gliche Anwesenheits\u00fcbersicht\", nachricht);\n  tagStartZeit = millis() \/ 1000;\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ FUNKTION: E-Mail senden<\/em>\n<em>\/\/ ============================================================<\/em>\nvoid sendeEmail(String betreff, String nachricht) {\n  if (WiFi.status() != WL_CONNECTED) {\n    Serial.println(\"\u274c WiFi nicht verbunden!\");\n    return;\n  }\n  \n  if (millis() - letzteEmailZeit &lt; 10000) {\n    Serial.println(\"\u23f3 E-Mail zu oft, warte...\");\n    return;\n  }\n  \n  Serial.println(\"\ud83d\udce7 Sende E-Mail: \" + betreff);\n  letzteEmailZeit = millis();\n  \n  smtp.debug(0);\n  \n  ESP_Mail_Session session;\n  session.server.host_name = SMTP_SERVER;\n  session.server.port = SMTP_PORT;\n  session.login.email = EMAIL_ABSENDER;\n  session.login.password = EMAIL_PASSWORT;\n  session.login.user_domain = \"gmail.com\";\n  \n  SMTP_Message message;\n  message.sender.name = \"BLE Scanner\";\n  message.sender.email = EMAIL_ABSENDER;\n  message.subject = betreff;\n  message.message = nachricht;\n  message.addRecipient(\"Empf\u00e4nger\", EMAIL_EMPFANGER);\n  \n  if (!smtp.connect(&amp;session)) {\n    Serial.println(\"\u274c SMTP-Verbindung fehlgeschlagen!\");\n    return;\n  }\n  \n  if (!MailClient.sendMail(&amp;smtp, &amp;message)) {\n    Serial.println(\"\u274c E-Mail fehlgeschlagen: \" + smtp.errorReason());\n  } else {\n    Serial.println(\"\u2705 E-Mail gesendet!\");\n  }\n  \n  smtp.closeSession();\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ FUNKTION: Display initialisieren<\/em>\n<em>\/\/ ============================================================<\/em>\nvoid initDisplay() {\n  tft.init();\n  tft.setRotation(1);\n  tft.invertDisplay(true);\n  tft.fillScreen(TFT_BLACK);\n  tft.setTextColor(TFT_WHITE, TFT_BLACK);\n  tft.setTextSize(2);\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ FUNKTION: Touch initialisieren<\/em>\n<em>\/\/ ============================================================<\/em>\nvoid initTouch() {\n  ts.begin();\n  ts.setRotation(1);\n  Serial.println(\"\u2705 Touch initialisiert!\");\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ FUNKTION: Touch lesen<\/em>\n<em>\/\/ ============================================================<\/em>\nbool touchErkannt() {\n  if (ts.touched()) {\n    TS_Point p = ts.getPoint();\n    if (!touchPressed) {\n      touchPressed = true;\n      touchX = p.x;\n      touchY = p.y;\n      \n      <em>\/\/ Nur auf Touch im oberen Bereich reagieren (Statusleiste)<\/em>\n      if (p.x &gt; 0 &amp;&amp; p.x &lt; 320 &amp;&amp; p.y &gt; 0 &amp;&amp; p.y &lt; 50) {\n        return true;\n      }\n    }\n  } else {\n    touchPressed = false;\n  }\n  return false;\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ FUNKTION: Live-Ansicht<\/em>\n<em>\/\/ ============================================================<\/em>\nvoid zeigeLiveAnsicht(BLEScanResults foundDevices) {\n  tft.fillScreen(TFT_BLACK);\n  \n  tft.setTextColor(TFT_CYAN, TFT_BLACK);\n  tft.setTextSize(2);\n  tft.setCursor(10, 10);\n  tft.println(\"\ud83d\udce1 BLE Scanner\");\n  \n  tft.setTextColor(TFT_GREEN, TFT_BLACK);\n  tft.setTextSize(1);\n  tft.setCursor(10, 35);\n  tft.print(\"Gefunden: \");\n  tft.print(foundDevices.getCount());\n  \n  tft.setTextColor(TFT_DARKGREY, TFT_BLACK);\n  tft.setCursor(200, 35);\n  tft.setTextSize(1);\n  tft.print(\"[Touch]\");\n  \n  int deviceCount = foundDevices.getCount();\n  int maxDisplay = 7;\n  \n  if (deviceCount == 0) {\n    tft.setTextColor(TFT_ORANGE, TFT_BLACK);\n    tft.setTextSize(2);\n    tft.setCursor(10, 80);\n    tft.println(\"Keine Ger\u00e4te\");\n  } else {\n    for (int i = 0; i &lt; deviceCount &amp;&amp; i &lt; maxDisplay; i++) {\n      BLEAdvertisedDevice device = foundDevices.getDevice(i);\n      String mac = device.getAddress().toString().c_str();\n      int rssi = device.getRSSI();\n      String name = getNameFuerMAC(mac);\n      \n      int yPos = 60 + (i * 30);\n      tft.fillRect(10, yPos - 5, 300, 30, TFT_BLACK);\n      \n      tft.setTextColor(TFT_WHITE, TFT_BLACK);\n      tft.setTextSize(2);\n      tft.setCursor(10, yPos);\n      tft.print(name);\n      \n      tft.setTextColor(TFT_DARKGREY, TFT_BLACK);\n      tft.setTextSize(1);\n      tft.setCursor(160, yPos + 2);\n      if (mac.length() &gt; 8) tft.print(mac.substring(0, 8) + \"...\");\n      else tft.print(mac);\n      \n      int xPosRSSI = 250;\n      tft.setCursor(xPosRSSI, yPos + 2);\n      if (rssi &gt; -50) tft.setTextColor(TFT_GREEN, TFT_BLACK);\n      else if (rssi &gt; -70) tft.setTextColor(TFT_YELLOW, TFT_BLACK);\n      else tft.setTextColor(TFT_RED, TFT_BLACK);\n      tft.setTextSize(1);\n      tft.print(rssi);\n      tft.print(\"dBm\");\n      \n      if (name != \"Unbekannt\") {\n        aktualisiereAnwesenheit(name, mac, rssi);\n        fuegeHistorieHinzu(name, mac, rssi);\n      }\n    }\n  }\n  \n  tft.setTextColor(TFT_DARKGREY, TFT_BLACK);\n  tft.setTextSize(1);\n  tft.setCursor(10, 300);\n  tft.print(\"Historie: \");\n  tft.print(historieAnzahl);\n  tft.print(\" | Ger\u00e4te: \");\n  tft.print(anzahlGer\u00e4te);\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ FUNKTION: Historie anzeigen<\/em>\n<em>\/\/ ============================================================<\/em>\nvoid zeigeHistorieAnsicht() {\n  tft.fillScreen(TFT_BLACK);\n  \n  tft.setTextColor(TFT_CYAN, TFT_BLACK);\n  tft.setTextSize(2);\n  tft.setCursor(10, 10);\n  tft.println(\"\ud83d\udcdc Anwesenheit\");\n  \n  tft.setTextColor(TFT_GREEN, TFT_BLACK);\n  tft.setTextSize(1);\n  tft.setCursor(10, 35);\n  tft.print(\"Letzte \");\n  tft.print(historieAnzahl);\n  tft.print(\" Ereignisse\");\n  \n  tft.setTextColor(TFT_DARKGREY, TFT_BLACK);\n  tft.setCursor(200, 35);\n  tft.setTextSize(1);\n  tft.print(\"[Touch]\");\n  \n  if (historieAnzahl == 0) {\n    tft.setTextColor(TFT_ORANGE, TFT_BLACK);\n    tft.setTextSize(2);\n    tft.setCursor(10, 80);\n    tft.println(\"Keine Historie\");\n    return;\n  }\n  \n  int maxAnzeige = 8;\n  int startIndex = (historieIndex - 1 + MAX_HISTORIE) % MAX_HISTORIE;\n  \n  for (int i = 0; i &lt; maxAnzeige &amp;&amp; i &lt; historieAnzahl; i++) {\n    int idx = (startIndex - i + MAX_HISTORIE) % MAX_HISTORIE;\n    HistorienEintrag eintrag = historie[idx];\n    \n    int yPos = 60 + (i * 28);\n    tft.fillRect(10, yPos - 5, 300, 28, TFT_BLACK);\n    \n    tft.setTextColor(TFT_WHITE, TFT_BLACK);\n    tft.setTextSize(2);\n    tft.setCursor(10, yPos);\n    tft.print(eintrag.name);\n    \n    tft.setTextColor(TFT_YELLOW, TFT_BLACK);\n    tft.setTextSize(1);\n    tft.setCursor(120, yPos + 2);\n    unsigned long alter = (millis() - eintrag.zeitstempel) \/ 1000;\n    if (alter &lt; 60) tft.print(alter); else tft.print(alter\/60);\n    tft.print(alter &lt; 60 ? \"s alt\" : \"m alt\");\n    \n    tft.setTextColor(TFT_DARKGREY, TFT_BLACK);\n    tft.setCursor(250, yPos + 2);\n    tft.setTextSize(1);\n    tft.print(eintrag.rssi);\n    tft.print(\"dBm\");\n  }\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ SETUP<\/em>\n<em>\/\/ ============================================================<\/em>\nvoid setup() {\n  Serial.begin(115200);\n  Serial.println(\"Starte BLE Scanner mit E-Mail...\");\n  \n  initDisplay();\n  initTouch();  <em>\/\/ Touch initialisieren<\/em>\n  \n  Serial.print(\"Verbinde mit WiFi...\");\n  WiFi.begin(WIFI_SSID, WIFI_PASS);\n  int versuche = 0;\n  while (WiFi.status() != WL_CONNECTED &amp;&amp; versuche &lt; 20) {\n    delay(500);\n    Serial.print(\".\");\n    versuche++;\n  }\n  if (WiFi.status() == WL_CONNECTED) {\n    Serial.println(\"\\n\u2705 WiFi verbunden! IP: \" + WiFi.localIP().toString());\n  } else {\n    Serial.println(\"\\n\u274c WiFi Verbindung fehlgeschlagen!\");\n  }\n  \n  BLEDevice::init(\"CYD-Scanner\");\n  pBLEScan = BLEDevice::getScan();\n  pBLEScan-&gt;setActiveScan(true);\n  pBLEScan-&gt;setInterval(100);\n  pBLEScan-&gt;setWindow(99);\n  \n  tagStartZeit = millis() \/ 1000;\n  Serial.println(\"\u2705 Bereit!\");\n}\n\n<em>\/\/ ============================================================<\/em>\n<em>\/\/ LOOP<\/em>\n<em>\/\/ ============================================================<\/em>\nvoid loop() {\n  if (touchErkannt()) {\n    zeigeHistorie = !zeigeHistorie;\n    delay(300);\n  }\n  \n  unsigned long jetzt = millis() \/ 1000;\n  if (jetzt - tagStartZeit &gt;= TAG_IN_SEKUNDEN) {\n    sendeTages\u00fcbersicht();\n  }\n  \n  if (zeigeHistorie) {\n    zeigeHistorieAnsicht();\n    delay(1000);\n  } else {\n    Serial.println(\"\ud83d\udd0d Scanne...\");\n    BLEScanResults foundDevices = pBLEScan-&gt;start(scanTime, false);\n    Serial.print(\"\ud83d\udcf1 Gefunden: \");\n    Serial.println(foundDevices.getCount());\n    \n    zeigeLiveAnsicht(foundDevices);\n    pr\u00fcfeAbwesenheit();\n    \n    pBLEScan-&gt;clearResults();\n    delay(pauseTime * 1000);\n  }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dieser Code ist ein Paradebeispiel daf\u00fcr, wie viel Macht in wenigen Zeilen Code steckt. Er scannt nicht nur die Umgebung, sondern speichert auch eine Historie, verfolgt Anwesenheitszeiten und versendet sogar E-Mail-Benachrichtigungen. In einem privaten Smart-Home-Szenario, etwa um die Anwesenheit von Familienmitgliedern zu erkennen, mag dies zun\u00e4chst harmlos erscheinen. Die zugrundeliegende Technik ist jedoch dieselbe, die auch f\u00fcr weitaus kritischere Anwendungen genutzt werden kann.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Die d\u00fcnne Grenze zwischen Komfort und Kontrolle<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Die eigentliche Brisanz entfaltet sich, wenn man die m\u00f6glichen Erweiterungen eines solchen Scanners bedenkt. Die folgende Tabelle zeigt, wie schnell aus einem harmlosen Anwesenheitssensor ein m\u00e4chtiges \u00dcberwachungswerkzeug werden kann.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Funktion<\/th><th class=\"has-text-align-left\" data-align=\"left\">Beschreibung<\/th><th class=\"has-text-align-left\" data-align=\"left\">Potenzielle Gefahr<\/th><\/tr><\/thead><tbody><tr><td><strong>MAC-Adress-Logging<\/strong><\/td><td>Speicherung aller gesehenen MAC-Adressen mit Zeitstempel<\/td><td>Erstellung von Bewegungsprofilen<\/td><\/tr><tr><td><strong>Historie &amp; Analyse<\/strong><\/td><td>Speicherung von Anwesenheitszeiten und -h\u00e4ufigkeiten<\/td><td>Detaillierte Einblicke in Alltagsroutinen<\/td><\/tr><tr><td><strong>E-Mail-Benachrichtigung<\/strong><\/td><td>Automatischer Versand von Nachrichten bei bestimmten Ereignissen<\/td><td>Sofortige Information \u00fcber die Abwesenheit einer Person<\/td><\/tr><tr><td><strong>Externe Datenanbindung<\/strong><\/td><td>Senden der Daten an einen Cloud-Dienst oder Server<\/td><td>Zentralisierung und Missbrauch von Bewegungsdaten<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Gef\u00e4hrliche Erweiterungen: Vom Scanner zum \u00dcberwachungssystem<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Die im Code gezeigten Funktionen sind nur der Anfang. Erweiterungen k\u00f6nnten das System in ein noch m\u00e4chtigeres Werkzeug verwandeln:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Automatische Profilerstellung:<\/strong>\u00a0Durch die Verkn\u00fcpfung von MAC-Adressen mit Orten und Zeiten lie\u00dfe sich ein detailliertes Bewegungsprofil jeder erfassten Person erstellen.<\/li>\n\n\n\n<li><strong>Vernetzung mehrerer Scanner:<\/strong>\u00a0Ein Netzwerk aus mehreren Scannern k\u00f6nnte die Bewegung von Personen durch ganze Geb\u00e4ude oder Stadtviertel verfolgen.<\/li>\n\n\n\n<li><strong>Kopplung mit anderen Datenquellen:<\/strong>\u00a0Die Kombination mit WLAN-Scans, Kamerasystemen oder sozialen Medien w\u00fcrde eine nahezu l\u00fcckenlose \u00dcberwachung erm\u00f6glichen.<\/li>\n\n\n\n<li><strong>KI-gest\u00fctzte Analyse:<\/strong>\u00a0Mithilfe von k\u00fcnstlicher Intelligenz lie\u00dfen sich Verhaltensmuster erkennen und sogar Vorhersagen \u00fcber zuk\u00fcnftige Aufenthalte treffen.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Kein Einzelfall: Der ESP32 und seine dokumentierten Debug-Funktionen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Die Gefahr wird durch die weite Verbreitung des Mikrocontrollers ESP32 potenziert. Dieser Chip ist in \u00fcber einer Milliarde Ger\u00e4ten weltweit verbaut und stand aufgrund einer spektakul\u00e4ren Entdeckung im Fokus der Sicherheitsforschung. Im M\u00e4rz 2025 berichtete die spanische Firma Tarlogic Security, dass der ESP32 undokumentierte Befehle enth\u00e4lt \u2013 sogenannte \u201eversteckte Funktionen&#8220;, die urspr\u00fcnglich f\u00fcr Debugging- und Testzwecke vorgesehen waren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Entdeckung f\u00fchrte zun\u00e4chst zu Besorgnis, da die Befehle potenziell eine Hintert\u00fcr in milliardenf\u00e4higen Ger\u00e4ten darstellen k\u00f6nnten. Eine genauere Analyse durch den Hersteller Espressif und unabh\u00e4ngige Sicherheitsexperten relativierte die Gefahr jedoch deutlich. Es stellte sich heraus, dass es sich nicht um eine b\u00f6swillig eingebaute Backdoor handelt, sondern um interne Debug-Funktionen, die:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>nicht aus der Ferne (remote) ausgenutzt werden k\u00f6nnen<\/strong>\u00a0\u2013 Angreifer m\u00fcssten physischen Zugang zum Ger\u00e4t haben,<\/li>\n\n\n\n<li><strong>keine Internetverbindung ben\u00f6tigen<\/strong>, um genutzt zu werden, daf\u00fcr aber privilegierte Zugriffsrechte auf dem System voraussetzen,<\/li>\n\n\n\n<li>als \u201eschlechte Praxis&#8220; des Herstellers gelten, aber kein aktives Sicherheitsrisiko f\u00fcr Endanwender darstellen.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Espressif reagierte umgehend auf die Berichterstattung, stellte die Funktionen klar und k\u00fcndigte an, die entsprechenden Debug-Befehle in zuk\u00fcnftigen Chip-Revisionen zu entfernen oder besser zu dokumentieren. Die Sicherheitsl\u00fccke wurde unter der Kennung&nbsp;<strong>CVE-2025-27840<\/strong>&nbsp;registriert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dennoch wirft der Vorfall ein grelles Licht auf die Verwundbarkeit der globalen IoT-Infrastruktur. Die Tatsache, dass ein f\u00fcr wenige Euro erh\u00e4ltlicher Chip undokumentierte Funktionen enthalten kann, zeigt die Dringlichkeit, die Sicherheit von BLE-Systemen zu \u00fcberdenken. Auch wenn die ESP32-Debug-Befehle selbst keine unmittelbare Gefahr darstellen, bleibt das zugrundeliegende Problem bestehen: Die F\u00e4higkeit, BLE-Signale passiv zu erfassen und auszuwerten, erm\u00f6glicht eine neue \u00c4ra der \u00dcberwachung.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ein Ausblick in die gl\u00e4serne Gesellschaft<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Die hier beschriebenen Techniken sind keine Zukunftsmusik mehr, sondern bereits heute im Einsatz. Die F\u00e4higkeit, BLE-Signale passiv zu erfassen und auszuwerten, hat eine neue \u00c4ra der \u00dcberwachung eingel\u00e4utet. Die scheinbare Anonymit\u00e4t im \u00f6ffentlichen Raum ist l\u00e4ngst zur Illusion geworden, denn jedes mitgef\u00fchrte Smartphone sendet unerm\u00fcdlich eine eindeutige Kennung aus. Zwar haben die Hersteller mit der MAC-Adress-Randomisierung eine Schutzma\u00dfnahme eingef\u00fchrt, doch wie aktuelle Forschungen zeigen, lassen sich Ger\u00e4te dennoch \u00fcber Verhaltensmuster oder andere statische Identifikatoren in den Broadcast-Daten identifizieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die rechtlichen Rahmenbedingungen, insbesondere die Datenschutz-Grundverordnung (DSGVO) in Europa, versuchen zwar, Grenzen zu ziehen, doch die technologische Entwicklung l\u00e4uft ihnen oft davon. Es bedarf daher nicht nur eines Bewusstseinswandels bei den Nutzern, die den Umgang mit ihren Ger\u00e4ten hinterfragen sollten, sondern auch eines Umdenkens bei den Herstellern. Die Integration von Sicherheitsma\u00dfnahmen wie Secure Boot, authentifizierten Firmware-Updates und einer robusten Verschl\u00fcsselung ist kein optionales Extra, sondern eine Notwendigkeit, um das Vertrauen in die digitalen Technologien zu bewahren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der leichte Umgang mit dem Smartphone und seinen Funktionen f\u00fchrt uns geradewegs in eine Welt, in der das eigene Ger\u00e4t zum stillen Kronzeugen gegen einen selbst wird. Der gezeigte Code ist dabei nur ein kleiner Ausschnitt des M\u00f6glichen \u2013 ein Beispiel daf\u00fcr, wie einfach es ist, mit wenigen Zeilen Code eine \u00dcberwachungsinfrastruktur aufzubauen. Es ist an der Zeit, diese Entwicklung kritisch zu hinterfragen und sich der Tatsache bewusst zu werden, dass die Technologie, die uns verbindet, uns auch gl\u00e4sern machen kann.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Quellen<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Tarlogic Security: \u201eUndocumented Commands Found in Espressif&#8217;s ESP32 Chip&#8220; (2025) \u2013\u00a0<em>inzwischen durch Hersteller und CERT klarstellend erg\u00e4nzt<\/em><\/li>\n\n\n\n<li>Espressif Systems: Offizielle Stellungnahme zu CVE-2025-27840 (M\u00e4rz 2025)<\/li>\n\n\n\n<li>INCIBE (spanisches CERT): Sicherheitshinweis zu CVE-2025-27840<\/li>\n\n\n\n<li>IEEE Xplore: \u201eA Survey on Bluetooth Low Energy Security&#8220; (2024)<\/li>\n\n\n\n<li>Datenschutz-Grundverordnung (DSGVO) \u2013 EU-Verordnung 2016\/679<\/li>\n\n\n\n<li>Espressif Systems: ESP32 Technical Reference Manual<\/li>\n\n\n\n<li>USENIX Security Symposium: \u201eBLE Location Tracking&#8220; (2023)<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Autor: DerSchneider Das Smartphone ist l\u00e4ngst zum st\u00e4ndigen Begleiter avanciert \u2013 ein multifunktionales Werkzeug, das uns vernetzt, informiert und unterh\u00e4lt. Doch w\u00e4hrend wir die Annehmlichkeiten der mobilen Konnektivit\u00e4t genie\u00dfen, l\u00e4uft im Hintergrund ein permanenter Austausch von Datenpaketen, der kaum Beachtung findet. Eine dieser Technologien, Bluetooth Low Energy (BLE), ist aus dem Alltag nicht mehr wegzudenken [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[10517,1424,10218,2225,3428,5529,10178,10518,705],"class_list":["post-7095","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-bluetooth-low-energy","tag-datenschutz","tag-derschneider","tag-esp32","tag-iot-sicherheit","tag-privatsphare","tag-technodidact","tag-technologieethik","tag-uberwachung"],"_links":{"self":[{"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/posts\/7095","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/comments?post=7095"}],"version-history":[{"count":4,"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/posts\/7095\/revisions"}],"predecessor-version":[{"id":7099,"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/posts\/7095\/revisions\/7099"}],"wp:attachment":[{"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/media?parent=7095"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/categories?post=7095"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/tags?post=7095"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}