<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Security on</title><link>/tags/security/</link><description>Recent content in Security on</description><generator>Hugo</generator><language>en</language><lastBuildDate>Sun, 18 May 2025 13:29:00 +0000</lastBuildDate><atom:link href="/tags/security/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 8: Security Fuzzing — Finding vulnerabilities before attackers do</title><link>/post/rust/rust-sec-fuzzing-security/</link><pubDate>Sun, 18 May 2025 13:29:00 +0000</pubDate><guid>/post/rust/rust-sec-fuzzing-security/</guid><description>&lt;p&gt;I found a panic in a production parser by accident last year. A user in Japan sent a request with a multi-byte UTF-8 character right at a boundary where our code was slicing a string by byte index. It worked fine for ASCII. It worked fine for most Unicode. But this particular combination of character and position triggered an index-out-of-bounds panic that crashed the request handler.&lt;/p&gt;
&lt;p&gt;I fixed the bug in ten minutes. What bothered me was that we&amp;rsquo;d had unit tests, integration tests, and even some property-based tests — and none of them caught it. The input space was too large. The edge case was too specific. A human writing test cases would never think to put a 3-byte UTF-8 character at exactly that offset.&lt;/p&gt;</description></item><item><title>Lesson 7: Sandboxing and Privilege Dropping — Least privilege</title><link>/post/rust/rust-sec-sandboxing/</link><pubDate>Thu, 15 May 2025 07:41:00 +0000</pubDate><guid>/post/rust/rust-sec-sandboxing/</guid><description>&lt;p&gt;Here&amp;rsquo;s a pattern I&amp;rsquo;ve seen too many times: a Rust web service runs as root in a Docker container because &amp;ldquo;it needs to bind port 443.&amp;rdquo; The service handles user uploads, parses JSON, processes images, and talks to a database — all with root privileges. If any part of that pipeline has a vulnerability, the attacker gets root on the container. And if the container isn&amp;rsquo;t properly isolated, they might get the host too.&lt;/p&gt;</description></item><item><title>Lesson 6: Supply Chain Security — Lockfiles, vendoring, and trust</title><link>/post/rust/rust-sec-supply-chain/</link><pubDate>Mon, 12 May 2025 10:08:00 +0000</pubDate><guid>/post/rust/rust-sec-supply-chain/</guid><description>&lt;p&gt;The xz backdoor was a wake-up call for the entire industry, but honestly, supply chain attacks had been happening for years before that — just more quietly. Typosquatting on npm, malicious PyPI packages, compromised maintainer accounts. The question isn&amp;rsquo;t whether Rust&amp;rsquo;s ecosystem is vulnerable to supply chain attacks. It is. The question is what you&amp;rsquo;re doing about it.&lt;/p&gt;
&lt;p&gt;I spent a week last year hardening our build pipeline after we realized that a &lt;code&gt;cargo build&lt;/code&gt; on our CI server was pulling fresh crate downloads from the internet with no verification beyond what Cargo does by default. If crates.io got compromised, or if our DNS got hijacked, we&amp;rsquo;d be compiling and shipping attacker code with zero friction.&lt;/p&gt;</description></item><item><title>Lesson 5: Dependency Auditing — cargo-audit and cargo-deny</title><link>/post/rust/rust-sec-dependency-audit/</link><pubDate>Fri, 09 May 2025 14:55:00 +0000</pubDate><guid>/post/rust/rust-sec-dependency-audit/</guid><description>&lt;p&gt;You know what keeps me up at night? Not my own code — I can review that. It&amp;rsquo;s the 200+ transitive dependencies in my &lt;code&gt;Cargo.lock&lt;/code&gt; that I&amp;rsquo;ve never read a single line of. Every one of those crates runs with the same permissions as my code. If any of them has a vulnerability, it&amp;rsquo;s my vulnerability.&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t theoretical. In 2024, the &lt;code&gt;xz&lt;/code&gt; backdoor showed that even core infrastructure maintained by a single person can be compromised. Rust&amp;rsquo;s ecosystem isn&amp;rsquo;t immune. We&amp;rsquo;ve had actual advisories for real crates — buffer overflows in parsing libraries, unsound &lt;code&gt;unsafe&lt;/code&gt; code in popular crates, logic bugs in crypto implementations.&lt;/p&gt;</description></item><item><title>Lesson 4: Secret Management — zeroize and secure memory</title><link>/post/rust/rust-sec-secrets/</link><pubDate>Wed, 07 May 2025 08:23:00 +0000</pubDate><guid>/post/rust/rust-sec-secrets/</guid><description>&lt;p&gt;A while back I was debugging a crash in production and pulled a core dump from the server. Sitting right there in the heap, in plain text, was a database connection string with credentials. The service had loaded the secret from Vault on startup, stored it in a regular &lt;code&gt;String&lt;/code&gt;, and that &lt;code&gt;String&lt;/code&gt; stayed in memory for the entire process lifetime. When it crashed, the secret got written to disk in the core dump.&lt;/p&gt;</description></item><item><title>Lesson 3: Cryptography — ring, RustCrypto, and sodiumoxide</title><link>/post/rust/rust-sec-crypto/</link><pubDate>Mon, 05 May 2025 11:47:00 +0000</pubDate><guid>/post/rust/rust-sec-crypto/</guid><description>&lt;p&gt;I&amp;rsquo;m going to say something controversial: most developers should never write cryptographic code. Not because they&amp;rsquo;re not smart enough — because the field is absurdly hostile to even tiny mistakes. A single branch in your constant-time comparison function leaks timing information. A reused nonce in AES-GCM completely destroys confidentiality. An ECDSA implementation with a biased random number generator leaks your private key after enough signatures.&lt;/p&gt;
&lt;p&gt;But you still need to &lt;em&gt;use&lt;/em&gt; cryptography. Every production system needs hashing, encryption, signatures, or key derivation at some point. The trick is picking the right library, using it correctly, and understanding just enough of the theory to avoid the common footguns.&lt;/p&gt;</description></item><item><title>Lesson 9: Dependency Scanning — govulncheck before you deploy</title><link>/post/go/go-sec-dependency-scanning/</link><pubDate>Mon, 05 May 2025 00:00:00 +0000</pubDate><guid>/post/go/go-sec-dependency-scanning/</guid><description>&lt;p&gt;The security posture of your Go application is not just about the code you write — it is also about the code you import. A typical Go microservice will have dozens of direct and transitive dependencies. Any one of them might have a known vulnerability in the specific version you are using. Unlike the bugs in your own code, these vulnerabilities are publicly catalogued, exploits are often published, and attackers scan for them at scale.&lt;/p&gt;</description></item><item><title>Lesson 2: Input Validation and Sanitization — Trust nothing</title><link>/post/rust/rust-sec-input-validation/</link><pubDate>Sat, 03 May 2025 16:12:00 +0000</pubDate><guid>/post/rust/rust-sec-input-validation/</guid><description>&lt;p&gt;A few months ago, I was reviewing a PR where someone had written a REST API handler that took a user-supplied filename, appended it to a base path, and opened the file. The code compiled perfectly. Clippy was happy. Tests passed. And it was a textbook path traversal vulnerability — &lt;code&gt;../../etc/passwd&lt;/code&gt; would work just fine.&lt;/p&gt;
&lt;p&gt;Rust&amp;rsquo;s type system protects you from memory corruption. It doesn&amp;rsquo;t protect you from trusting user input. That&amp;rsquo;s still on you. And honestly? It&amp;rsquo;s where most production vulnerabilities in Rust code are going to come from.&lt;/p&gt;</description></item><item><title>Lesson 1: Memory Safety — What Rust gives you for free</title><link>/post/rust/rust-sec-memory-safety/</link><pubDate>Thu, 01 May 2025 09:34:00 +0000</pubDate><guid>/post/rust/rust-sec-memory-safety/</guid><description>&lt;p&gt;Last year I inherited a C++ service that had been &amp;ldquo;battle-tested&amp;rdquo; in production for three years. Within a week of digging through crash dumps, I found two use-after-free bugs, a buffer overread that leaked heap data into API responses, and a data race in the connection pool that only triggered under load. Three years. Battle-tested. Right.&lt;/p&gt;
&lt;p&gt;That experience is what finally pushed me from &amp;ldquo;Rust is interesting&amp;rdquo; to &amp;ldquo;Rust is non-negotiable for anything touching the network.&amp;rdquo; The memory safety guarantees aren&amp;rsquo;t just a nice-to-have — they&amp;rsquo;re the single biggest security win you get by choosing Rust.&lt;/p&gt;</description></item><item><title>Lesson 8: Secure HTTP Defaults — Your production server needs these headers</title><link>/post/go/go-sec-http-defaults/</link><pubDate>Mon, 10 Mar 2025 00:00:00 +0000</pubDate><guid>/post/go/go-sec-http-defaults/</guid><description>&lt;p&gt;When I run the Go HTTP server in the standard library with no configuration, it does a lot of things right — it is fast, it handles HTTP/2, it is well-tested. But it also does a handful of things that are wrong for production: no timeouts, no response headers that protect against common browser-based attacks, and server identification information in responses. These are not bugs in the standard library; they are defaults appropriate for development that need to be changed before you deploy.&lt;/p&gt;</description></item><item><title>Lesson 7: JWT Caveats — JWTs are not sessions</title><link>/post/go/go-sec-jwt/</link><pubDate>Sat, 18 Jan 2025 00:00:00 +0000</pubDate><guid>/post/go/go-sec-jwt/</guid><description>&lt;p&gt;JWT has become the default answer to &amp;ldquo;how should I handle authentication tokens?&amp;rdquo; in the Go community. I have shipped JWTs in production and I have also shipped systems that would have been much simpler and more secure with server-side sessions. The point of this lesson is not that JWTs are bad — it is that they carry specific security risks that are easy to overlook, and they solve a specific problem (stateless authentication across services) that not every application actually has.&lt;/p&gt;</description></item><item><title>Lesson 6: Password Hashing — bcrypt or argon2, nothing else</title><link>/post/go/go-sec-password-hashing/</link><pubDate>Mon, 02 Dec 2024 00:00:00 +0000</pubDate><guid>/post/go/go-sec-password-hashing/</guid><description>&lt;p&gt;Password hashing is one of those topics where the correct answer is short and clear, the wrong answers are numerous and subtle, and developers who confidently implement one of the wrong answers often do not know they did anything wrong until a database is leaked and journalists start writing about it. I have sat in a post-mortem where the words &amp;ldquo;we were using MD5&amp;rdquo; were spoken in a conference room full of very quiet people. That was not my code, but I understood how it happened — MD5 was the &amp;ldquo;hash function&amp;rdquo; the developer knew, and they did not realize it was entirely unsuitable for passwords.&lt;/p&gt;</description></item><item><title>Lesson 5: Auth Middleware — Authentication is not authorization</title><link>/post/go/go-sec-auth-middleware/</link><pubDate>Tue, 22 Oct 2024 00:00:00 +0000</pubDate><guid>/post/go/go-sec-auth-middleware/</guid><description>&lt;p&gt;A few years ago I audited a Go API where every endpoint was protected by an authentication middleware. The middleware checked for a valid JWT, extracted the user ID, and set it in the request context. The developer was proud of it — every route was secured. The problem was that the product had a concept of &amp;ldquo;organizations&amp;rdquo; — users belonged to organizations — and the API let you fetch any organization&amp;rsquo;s data as long as you were authenticated. The authentication was solid. The authorization was completely absent.&lt;/p&gt;</description></item><item><title>Lesson 4: SSRF and Injection — The URL your user gave you might be localhost</title><link>/post/go/go-sec-ssrf-injection/</link><pubDate>Wed, 18 Sep 2024 00:00:00 +0000</pubDate><guid>/post/go/go-sec-ssrf-injection/</guid><description>&lt;p&gt;I reviewed a Go service that let users provide a &amp;ldquo;webhook URL&amp;rdquo; — we would call that URL when their account had a notification. The service fetched a preview of the URL to display in the UI. The developer who built it figured that since it only fetched the URL and never executed what was returned, it was safe. They had not considered that &lt;code&gt;http://169.254.169.254/latest/meta-data/iam/security-credentials/&lt;/code&gt; is also a URL.&lt;/p&gt;
&lt;p&gt;SSRF — Server-Side Request Forgery — is the class of vulnerability where an attacker tricks your server into making an HTTP request to a target of their choosing, typically to access internal services that are not exposed to the internet. AWS instance metadata, Redis, internal Kubernetes API servers, admin dashboards, other services in your VPC — all are reachable from a server that blindly follows user-supplied URLs.&lt;/p&gt;</description></item><item><title>Lesson 3: TLS Configuration — Your default HTTP server is unencrypted</title><link>/post/go/go-sec-tls/</link><pubDate>Mon, 12 Aug 2024 00:00:00 +0000</pubDate><guid>/post/go/go-sec-tls/</guid><description>&lt;p&gt;When I started writing Go HTTP servers, I thought TLS was someone else&amp;rsquo;s problem. A load balancer in front of my service handled HTTPS termination, so my service only ever spoke plain HTTP on the internal network. That is a common and often defensible architecture. But it means that if anyone gains access to your internal network — a compromised service, a misconfigured cloud security group, a rogue container — all traffic between your services is plaintext. I learned this lesson not from a breach but from a penetration test report that listed it as a finding with a crisp paragraph explaining exactly why it mattered.&lt;/p&gt;</description></item><item><title>Lesson 2: Secret Handling — Env vars are not a vault</title><link>/post/go/go-sec-secrets/</link><pubDate>Mon, 08 Jul 2024 00:00:00 +0000</pubDate><guid>/post/go/go-sec-secrets/</guid><description>&lt;p&gt;I once found database credentials committed directly in a Go source file — not in a toy project, in a staging environment of a real product. The developer who wrote it knew it was wrong, but they were &amp;ldquo;just testing&amp;rdquo; and forgot to revert it. That file lived in the repository for eight months before anyone noticed. By then, the credentials had been rotated, but the history was permanent.&lt;/p&gt;
&lt;p&gt;Secret handling in Go is not primarily a coding problem. It is a discipline problem. The code patterns are simple. The failures happen when you treat secrets as a detail to clean up later, or when you assume that environment variables are inherently safe because they are not in source code.&lt;/p&gt;</description></item><item><title>Lesson 1: Input Validation — Trust nothing from the wire</title><link>/post/go/go-sec-input-validation/</link><pubDate>Wed, 05 Jun 2024 00:00:00 +0000</pubDate><guid>/post/go/go-sec-input-validation/</guid><description>&lt;p&gt;The first production security incident I dealt with involved a simple integer field in a JSON body. The client sent a negative number where we expected a positive quantity. We hadn&amp;rsquo;t validated it. The result was a negative charge on a purchase — we were effectively paying customers money. That taught me more about input validation than any security course.&lt;/p&gt;
&lt;p&gt;Every byte that arrives over the wire is adversarial by default. Not because every user is malicious, but because your assumptions about the data are not enforced by anything except your own code. HTTP gives you a byte stream. JSON decoding gives you Go values. Neither of those steps checks whether the values make sense for your business logic. That job is yours.&lt;/p&gt;</description></item></channel></rss>