Locking the doors on a small read-later app

Reading Rewind visits web pages on your behalf and accepts data from a browser extension, so security work started in its first week and has come back every few months since. The two largest passes so far were the one on 10 June and a later one, where most of what follows comes from. We describe what changed at a level that helps you judge the app, and leave out the details that would help someone attack it.

Fetching pages

Every saved article means our server visits an address someone else gave it. That is the risky part of the app, and getting it right took several rounds. From the first version in April the fetcher refused private network addresses, and a review later that month made the check stricter. In June every redirect started going through the same checks as the first request. The later pass put limits on what a single fetch can download and how long it can take, and made sure a failed fetch never blocks the page you're reading.

The article text we keep is cleaned before it's stored, and has been since the first week.

Pairing links

Connecting a browser starts with a pairing link from Settings. Links expire after a short while and now work only once, and after a browser's first sync Settings shows a fresh one. You can disconnect a browser source in Settings too, which stops it syncing without deleting anything.

In April the extension also dropped a permission it didn't need and stopped talking to anything but secure addresses.

Rate limits

Most abuse is the same request sent many times over. So the places where repetition hurts have limits: signing in and the account forms, password checks, bookmark file imports, reader views and page-title fetches, and sync. We won't list the numbers here, but they sit well above what a person clicking around ever reaches.

The sync limit is tied to each connected browser, so people who share one office or campus connection don't eat into each other's allowance.

Headers and dependencies

The app sends a Content Security Policy, HSTS, a referrer policy and a permissions policy. We loaded the pages in Chromium with the policy switched on and saw zero violations. Changing your password signs out your other sessions, and new accounts confirm their email address before they get in.

Our dependency audits went from 2 open advisories to 0 for the PHP packages and from 10 to 0 for the JavaScript build tools, and we removed an HTTP library nobody was using.

Almost every commit behind this chapter added or changed tests, and the full suite passed 613 of them on the day of that pass.

The chapter after this one is about speed: How we cut a 115-second sync to 4 seconds.

More from the build log