ring-ssl icon indicating copy to clipboard operation
ring-ssl copied to clipboard

expire hsts cache & invert ssl redirect

Open ccfontes opened this issue 9 years ago • 7 comments

Say I use wrap-ssl-redirect for the case the user loads a website using http to get him redirected to the https version. I also use wrap-hsts for the second time and so on, so that when the user tries to access the website using http, it is immediately converted to https before sending the request.

Then it happens that my new ssl certificate is unsecure and browsers start complaining. Many former users are still accessing the website via https, which will take them to a security page first. To make matters worse, even if they do use http, the link will immediately be converted to https due to the hsts cache.

To solve these issues, I came up with this:


(defn- build-hsts-expire-header
  [{:keys [include-subdomains?] :or {include-subdomains? true}}]
  (str "max-age=" 0
       (if include-subdomains? "; includeSubDomains")))

(defn wrap-expire-hsts
  {:arglists '([handler] [handler options])}
  [handler & [{:as options}]]
  (fn [request]
    (-> (handler request)
        (resp/header "Strict-Transport-Security" (build-hsts-expire-header options)))))

(defn- http-url [url-string port]
  (let [url (java.net.URL. url-string)]
    (str (java.net.URL. "http" (.getHost url) (or port 80) (.getFile url)))))

(defn- get-request? [{method :request-method}]
  (or (= method :head)
      (= method :get)))

(defn wrap-insecure-redirect
  "Middleware that redirects any HTTPS request to the equivalent HTTP URL.
  Accepts the following options:
  :port - the port to use for redirects, defaults to 80."
  {:arglists '([handler] [handler options])}
  [handler & [{:keys [port]}]]
  (fn [request]
    (if (= (:scheme request) :https)
      (-> (resp/redirect (http-url (req/request-url request) port))
          (resp/status   (if (get-request? request) 301 307)))
      (handler request))))

Is there an easier way to solve this? If not, how about including this use case in ring-ssl along the lines of undoing ssl?

ccfontes avatar Nov 17 '15 20:11 ccfontes

I'm not quite sure what your code is supposed to be doing. Your wrap-expire-hsts function is the same as wrap-hsts, and I'm uncertain what wrap-unsecure-redirect has to do with your problem.

(Also, it's "insecure" rather than "unsecure" - unfortunately English is pretty inconsistent that way.)

weavejester avatar Nov 17 '15 21:11 weavejester

Thanks. I just fixed the lexical error, and added a missing helper function.

wrap-insecure-redirect serves to make a permanent redirect from https to http. The purpose is that from then on, typing site.com will get http:// appended, instead of https://.

ccfontes avatar Nov 18 '15 13:11 ccfontes

Furthermore,

If I used wrap-hsts and wrap-ssl-redirect before, and then replace that with just wrap-insecure-redirect, after the first request, typing https://site.com will be rewritten to http://site.com in Chrome, Firefox and Safari, but it has to be in same browser session. If I restart the browser session, Firefox won't rewrite https to http on first request, but Safari will (didn't test with Chrome this time). Finally, when using also wrap-expire-hsts, Firefox will rewrite https to http after the browser session is restarted, on first request. I think this is enough to prove both functions utility.

ccfontes avatar Nov 18 '15 15:11 ccfontes

I mean "first request" above as a request made after the request that was used by wrap-expire-hsts or wrap-expire-hsts the first time, independent of the browser session.

ccfontes avatar Nov 18 '15 15:11 ccfontes

I still don't see the point of wrap-expire-hsts, as it's the same as wrap-hsts but with the :max-age option set to zero:

(wrap-hsts handler {:max-age 0})

I'm also not sure what the benefit of wrap-insecure-redirect is. It seems like something you'd almost never need to do! Under what circumstances would you want to permanently redirect HTTPS to HTTP?

weavejester avatar Nov 18 '15 15:11 weavejester

Thanks, I didn't remember max-age was an option . To be clear, that code is just to give an example for the use case. In no way I mean that specific implementation should go to the repo. :)

Circumstances

I create an SSL certificate with StartSSL™.

Then there are two circumstances:

  1. The next year I need to renew it, but in the middle of the process I make a mistake and produce a broken certificate. That mistake is expensive, because I can't renew the certificate without revoking it. Revoking it comes with a fee. So then I'm stuck with a broken SSL certificate, and browsers start complaining about it to users. The browsers give alerts or take them to security pages to tell them how dangerous my website is. Users become bothered with the prompts, or just fearful, and visits to my website go down. If I just use http, then when they try to access the website, they will think it's down.
  2. The next year I think the renewal process it's too much trouble for my specific website. So I decide to go http, but now everyone is linking to my website with https links and so they will fail to access it.

It's not just about the previous users cache. Also, all google links in search results are https at the moment. And google takes it's time. Maybe some days, maybe weeks.. to update the results to http.

It's true that majors players in the browser market are making a huge effort to make https everywhere. But one thing is vision, another thing is reality and choice. We still have to wait for letsencrypt.org to become public on 3 of December, and even then, we don't know for sure what the market's response will be.

ccfontes avatar Nov 18 '15 16:11 ccfontes

Seems like the market response was very positive.

bheesham avatar May 24 '16 19:05 bheesham