Wherein I write about whatever occurs to me. But mostly software development stuff.

Forwarded Header Sabotage


We all know by now that the leftmost values in the X-Forwarded-For header can be spoofed and only the rightmost IPs – added by your own reverse proxies – can be trusted. The Forwarded header (RFC 7239, 2014) has that same problem, and a new one: If the header is parsed correctly, an attacker can sabotage the whole header.

Let’s take a quick trip to understanding how that can happen and how complicated Forwarded parsing can get. (Think about how you’d parse the header as we go.)

Should you strip the IPv6 zone?

There have recently been three different (but related) contexts where I have asked or been asked that question: When a reverse proxy is adding the client IP to the X-Forwarded-For header. When the client IP is being used for rate limiting. When checking if a client IP is contained in a configured list of ranges/prefixes/CIDRs. As I understood more about zones my opinion on this changed. This is an attempt to capture my understanding and where I ended up.

A tiny flaw in Go's netip design


Update 2022-03-23: Matt Layher created a Go issue about this.

Update 2022-04-14: In response to that issue, two weeks ago a change was committed to Go that makes netip.ParsePrefix behave like net.ParseCIDR: they both return an error when a zone is present. It wasn’t released in 1.18.1, but I’m guessing it’ll be in 1.18.2. So that’s great!

Does this surprise you? (Try it in the playground.)

prefix := netip.MustParsePrefix("fe80::%zone/10")
addr := netip.MustParseAddr("fe80::1%zone")
fmt.Println(prefix.Contains(addr)) // ==> false

Go’s new-as-of-1.18 netip package is better in every way than the previous net.IP, etc., but this one design decision will probably burn someone, somewhere, sometime.

The perils of the “real” client IP


## Summary

This post ended up being incredibly long comprehensive. I’m afraid that many people won’t read enough to get everything that’s important, so here are the key points:

  • When deriving the “real client IP address” from the X-Forwarded-For header, use the rightmost IP in the list.

  • The leftmost IP in the XFF header is commonly considered to be “closest to the client” and “most real”, but it’s trivially spoofable. Don’t use it for anything even close to security-related.

  • When choosing the rightmost XFF IP, make sure to use the last instance of that header.

  • Using special “true client IPs” set by reverse proxies (like X-Real-IP, True-Client-IP, etc.) can be good, but it depends on the a) how the reverse proxy actually sets it, b) whether the reverse proxy sets it if it’s already present/spoofed, and c) how you’ve configured the reverse proxy (sometimes).

  • Any header not specifically set by your reverse proxy cannot be trusted. For example, you must not check the X-Real-IP header if you’re not behind Nginx or something else that always sets it, because you’ll be reading a spoofed value.

  • A lot of rate limiter implementations are using spoofable IPs and are vulnerable to rate limiter escape and memory exhaustion attacks.

If you use the “real client IP” anywhere in your code or infrastructure, you need to go check right now how you’re deriving it.

This is all explained in detail below, so keep reading. It’s a weird, scary, bumpy ride.

The scary state of IPv6 rate-limiting


IPv6 rate-limiting is scarily half-baked right now. If you run a server that does any kind of IP-based rate-limiting, consider not enabling IPv6 if possible. If you do use IPv6, check how your rate-limiter actually handles it.

Git Submodule vs Subtree


Every now and then I need to make a choice between using git submodules or subtrees (or nothing), or I get asked about them by coworkers. This is infrequent enough that I forget some of the details each time and need to refresh my memory. So I wrote up these notes to share with my coworkers and to help my future self. Hopefully they’re of some use to others as well.

Diving into Go's HTTP server timeouts

Recently, I was adding timeouts to a Go HTTP server and ended up exploring how the different settings and approaches act and interact. I’m going to publish my notes here, along with the code I used for testing. Hopefully this will help someone else (or myself) in the future. The timeout testing client can be found here: There is a server in the examples directory that you can make requests to.

The Ethics of Driving Speed in Travel Time Estimation

How should travel time be estimated? What are the ethical implications of the approach taken? You enter your destination into your maps app. It finds a few likely routes. It determines the distance of each pretty easily. It checks traffic conditions along the routes. But we don’t yet have a travel time estimate. Time equals distance divided by speed, adjusted for traffic. What travel speed (traffic notwithstanding) does the app use?

Timing attack mitigation must exclude network

TL;DR: When trying to prevent timing attacks (e.g., against login username enumeration) by making a request take constant time, make sure you exclude the network read and write time. If you don’t, an attacker can slow down their request to bypass it. I’ll be covering some background and contextual information here. If you don’t need it, skip to “Exclude network time from constant-time limiting”. ## What is a “timing attack”?

Make sure you have a backup Yubikey

For four years I carried a Yubikey NEO (USB Type-A) in my pocket, on my keychain. And then it died (would no longer be recognized by any computer). Yubikey’s durability claim was: Crush-resistant and waterproof, YubiKey NEO is practically indestructible during normal use, weighs only 3g, and attaches to your keychain alongside your house and car keys So, they didn’t explicitly say “carrying it on your keychain won’t kill it”, but they sure did imply it.

Dev Story: Unicode URL length limit blues

I have enjoyed reading other people’s design and debugging train-of-thought posts, so after I spent two days wrestling with a code problem, I thought I’d write it up. It’s not technically exciting, but I think that describing it might be useful to someone – or my future self – someday. Or, at the very least, a little amusing. (Bonus: While writing this I discovered an error I made while doing the actual work.

The short happy life of the Breached extension

In October 2017, Troy Hunt of Have I Been Pwned held a contest inviting people to do something cool with the HIBP API. I decided a) that I would kind of like the special edition ThinkPad he was giving away, and b) that I could probably whip something up pretty quickly. I decided to create a browser extension that would simply pull HIBP breach information and show a browser notification – with the ability to view extra info – when the user visited a site that had been breached.

Markdown Here: Splitting the Firefox and Thunderbird Extension

[This started as notes to myself to help clarify the problem and solution. It’s probably more suited to a Github issue than a blog post, and it may get copied into one.] # The story so far The Firefox and Thunderbird versions of Markdown Here both used nearly the same code – an old-style XUL extension. Tb is only capable of using a XUL extension, while Fx supports at least three extension types: XUL-based, Add-on SDK (aka Jetpack, aka jpm), and WebExtensions.

Android Non-Vulnerability: Steal a Device and Keep it Unlocked

While poking around in my Android phone’s developer options, I realized that if you steal a phone that’s currently unlocked because it’s in a “trusted place”, then you can force it to remain unlocked forever. (And then I got schooled about that not being a problem.) ## Security Feature: Smart Lock with Trusted Places Android’s Smart Lock allows users to configure conditions under which to keep the phone unlocked.

Why and How to Use a Contributor License Agreement

## Background and Motivation I received a pull request for Markdown Here that was great: it found a bug, fixed it, and included tests for the fix. However, the PR submitter didn’t write the tests using the existing framework, so I figured I’d massage his test code into the proper form. And then I noticed that he included a copyright line in the test file. It says “MIT License”, which is the license used for the rest of the project, but that got me thinking about what that might mean…

Test post: Markdown Here in Disqus

This is just a stub test post to allow me to try out Markdown Here in Disqus comments. Right now MDH won’t work with Disqus in Chrome because of cross-origin restrictions. See: Update: The Disqus edit box is contenteditable, and MDH will render in it, but all formatting seems to get stripped out when you actually post the comment. Seems like the rich-edit-ness is probably just to support Disqus’s add-an-image feature.

Safari Extensions Gallery: half-baked

Trying to get Markdown Here listed in the Safari Extensions Gallery is by far the worst browser extension “store” experience I’ve had so far. Shockingly bad. ## No hosting First of all, but least of all: There’s no hosting. Unlike the Chrome and Mozilla stores, the Safari store doesn’t host the extension for you – it’s really more of a listing of links to wherever you host your extension files.

No One Knows to Click on a Page Action

Page actions – the buttons in a browser’s address bar – are a surprising UI failure. When adding a button for a browser extension, a choice must be made whether to make it a “page action” or a “browser action” (button on the toolbar). But browsers have failed to communicate the interactiveness of page actions, and almost no one – techy or layman – realizes that they’re clickable. To complement the context menu item and hotkey, and to fulfil a user feature request, I decided to add a button to the Markdown Here browser extension.