<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Cybersecurity on tiago mendes</title>
    <link>https://tiago.mendes.im/tags/cybersecurity/</link>
    <description>Recent content in Cybersecurity on tiago mendes</description>
    <generator>Hugo -- 0.147.0</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 28 Apr 2026 00:00:00 +0100</lastBuildDate>
    <atom:link href="https://tiago.mendes.im/tags/cybersecurity/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Google Cybersecurity Certificate</title>
      <link>https://tiago.mendes.im/posts/google-cybersecurity-certificate/</link>
      <pubDate>Tue, 28 Apr 2026 00:00:00 +0100</pubDate>
      <guid>https://tiago.mendes.im/posts/google-cybersecurity-certificate/</guid>
      <description>Finished the Google Cybersecurity Professional Certificate on Coursera. Here&amp;rsquo;s what&amp;rsquo;s good, what&amp;rsquo;s bad, and how it compares to a university course on the same topic.</description>
      <content:encoded><![CDATA[<p>In January 2026 I started the <a href="https://www.coursera.org/professional-certificates/google-cybersecurity">Google Cybersecurity Professional Certificate</a> on Coursera. The lectures for <strong>Informatics and Communications Security</strong> (SIC), a 3rd year course at the University of Aveiro, had got me interested in going deeper into the field, and with an exam coming up I figured I&rsquo;d combine both. SIC covers a lot of the same ground: cryptography from scratch (digest functions, symmetric, asymmetric, PKI), authentication protocols, IPSec, SSH, firewalls, OS hardening. There was enough overlap to make the Google course useful as exam prep and worth doing in its own right.</p>
<p>The certificate has 9 courses:</p>
<ol>
<li>Foundations of Cybersecurity</li>
<li>Play It Safe: Manage Security Risks</li>
<li>Connect and Protect: Networks and Network Security</li>
<li>Tools of the Trade: Linux and SQL</li>
<li>Assets, Threats, and Vulnerabilities</li>
<li>Sound the Alarm: Detection and Response</li>
<li>Automate Cybersecurity Tasks with Python</li>
<li>Put It to Work: Prepare for Cybersecurity Jobs</li>
<li>Accelerate Your Job Search with AI</li>
</ol>
<p>Coursera offers a 7-day free trial. In January I used it to get through courses 1 to 4, then other exams got in the way and I let it expire rather than pay $49/month, which is steep. Courses 5 through 9 had to wait. A few days ago I came back, grabbed another trial (I am NOT paying 49$), and finished everything. The first 5 I had already done so they went fast, about 1 day, and the last 4 took another 2. All 9 done within a week.</p>
<h2 id="whats-good">What&rsquo;s good</h2>
<h3 id="videos">Videos</h3>
<p>Short, 2 to 8 minutes each, and consistently well produced. Each concept gets its own clip, so you can move quickly through things you already know and slow down where you need to. I took a lot of notes on Course 6 (detection and response) and skimmed most of the rest.</p>
<h3 id="quizzes">Quizzes</h3>
<p>4 to 10 questions per module, conceptual rather than tricky. With a CS background they are not a challenge, but they do make you check whether you actually absorbed the video or just had it on in the background while doing something else (not that I would know anything about that).</p>
<h2 id="whats-bad">What&rsquo;s bad</h2>
<h3 id="repetition">Repetition</h3>
<p>The biggest issue by far. SIEM (Security Information and Event Management, basically a tool that aggregates logs from across your infrastructure and helps you detect threats) is introduced in Course 2, reappears in Course 3&rsquo;s network security section, and then Course 6 dedicates an entire module to it. Brute force attacks (trying every possible password combination until one works) show up in Course 3 and again in Course 5. OWASP (Open Worldwide Application Security Project, the organisation behind the well-known Top 10 list of most critical web vulnerabilities) principles are in Course 2, and the OWASP Top 10 is back in Course 5. None of it builds on what came before, it is just the same explanation with slightly different slides. By Course 6 you notice it, and by Course 7 you start wondering if the authors have a SIEM-shaped hole in their hearts.</p>
<h3 id="course-7-python">Course 7 (Python)</h3>
<p>Python 101: variables, loops, functions, strings, lists, regex, file handling. If you have written any code before, skip it entirely. I took zero notes and finished it faster than it takes to make coffee.</p>
<h3 id="courses-8-and-9">Courses 8 and 9</h3>
<p>Mostly career prep: how to escalate incidents, how to communicate with stakeholders, how to use AI for job hunting. Course 9 is literally called &ldquo;Accelerate Your Job Search with AI.&rdquo; Fine if you are entering the field from a non-technical background, but if you are doing this alongside a CS degree it feels like the course ran out of technical content and needed to fill two more modules.</p>
<p>More broadly, a lot of the material (Python, SQL, web vulnerabilities) is not difficult with <strong>any</strong> programming background. The course is built for people without one, which makes sense, but it does mean you will spend more time than you would like on things you already know.</p>
<h2 id="how-it-compares-to-sic">How it compares to SIC</h2>
<p>The UA course goes into depth that Google does not touch: the math behind cryptographic primitives, how PKI (Public Key Infrastructure, the system of certificates and authorities that makes encrypted communication on the web possible) works at the protocol level, SSH internals, IPSec (the protocol suite that secures traffic at the network layer, used in most VPNs), the full evolution of wireless security standards from WEP to WPA3. Google covers VPNs, firewalls, brute force, phishing, and IDS/IPS (Intrusion Detection and Prevention Systems, which monitor network traffic for suspicious activity), but at a surface level. The difficulty gap is significant.</p>
<p>The parts that were actually useful for the exam were network security and threat modeling, where the Google course gave me a slightly different framing of concepts I had already seen in the UA lectures. Not a large difference, but occasionally helpful.</p>
<h2 id="is-it-worth-it">Is it worth it?</h2>
<p>At $49/month, probably not, unless you need the certificate for a job application. For anyone with a CS background, courses 1 to 5 are where the value is: they actually focus on cybersecurity, and the 7-day trial is enough to get through them if you commit to it. Courses 6 to 9 are where the repetition and career fluff kick in, and the returns drop off quickly.</p>
<p>As a broad introduction to the cybersecurity field before going deeper elsewhere, it is worth doing once. Just use the trial.</p>
<p>Google&rsquo;s certificate is a good starting line, not a destination :)</p>
]]></content:encoded>
    </item>
    <item>
      <title>Building a secure BLE tree network</title>
      <link>https://tiago.mendes.im/posts/secure-ble-tree-network/</link>
      <pubDate>Wed, 28 Jan 2026 10:00:00 +0000</pubDate>
      <guid>https://tiago.mendes.im/posts/secure-ble-tree-network/</guid>
      <description>A walkthrough of the final project for my security class, a secure ad-hoc Bluetooth network over BLE with a tree topology, mutual auth, ECDH session keys, and DTLS-style end-to-end encryption</description>
      <content:encoded><![CDATA[<p>Last semester I took the Computer and Network Security course in my degree. The final project was this group thing where we had to build a secure ad-hoc network over Bluetooth, in a tree topology, that routes messages between nodes through a central sink, with proper crypto all the way down. Ended up being a ton of fun to work on, mostly because it touched so many different things at once: wireless transport, ad-hoc routing, PKI, key exchange, message integrity, end-to-end encryption, and a GUI to watch it all happen. Writing about it because there&rsquo;s more than enough interesting pieces in there to fill a post.</p>
<h1 id="the-rough-idea">The rough idea</h1>
<p>The setup is basically a tree. There&rsquo;s one <strong>Sink</strong> at the root and a bunch of <strong>Nodes</strong> hanging off it in layers. Each node forwards messages upward to the Sink, the Sink routes them down to wherever they need to go, and end-to-end everything is encrypted so intermediate nodes can&rsquo;t actually read the payloads they&rsquo;re carrying. The whole thing runs over <strong>Bluetooth Low Energy</strong>, using the Linux BlueZ stack underneath, and the nodes form the tree automatically by picking the best parent they can find.</p>
<p>The interesting bit for a security course is the layered protection: everything between two nodes is mutually authenticated with certificates, every hop is HMAC-protected against tampering, and end-to-end traffic gets its own AES-256-GCM layer so even intermediate nodes on the path only see routing headers, never content. Kind of like DTLS, but cobbled together over BLE GATT characteristics.</p>
<h1 id="why-ble-and-why-a-tree">Why BLE and why a tree</h1>
<p>BLE mostly because the spec asked for it, but also because it&rsquo;s actually pretty convenient: discovery and pairing primitives are built in, you get small broadcasts for advertising, and GATT gives you a clean request/response channel once connected. Tree topology because it&rsquo;s about the simplest routing structure that still lets arbitrary nodes talk through a central authority (the Sink), and because it limits how much state any one node has to carry.</p>
<p>Nodes pick a parent by scanning around, reading the hop count each candidate advertises, and going with the lowest one they can find. So if a node hears a neighbor that&rsquo;s 2 hops from the Sink, it&rsquo;ll prefer that one over a neighbor that&rsquo;s 4 hops away, and its own hop count becomes 3. Enough to get a working tree without any central coordinator.</p>
<p><img alt="Tree topology with Sink at hop=0 and nodes fanning out by hop count" loading="lazy" src="/i1/ble_tree_topology.svg">
<em>The tree forms on its own as nodes pick the lowest-hop parent they can see.</em></p>
<h1 id="dual-role-ble-aka-a-node-is-both-a-client-and-a-server">Dual-role BLE (aka &ldquo;a node is both a client and a server&rdquo;)</h1>
<p>Each node has to accept connections from children below it while also maintaining its own uplink to its parent, which means it needs to be a <strong>GATT server</strong> and a <strong>GATT client</strong> at the same time. Python doesn&rsquo;t really have one library that does both cleanly, so we mixed two:</p>
<ul>
<li><code>bleak</code> for the client side (uplink towards the parent)</li>
<li><code>dbus-python</code> + GLib for the server side (downlink from children)</li>
</ul>
<p>The GUI runs on top of that in Tkinter, so at runtime each node has three concurrent contexts going: <strong>GLib</strong> for D-Bus/BlueZ callbacks, <strong>asyncio</strong> for the Bleak client, and <strong>Tkinter</strong> for the UI. Keeping those three cooperating takes a bit of care, especially around anything touching shared state.</p>
<p><img alt="Dual-role node: asyncio/bleak uplink, GLib/dbus-python downlink, Tkinter GUI, all coordinating through shared state" loading="lazy" src="/i1/dual_role_architecture.png">
<em>One process, three concurrent contexts, one block of shared state holding them together.</em></p>
<h1 id="the-security-side">The security side</h1>
<p>This is the part the course was actually about.</p>
<h2 id="provisioning-and-pki">Provisioning and PKI</h2>
<p>Before anything runs, every device gets provisioned with its own credentials:</p>
<ul>
<li>A <strong>P-521 ECC keypair</strong></li>
<li>An <strong>X.509 certificate</strong> signed by a shared CA</li>
<li>A copy of the CA cert so it can verify others</li>
</ul>
<p>The device identity (a 128-bit NID) is embedded into the Subject Alternative Name of its certificate, so you can&rsquo;t just present any valid cert, the NID in the cert has to match the one the device is advertising over BLE.</p>
<h2 id="mutual-auth-on-pairing">Mutual auth on pairing</h2>
<p>When two nodes pair, both read each other&rsquo;s certificate off a known GATT characteristic, verify the signature against the CA public key, and check that the NID in the cert matches the one being advertised. If either check fails, the pairing just doesn&rsquo;t happen. Pretty standard mutual auth, but wiring it into GATT read/write flows in both directions is its own little adventure.</p>
<h2 id="session-keys-via-ephemeral-ecdh">Session keys via ephemeral ECDH</h2>
<p>Once both sides trust each other, they do an <strong>ephemeral ECDH</strong> handshake. Each generates a fresh P-521 keypair, they exchange public halves, compute the shared secret, and run it through <strong>HKDF-SHA256</strong> to derive a session key. Because the keypairs are ephemeral, every connection has its own session key, so past traffic stays safe even if a long-term key leaks later. That&rsquo;s basically <strong>Perfect Forward Secrecy</strong>, at least for the link layer.</p>
<p><img alt="Sequence diagram of mutual authentication followed by the ephemeral ECDH handshake" loading="lazy" src="/i1/mutual_auth_ecdh.png">
<em>Certs first, then ephemeral key exchange. The session key is derived locally on both sides and never actually travels on the wire.</em></p>
<h2 id="hop-by-hop-integrity">Hop-by-hop integrity</h2>
<p>Every message on the wire gets prepended with a sequence counter and an <strong>HMAC-SHA256</strong> computed with the session key, then the routing headers, then the payload. The receiver verifies the HMAC, checks the sequence counter is strictly greater than the last one it saw from that sender (that&rsquo;s the <strong>replay protection</strong>), and only then processes the message. This layer is always on, including for end-to-end encrypted traffic.</p>
<h2 id="end-to-end-dtls-style">End-to-end, DTLS-style</h2>
<p>Routing headers have to be readable by intermediate nodes (otherwise the Sink can&rsquo;t route anything), but the payload shouldn&rsquo;t be. So we added a second layer: the Node and Sink do their own mini-handshake (ClientHello / ServerHello with nonces), derive a <strong>separate end-to-end key</strong> via HKDF, and encrypt the payload with <strong>AES-256-GCM</strong> before handing it off. Intermediate hops see the headers, forward the encrypted blob, and that&rsquo;s the extent of what they can read.</p>
<p><img alt="Message layout: seq + HMAC (hop-by-hop), routing headers (plain), AES-GCM payload (E2E)" loading="lazy" src="/i1/ble_message_layout.svg">
<em>Outer layer is re-HMACed at every hop; the inner AES-GCM payload is only readable by the Node and the Sink.</em></p>
<h2 id="heartbeats-and-recovery">Heartbeats and recovery</h2>
<p>The Sink signs a heartbeat every 5 seconds with an incrementing counter. Nodes verify the signature, update their state, and forward it down to their children. If a node misses 3 heartbeats in a row, it assumes its uplink is dead, sets its own hop count to <code>-1</code>, broadcasts that to its children, who then recursively disconnect their own subtrees and go back into scanning mode to find a new parent. That cascading disconnect was fiddly to get right but is probably the most satisfying piece to watch in the GUI.</p>
<p><img alt="Cascading disconnect: 3 missed heartbeats trigger a hop=-1 broadcast that propagates down the subtree" loading="lazy" src="/i1/cascading_disconnect.png">
<em>A single broken uplink takes the whole subtree back to scanning mode, layer by layer.</em></p>
<h1 id="what-i-took-from-it">What I took from it</h1>
<p>A few things. Writing <strong>crypto glue code</strong> (as opposed to crypto primitives, which the <code>cryptography</code> library handles for you) is where the subtle bugs hide: off-by-one on the sequence counter, forgetting to HKDF the shared secret, passing the wrong nonce into GCM, stuff like that. Second, designing a protocol that works across unreliable wireless links forces you to take state recovery seriously in a way purely wired setups don&rsquo;t.</p>
<p>And third, honestly the single biggest challenge of the whole project was just working with the <strong>Bluetooth stack on Linux</strong>, because BLE and BlueZ have their fair share of quirks that you only really find out about by running into them. The D-Bus API surface changes subtly between BlueZ versions, GATT MTU negotiation doesn&rsquo;t always give you the size you asked for (so big writes get silently fragmented or rejected, don&rsquo;t even ask about the headache this was), pairing state sometimes lingers across reboots in ways that make you question reality, and switching roles between central and peripheral on the same adapter can leave <code>hci0</code> in weird states that only <code>hciconfig reset</code> fixes. The error messages are also pretty cryptic, so you end up spending a lot of time reading <code>dbus-monitor</code> output and cross-referencing source comments in BlueZ itself to figure out what&rsquo;s actually going on</p>
<p>If you want to poke at the code, it&rsquo;s on GitHub at <a href="https://github.com/tfdmendes/SIC-final-project">tfdmendes/SIC-final-project</a>. Happy to answer questions if anyone else is running into the same BLE-on-Linux potholes 😵‍💫</p>
<p>Anyway, turns out teaching a bunch of BLE nodes to trust each other is mostly an exercise in not getting state wrong :)</p>
]]></content:encoded>
    </item>
    <item>
      <title>The CrowdStrike Lesson</title>
      <link>https://tiago.mendes.im/posts/the-crowdstrike-lesson/</link>
      <pubDate>Sun, 15 Dec 2024 10:00:00 +0000</pubDate>
      <guid>https://tiago.mendes.im/posts/the-crowdstrike-lesson/</guid>
      <description>One vendor, one bad file, 8.5 million blue screens. What July 19th taught me about trusting the software that&amp;rsquo;s supposed to be protecting us.</description>
      <content:encoded><![CDATA[<p>Ok so, remember that Friday in July 2024 where half the internet basically just stopped for the day? Under the hood, CrowdStrike had pushed one broken file and taken down around 8.5 million Windows machines around the world. Around 4am UTC on the 19th they sent a routine update to their Falcon sensor (Falcon is their EDR, which is basically a souped-up antivirus that sits deep in the operating system and watches everything a computer is doing), and within minutes airlines couldn&rsquo;t fly, hospitals pushed back surgeries, 911 lines went quiet, TV channels went black. And nobody was attacking anything, the antivirus tripped over itself.</p>
<h1 id="ok-but-what-actually-broke">Ok but what actually broke</h1>
<p>Falcon ships with a kernel-mode driver, which is a fancy way of saying a piece of code that runs right next to the Windows kernel with full access to memory and hardware. EDRs need to live there because attackers often operate at that level too. That driver pulls in small configuration files called &ldquo;channel files&rdquo; constantly, little rule updates about new threats, and that morning one of them (<code>C-00000291</code>) shipped with malformed data inside. The driver tried to read a memory address it shouldn&rsquo;t have (an out-of-bounds read, in the jargon), and because kernel code runs in ring 0 (the most privileged level the CPU offers) there&rsquo;s no graceful &ldquo;oh well, that thread crashed&rdquo; fallback. When a ring-0 driver faults, the whole OS faults with it. Blue screen, reboot, load the same broken file, blue screen again.</p>
<p>Now picture that happening on thousands of machines at once inside an airport. The &ldquo;fix&rdquo; was that someone physically had to walk up to every single one, boot into safe mode, delete the file by hand, and reboot, because the driver loaded before Windows could even reach the network for a remote patch. You saw the consequences of that for days on the news: Delta staff scribbling flight info on whiteboards, gate screens stuck on blue, passengers camping out on terminal floors in Atlanta with handwritten boarding passes, radiology departments turning people away because the scanners wouldn&rsquo;t come back up.</p>
<p><img alt="Blue screens across the monitors at LaGuardia Airport during the outage" loading="lazy" src="/i1/CrowdStrike_BSOD_at_LGA.jpg">
<em>LaGuardia Airport, July 19th 2024. Photo: Smishra1, CC BY-SA 4.0 via Wikimedia Commons.</em></p>
<h1 id="the-awkward-bit">The awkward bit</h1>
<p>None of this was exotic. CrowdStrike isn&rsquo;t some sketchy vendor, they&rsquo;re one of the biggest names in the business, and Falcon is everywhere because it actually works well. The update pipeline did exactly what it was designed to do, which was take a signed, vendor-approved file and ship it to every sensor on the planet in a few minutes. Everything worked as intended, and that&rsquo;s kind of the uncomfortable part.</p>
<p>The thing that makes modern EDR useful (push fast, push everywhere, run in ring 0 because that&rsquo;s where the interesting stuff happens) is the same thing that turned one bad file into a worldwide outage. No attacker, no zero-day, nothing clever going on, just a signed update shipped at scale with nothing meaningful in the way of a circuit breaker.</p>
<h1 id="what-i-actually-took-from-it">What I actually took from it</h1>
<h2 id="your-security-tool-is-a-supply-chain-too">Your security tool is a supply chain too</h2>
<p>We spend a lot of time worrying about supply chain attacks, meaning malicious code slipped in through some dependency like a compromised npm package. We spend way less time worrying about supply chain <em>accidents</em>, where the code is fine, signed correctly, and just behaves badly anyway. From the customer&rsquo;s seat, the outcome is pretty much identical: the kernel still panics and the plane still doesn&rsquo;t move.</p>
<p>Any vendor that can auto-push code into ring 0 on every machine in your fleet is, by definition, a single point of failure for that fleet, and it&rsquo;s worth being honest about that rather than filing it under &ldquo;boring dependency.&rdquo;</p>
<h2 id="defense-in-depth-is-doing-more-lifting-than-it-can">&ldquo;Defense in depth&rdquo; is doing more lifting than it can</h2>
<p>Everyone says &ldquo;defense in depth&rdquo; like it&rsquo;s a magic phrase, but in practice most places run one EDR on every endpoint, from laptops to servers to point-of-sale machines. When that EDR breaks, there&rsquo;s no second layer underneath, the whole defensive stack goes with it. Real depth implies heterogeneity (different vendors for different tiers, or different OSes), and heterogeneity costs money and complexity that nobody volunteers for.</p>
<p>The outage didn&rsquo;t really punish companies for having bad security, it punished them for having the <em>same</em> security everywhere, which is a genuinely different problem.</p>
<h2 id="staged-rollouts-even-for-just-config">Staged rollouts, even for &ldquo;just config&rdquo;</h2>
<p>The detail from the post-mortem that really stuck with me was that the file went out to everyone at once. No ring deployment (shipping to 1% of machines first, then 10%, then the rest), no canary fleet, nothing. The reasoning was basically &ldquo;this is threat intelligence content, not code, content is low risk, threats move fast, we gotta ship fast.&rdquo; Which is also the exact reasoning that lets a broken file land on 8.5 million machines before anything can stop it.</p>
<p>The content-vs-code distinction is convenient for the vendor but doesn&rsquo;t give the customer any real safety. If a file can crash your kernel, it&rsquo;s code in every sense that matters, and it deserves the same care: canary deployments, ring rollouts, a rollback mechanism, a pause button customers can reach.</p>
<h2 id="the-recovery-plan-is-kinda-the-real-product">The recovery plan is kinda the real product</h2>
<p>Every vendor has an incident response plan. Almost nobody has one for the scenario where the vendor <em>is</em> the incident, and that&rsquo;s what separated the people who handled July 19th okay from the ones who didn&rsquo;t. The ones who did okay had thought about it ahead of time: Bitlocker recovery keys (which you need just to get into an encrypted Windows machine) stored somewhere reachable and not only on the servers that were also down; out-of-band management like IPMI or iLO to get into machines without the OS; runbooks written from the assumption that the security tool itself could be the thing breaking everything.</p>
<p>If your recovery plan quietly assumes your security vendor is healthy, it&rsquo;s less of a plan and more of an aspiration.</p>
<h1 id="what-i-keep-thinking-about">What I keep thinking about</h1>
<p>Security software earns its place by being trusted, privileged, and basically everywhere. You can&rsquo;t really design it any other way, an EDR that can&rsquo;t see inside the kernel can&rsquo;t catch an attacker who&rsquo;s operating in the kernel. But the flip side of &ldquo;trusted, privileged, everywhere&rdquo; is that one bad push from the vendor can become everybody&rsquo;s problem at the same moment.</p>
<p>CrowdStrike didn&rsquo;t teach me Falcon is bad, it isn&rsquo;t. What it reminded me of is that thinking about security software purely as a protector is only half the picture. It&rsquo;s also a dependency in the hard operational sense, sitting in the kernel with update access to thousands of machines, and a dependency that powerful needs to be treated as one, with some redundancy where you can afford it, some staged trust in the update pipeline, and an actual plan for the day something about it goes sideways.</p>
<p>One vendor shouldn&rsquo;t really be able to take down an airline, or a hospital, or someone&rsquo;s emergency line, and when it happens, honestly it&rsquo;s less about blaming CrowdStrike specifically and more about how we keep architecting systems in a way that lets one vendor have that kind of reach.</p>
<p>Anyway, maybe keep a Bitlocker key on paper somewhere :)</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
