123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- <!doctype html><!-- This is a valid HTML5 document. -->
- <!-- Screen readers, SEO, extensions and so on. -->
- <html lang="fr">
- <!-- Has to be within the first 1024 bytes, hence before the <title>
- See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
- <meta charset="utf-8">
- <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
- <!-- The viewport meta is quite crowded and we are responsible for that.
- See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
- <meta name="viewport" content="width=device-width,initial-scale=1">
- <!-- Required to make a valid HTML5 document. -->
- <title>Plaintext HTTP in a Modern World (archive) — David Larlet</title>
- <meta name="description" content="Publication mise en cache pour en conserver une trace.">
- <!-- That good ol' feed, subscribe :). -->
- <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
- <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
- <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
- <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
- <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
- <link rel="manifest" href="/static/david/icons2/site.webmanifest">
- <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
- <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
- <meta name="msapplication-TileColor" content="#f0f0ea">
- <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
- <meta name="theme-color" content="#f0f0ea">
- <!-- Documented, feel free to shoot an email. -->
- <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
- <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <script>
- function toggleTheme(themeName) {
- document.documentElement.classList.toggle(
- 'forced-dark',
- themeName === 'dark'
- )
- document.documentElement.classList.toggle(
- 'forced-light',
- themeName === 'light'
- )
- }
- const selectedTheme = localStorage.getItem('theme')
- if (selectedTheme !== 'undefined') {
- toggleTheme(selectedTheme)
- }
- </script>
-
- <meta name="robots" content="noindex, nofollow">
- <meta content="origin-when-cross-origin" name="referrer">
- <!-- Canonical URL for SEO purposes -->
- <link rel="canonical" href="https://jcs.org/2021/01/06/plaintext">
-
- <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick">
-
- <article>
- <header>
- <h1>Plaintext HTTP in a Modern World</h1>
- </header>
- <nav>
- <p class="center">
- <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
- </svg> Accueil</a> •
- <a href="https://jcs.org/2021/01/06/plaintext" title="Lien vers le contenu original">Source originale</a>
- </p>
- </nav>
- <hr>
- <p>On the modern web, everything must be encrypted.
- Unencrypted websites are treated as relics of the past with browsers declaring
- them toxic waste not to be touched (or
- <a href="//blog.mozilla.org/security/2020/11/17/firefox-83-introduces-https-only-mode/">even looked at</a>)
- and search engines de-prioritizing their content.</p>
-
- <p>While this push for security is good for protecting modern communication, there
- is a whole web full of information and services that don’t <em>need</em> to be secured
- and those trying to access them from older vintage computers or even through
- modern embedded devices are increasingly being left behind.</p>
-
- <p class="alert">Note: This article is mostly directed at those serving personal websites, like
- this one, with no expectation of privacy or security by most readers of the
- content.
- If you are running a commercial website, collecting personal information from
- users, or transmitting sensitive data that users would expect to be done
- privately, disregard everything here and don’t bother offering your website over
- plaintext.</p>
-
- <h2 id="http-upgrading">HTTP Upgrading</h2>
-
- <p>Though it’s less common these days, users may still type in your website URL
- manually as opposed to clicking on a link that already includes the <code class="language-plaintext highlighter-rouge">https</code>
- scheme.
- (Imagine a user hearing your website mentioned on a podcast and they have to
- type it into their browser.)</p>
-
- <p>For a URL entered with an <code class="language-plaintext highlighter-rouge">http</code> scheme or, more commonly, no scheme specified,
- unless your domain is listed in the
- <a href="//www.chromium.org/hsts"><abbr title="Strict Transport Security">STS</abbr> preload list</a>
- of the user’s browser or they are using a plugin like
- <a href="//www.eff.org/https-everywhere">HTTPS Everywhere</a>, the browser will
- default to loading your website over plaintext HTTP.
- For this reason, even if your website is only served over HTTPS, it’s still
- necessary to configure your server to respond to plaintext HTTP requests with a
- 301 or 302 redirect to the HTTPS version of the URL.</p>
-
- <p>If your server is properly configured to send a <code class="language-plaintext highlighter-rouge">Strict-Transport-Security</code>
- header, once the user’s browser loads your website’s HTTPS version, the browser
- will cache that information for days or months and future attempts to load your
- site will default to the HTTPS scheme instead of HTTP even if the user manually
- types in an <code class="language-plaintext highlighter-rouge">http://</code> URL.</p>
-
- <h2 id="avoid-forced-upgrading-by-default">Avoid Forced Upgrading by Default</h2>
-
- <p>This forced redirection is a major cause of websites becoming inaccessible on
- vintage computers.
- Your server responds to the HTTP request with a 301 or 302 status and no
- content, and either a) the browser follows the redirection and tries to
- negotiate an SSL connection, but your server doesn’t offer legacy SSL versions
- or old ciphers so the negotiation fails, or b) the browser just doesn’t support
- SSL/TLS at all and fails to follow the redirection.</p>
-
- <p>A real-life example of this is that I recently purchased a Powerbook G4 and
- updated it to MacOS X 10.5.8 from 2009.
- It has a 1.5Ghz processor and 1.25Gb of RAM, and can connect to my modern WiFi
- network and use most of my USB peripherals.
- It includes a Mail client that can talk to my IMAP and SMTP servers, and a
- Safari web browser which can render fairly modern CSS layouts.
- However, it’s unable to view any content at all on Wikipedia simply because it
- can’t negotiate TLS 1.2 with the ciphers Wikipedia requires.
- Why is a decade-old computer too old to view encyclopedia articles?</p>
-
- <p>A solution to this problem is for websites to continue offering their full
- content over plaintext HTTP in addition to HTTPS.
- If you’re using Nginx, instead of creating two <code class="language-plaintext highlighter-rouge">server</code> blocks with the <code class="language-plaintext highlighter-rouge">listen
- *:80</code> version redirecting to the <code class="language-plaintext highlighter-rouge">listen *:443 ssl</code> version, use a single
- <code class="language-plaintext highlighter-rouge">server</code> block with multiple <code class="language-plaintext highlighter-rouge">listen</code> lines, like so:</p>
-
- <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
- server_name jcs.org;
- listen *:80;
- listen *:443 ssl http2;
-
- ssl_certificate ...;
- ssl_certificate_key ...;
- ...
- ssl_protocols TLSv1.2;
- }
- </code></pre></div></div>
-
- <p>While it may seem counter to the point of this article, I recommend <strong>not</strong>
- serving legacy SSL/TLS ciphers like SSLv3 to try to help older browsers.
- These old protocols and ciphers are insecure and broken, and I feel it’s better
- to make it clear to the user they’re connecting to a website in cleartext than
- to offer a false sense of security by having the browser indicate a “secure
- connection” when it’s being done over an old, broken protocol.
- Also, while it may not be practical anymore, modern browsers might be
- <a href="//en.wikipedia.org/wiki/Downgrade_attack">tricked into negotiating</a>
- an old, broken cipher if your server still offers it.</p>
-
- <p>Even if you do offer legacy protocols and ciphers to older browsers, your TLS
- certificate might be signed by a certificate authority whose root certificate is
- not trusted by older browsers.</p>
-
- <h2 id="continue-upgrading-modern-browsers">Continue Upgrading Modern Browsers</h2>
-
- <p>Now that your entire website is being offered to legacy browsers over HTTP,
- modern browsers can still be directed to connect over HTTPS for added privacy by
- responding to the
- <a href="//developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Upgrade-Insecure-Requests"><code class="language-plaintext highlighter-rouge">Upgrade-Insecure-Requests</code></a>
- header.
- This header is only sent by modern browsers that support
- <a href="//developer.mozilla.org/en-US/docs/Web/HTTP/CSP">CSP</a>
- when making an HTTP request, so it’s a reasonable indicator that the client is
- sufficiently modern and robust that it will be able to negotiate a TLS 1.2
- connection if redirected to your site’s HTTPS version.</p>
-
- <p>For Nginx, this can be done inside a <code class="language-plaintext highlighter-rouge">server</code> block by defining a variable
- per-request that includes whether it was made over plaintext HTTP and whether it
- included an <code class="language-plaintext highlighter-rouge">Upgrade-Insecure-Requests: 1</code> header:</p>
-
- <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
- ...
- set $need_http_upgrade "$https$http_upgrade_insecure_requests";
- location / {
- if ($need_http_upgrade = "1") {
- add_header Vary Upgrade-Insecure-Requests;
- return 301 https://$host$request_uri;
- }
-
- ...
- }
- }
- </code></pre></div></div>
-
- <p>This <code class="language-plaintext highlighter-rouge">location</code> block will respond for any request and, for those made over
- plaintext HTTP where <code class="language-plaintext highlighter-rouge">$https</code> will be blank and which included an
- <code class="language-plaintext highlighter-rouge">Upgrade-Insecure-Requests: 1</code> header, they will be offered a 301 redirection to
- the HTTPS version.
- The <code class="language-plaintext highlighter-rouge">Vary</code> header is sent so that any caching proxies in the middle won’t cache
- the redirect.</p>
-
- <h2 id="content-concessions">Content Concessions</h2>
-
- <p>With legacy browsers now able to access your site, you may want to make some
- changes to your HTML and CSS to allow your site to render with some degree of
- readability.
- I don’t recommend giving it the full IE6 treatment catering to the lowest common
- denominator, but at least make the main text of your site readable.</p>
-
- <p>Obviously avoid JavaScript unless it is used progressively, though many older
- browsers raise error dialogs at the mere presence of modern JavaScript that
- can’t be parsed even if it’s never executed.</p>
-
- <p>Modern CSS and complex layouts can also be a problem even for browsers just a
- few years old, so it’s probably best to use them sparingly.
- For any <code class="language-plaintext highlighter-rouge"><a></code> or <code class="language-plaintext highlighter-rouge"><img></code> tags that are local to your site, use relative links to
- avoid specifying a particular scheme.</p>
-
- <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><a href="/posts/blah">
- <img src="/images/...">
- </a>
- </code></pre></div></div>
-
- <p>If you have to specify an absolute URL to another site that is also available
- over both HTTP and HTTPS, you can specify it without a scheme or colon and the
- browser will use the same <code class="language-plaintext highlighter-rouge">http:</code> or <code class="language-plaintext highlighter-rouge">https:</code> that the document is being viewed
- over:</p>
-
- <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><a href="//other.example.com/">My other site</a>
- </code></pre></div></div>
-
- <h2 id="a-rant-about-gemini">A Rant About Gemini</h2>
-
- <p>Tangentially related,
- <a href="//gemini.circumlunar.space/">Gemini</a>
- is a modern document transfer protocol that aims to fit between the ancient
- <a href="//en.wikipedia.org/wiki/Gopher_(protocol)">Gopher</a>
- protocol and the too-modern HTTP web.
- Its document markup language is based on
- <a href="//en.wikipedia.org/wiki/Markdown">Markdown</a>
- so it’s very lightweight and simple to parse without complex HTML/CSS parsers.</p>
-
- <p>It sounds like the perfect thing to bring modern content to vintage computers,
- except that its
- <a href="//gemini.circumlunar.space/docs/specification.html">protocol</a>
- requires all content to be transferred over TLS 1.2 or higher which makes it
- nearly impossible to access from a vintage computer or even a modern embedded
- system with limited CPU power.</p>
-
- <p>This requirement seems poorly thought out, especially considering the Gemini
- protocol doesn’t even support forms (other than a single text box on a search
- form) so there’s no chance of users submitting private data, and there’s no
- mechanism for client-server sessions so clients can’t be authenticated, meaning
- everything served pretty much has to be public anyway.</p>
-
- <p>Its protocol author argues that TLS is just a simple dependency no different
- than a TCP server module, so it should be trivial to implement in any client or
- server.
- But if your computer’s CPU is so slow that a modern TLS negotiation would take
- so long as to be unusable, or its
- <a href="http://tenfourfox.blogspot.com/2018/02/the-tls-apocalypse-reaches-power-macs.html">platform doesn’t have a TLS 1.2 library</a>
- available, that makes it difficult to write a client without depending on an
- <a href="//github.com/jcs/sockhole">external system</a>
- [<a href="https://oldvcr.blogspot.com/2020/11/fun-with-crypto-ancienne-tls-for.html">2</a>].</p>
-
- <p>In my opinion, the protocol should <em>recommend</em> that servers offer both plaintext
- and TLS encrypted versions and <em>recommend</em> that clients prefer TLS, but <em>may</em>
- use plaintext if needed.
- Clients for modern operating systems can continue enforcing a TLS requirement so
- their users aren’t feeling any less secure.</p>
-
- <p>Perhaps just sending actual Markdown text over plaintext HTTP to clients that
- ask for it can be the new, old web.</p>
-
- <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Accept: text/markdown,text/plain;q=0.9,*/*;q=0.1
- </code></pre></div></div>
-
- <hr />
-
- <p><em>Please don’t contact me to “well ackchyually” me and explain
- <a href="/2011/08/17/a_man-in-the-middle_attack_in_the_wild">MITM attacks</a>
- and how your terrible ISP inserts ads into your unencrypted web pages and how
- you were able to make a Gemini client out of a whistle and some shoelaces.
- If you don’t want to make your website content available to stupid old
- computers, then don’t.</em></p>
- </article>
-
-
- <hr>
-
- <footer>
- <p>
- <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
- </svg> Accueil</a> •
- <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-rss2"></use>
- </svg> RSS</a> •
- <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-user-tie"></use>
- </svg> Pro</a> •
- <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-mail"></use>
- </svg> Email</a> •
- <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-hammer2"></use>
- </svg> Légal</abbr>
- </p>
- <template id="theme-selector">
- <form>
- <fieldset>
- <legend><svg class="icon icon-brightness-contrast">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-brightness-contrast"></use>
- </svg> Thème</legend>
- <label>
- <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
- </label>
- <label>
- <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
- </label>
- <label>
- <input type="radio" value="light" name="chosen-color-scheme"> Clair
- </label>
- </fieldset>
- </form>
- </template>
- </footer>
- <script>
- function loadThemeForm(templateName) {
- const themeSelectorTemplate = document.querySelector(templateName)
- const form = themeSelectorTemplate.content.firstElementChild
- themeSelectorTemplate.replaceWith(form)
-
- form.addEventListener('change', (e) => {
- const chosenColorScheme = e.target.value
- localStorage.setItem('theme', chosenColorScheme)
- toggleTheme(chosenColorScheme)
- })
-
- const selectedTheme = localStorage.getItem('theme')
- if (selectedTheme && selectedTheme !== 'undefined') {
- form.querySelector(`[value="${selectedTheme}"]`).checked = true
- }
- }
-
- const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
- window.addEventListener('load', () => {
- let hasDarkRules = false
- for (const styleSheet of Array.from(document.styleSheets)) {
- let mediaRules = []
- for (const cssRule of styleSheet.cssRules) {
- if (cssRule.type !== CSSRule.MEDIA_RULE) {
- continue
- }
- // WARNING: Safari does not have/supports `conditionText`.
- if (cssRule.conditionText) {
- if (cssRule.conditionText !== prefersColorSchemeDark) {
- continue
- }
- } else {
- if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
- continue
- }
- }
- mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
- }
-
- // WARNING: do not try to insert a Rule to a styleSheet you are
- // currently iterating on, otherwise the browser will be stuck
- // in a infinite loop…
- for (const mediaRule of mediaRules) {
- styleSheet.insertRule(mediaRule.cssText)
- hasDarkRules = true
- }
- }
- if (hasDarkRules) {
- loadThemeForm('#theme-selector')
- }
- })
- </script>
- </body>
- </html>
|