Curl, unquoted URLs, and LANGSEC

April 1st, 2017

The other day I had an unpleasant realization about curl, and how I use it. I'm going to guess most programmers have had this experience:

tim@puter:~$ curl -sS
[1] 638
bash: baz: command not found
tim@puter:~$ <!doctype html>
    <title>Example Domain</title>

...and immediately have the reaction "oh dammit I forgot to quote the URL", because that innocuous little ampersand is getting interpreted in bash as "run the preceding as a command in the background".

This has happened to me from time to time for years, but it was only this week that I realized how *dangerous* it is.

Read full entry »

Which of my Firefox passwords might have been compromised by Cloudflare’s memory leak?

February 24th, 2017

Yesterday the internet learned that Cloudflare had been randomly spewing the contents of some connections through their services into other HTTP responses. What fun! Now we need to change all our passwords, rotate our keys, expire sessions, etc. because someone used C code in a sensitive context. But I have hundreds of passwords, and I don't want to change all of them. Here's how I found a set of candidates that could have been affected, using Firefox's password store.

Update 2017-02-24: Uses later date to only check sites in high-risk period.

Update 2017-02-24: Now actually checks if each identified site currently uses Cloudflare, and uses later date.

Read full entry »

Securing my Clojure photo gallery: Let’s Encrypt certs on NFSN

January 10th, 2017

I have a photo gallery site written in Clojure, hosted at NearlyFreeSpeech.NET. I just spent less than an hour learning about ACME and successfully hooked up my app with TLS! Here's what I did.

In my case, the Clojure application is packaged up as a .jar file, and is run as a daemon behind NFSN's reverse proxy, which is capable of terminating TLS. They also provide a script,, that can do the ACME challenge-response. However, it does this by dropping the response files in the Apache-served public directory, which isn't exposed in my configuration.

I chose to modify my application to proxy the acme-challenge dir. It has risks; if you screw up filesystem proxying, you risk path traversal attacks that allow an attacker to read anything on your filesystem! Anyway, it takes very little code, and it worked on the first try, so I would encourage people to use this method.

Below is the meat of the commit that implements this. It's not very much!

(ns org.timmc.pellucida.res.acme
  "Pass through ACME requests to filesystem. (ACME: Automated
Certificate Management Environment)"
   [compojure.core :refer [defroutes GET]]
   [org.timmc.pellucida.settings :as cnf]))

(defn is-token-shaped?
  "Does this string look like an ACME http-01 token? In particular,
does it *not* look like a path traversal attack?"
  [^String s]
  (boolean (re-matches #"[a-zA-Z0-9\-_=]+" s)))

(defn token-response
  "Answer with the contents of the ACME challenge file."
  ;; The ACME http-01 token value is used as the filename as well
  ;; as the value of a field within the JSON file.
  (when (is-token-shaped? magic-token)
    (let [resp-path (str (:acme-challenge-dir @cnf/config) "/" magic-token)]
      {:status 200
       :headers {"Content-Type" "text/plain"} ;; Spec: This or none
       :body ( resp-path)})))

(defroutes acme-routes
  (GET ["/.well-known/acme-challenge/:magic-token"] [magic-token :as r]
       (when (:acme-challenge-dir @cnf/config)
         (token-response magic-token))))

Incidentally, there were a few other options I thought of, but did not pursue or fully evaluate:

  • Hook: uses dehydrated to implement ACME, and you can hook into it for various events, including challenge generation. I could use that to notify the app to do something.
  • Proxy: I already configure the NFSN site to proxy the root path to my app's port 8080. Could I add another proxy that just mirrors /home/public/.well-known/acme-challenge? Not sure. From the forums, it sounds like if I change the site type to "Apache2.4 generic" instead of "Custom", I could possibly do this. Ah well, the code is already written!

Can we have online journaling while maintaining privacy?

November 27th, 2016

I've been blogging for over 11 years now, for better or worse. I cringe a bit when I look back at some of my older posts, but I'm loathe to delete them, because it's who I was then. I've also maintained a Livejournal (LJ) or Dreamwidth (DW) account for almost as long, where I've made posts of a more personal nature. How did I decide where to post, where the dividing line was? It came down to two things: 1) How personal the post was, and 2) where my intended audience was. Back in the mid-00's, many of my friends and acquaintances had public blogs, but were no on LJ. That meant that if I wanted them to know how I was doing, I had to post for the world to see, even if that meant future employers with boundary issues would see those posts out of context. As the blogosphere slowly deflated and I entered Boston-area social groups where LJ was more common, the choice became easier. Then Facebook rose to supremacy, and I chose not to jump off that particular bridge... but now it's much harder to have an online social life.

I really miss that, but Facebook is not an option. How can we return to the days of easy journaling? I'd like to lay out what I see as (a) central problem, then ask you for ideas in solving it.

Read full entry »

Buying laptops sucks, online stores make it worse

November 25th, 2016

We recently had to go shopping for a new laptop, and since Lenovo's design is going down the shitter, we had to do more research than usual.

Read full entry »