A remarkable experience with a HTTP Error 406 and some security measures of a web-hosting-provider

It is very seldom that you are confronted with a HTTP status message of type 406 "Not acceptable". However, this happened yesterday to a customer who uses a renowned hosting provider (in Norway) to publish his web-sites. The customer uses his own WordPress installation on hosted web-servers. His favorite browser is Firefox on a Win 10 desktop system. A week ago he could work without any restrictions. Then suddenly everything changed.

Access to website and WP admin interface broken due to security measures of the provider

At some point in time during last week the hosting-provider changed his security policies on his (Norwegian) Apache servers. The provider seems to have at least changed settings of the "mod_security" module - and thereby started to eliminate old browsers by some rules. (Maybe they even introduced the use of the mod_security module for the first time ?). To implement mod-security with a reasonable set of rules basically is a good measure.

However, the effect was that our customer got a 406 error whenever he tried to access his web-site with his Firefox browser. The "406 Not Acceptable" message indicates that a web server cannot or will not (due to some rules) satisfy some conditions in the HTTP GET- or POST-request. Our customer uses the latest version of Firefox. He tested whether he got something similar on a test installation of one of our hosted servers in Germany. Of course not.

A subsequent complaint of our customer was answered by his provider; the answer in a direct translation says:

Contact the Firefox technicians or use Chrome!

Very funny! Our customer asked us for help. We tested the web-servers response with multiple browsers from Linux and Windows desktops. The problem seemed to exist only for Firefox and only on desktop systems. This already indicated a strange server reaction to the HTTP "User-Agent" string.

But this was only part of the strange experience our customer got due to new security measures. In addition his provider enforced the usage of an Apache htaccess password (Basic HTTP user authentication) for all users who maintained their own WordPress installation on the hoster's web-servers. Our customer suddenly needed to provide a UserId and a password to get access to his WordPress installation's "wp-admin"-directory. We found out about this intentionally imposed restriction by having a look at the public web site of the provider. There, in a side column, we found a message regarding the new restriction. Customers were asked there to contact the hoster's specialists for required credentials. Our customer had not been directly informed by the provider about this new policy. So, we just sent the provider a mail and asked him to give us the authentication data to the admin folder of our customer's WP-installation. We got it one day later via email.

In my opinion these procedures indicate some mess we are facing with improperly handled IT-security activities these days.

Some comments regarding enforced HTTP Basic Authentication for WP's admin directory

Comment 1: It is, of course, OK to enforce a HTTP password access to directories of a web server. But this is only an effective protection measure if the provider at the same time enforces general TLS/SSL encryption for the access to the hosted web-sites. Otherwise the password would be sent in clear text over the Internet. However, you can still work with a WordPress installation or other CMS-installations on the provider's web-servers without any SSL certificate. Our customer has a SSL-certificate - but he had to pay for it. Here business interests of the provider obviously collide with real security procedures.

Comment 2: Personally, I regard it as a major mistake to set a common UserID and a fixed permanent password for customers and send these credentials to a web-admin via an unencrypted email. Ironically enough the provider asked the receiver in the mail to take note of the password and then to destroy the mail. So, mails on the customers mail system are dangerous, but the transfer of an unencrypted mail over at least partially unencrypted Internet lines is not?

Hey, we are not talking about a one time password here - but permanent credentials set and enforced by the provider. The CPanel admin tool offered by the hosting provider does NOT allow for the change of the fixed htaccess password set by the provider's admins.

Furthermore, why announce this policy on a public website and not inform the customers via a secure channel? Next question: How did they know that we were authorized to request the access data without contacting our customer first ???

The mess with the User-Agent string

Also interesting was the analysis of the Firefox problem. We can demonstrate the effect on the provider's own website. Here is what you presently (18.10.2019) get when opening the homepage of the provider with Firefox from a Linux desktop:

And here is what you get when you manipulate the User-Agent string a bit:

The blue rectangles have been added not to directly show the provider's name. Note the 406 error message in the FF developer tool at the bottom!

Well, well ... Our customer got the following when opening his own web-page:

Some analysis showed that we get a correct display of the web-site on the same browser if we manipulated the HTTP User-Agent-string for Firefox a bit. One way to do this is offered by the web developer tools of Firefox. However, there are also good plugins to fake the User-Agent string.

The next question was: What part in the User-Agent-string reacted the provider's Apache servers allergic to?

The standard User-Agent-string of Firefox in a HTTP-GET- or POST-request is defined to have the following structure:

Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion

This can be learned from related explanations of mozilla.org:
Firefox User Agent string

"geckotrail" can be an indication of a version or a date. However - quotation:

On Desktop, geckotrail is the fixed string "20100101"

And when we check the User-Agent-string for Firefox on e.g. a Linux desktop we indeed get:

Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20190110 Firefox/68.0

Both were accepted by the provider's servers with a HTTP status code of 200 - and a complete correct web-page display.

To enable the customer to work with his FF until the provider corrects his server settings we recommended to install a plugin which allows for a manipulation of the User-Agent string. We in addition informed the provider about our findings.


What a mess a provider can produce with improper security measures! The only conclusion I get out of all this is: Security awareness is good. Education of the administrators is even more important. In Norway and everywhere else ...


Firefox unter Linux – Cache reaktivieren nach Update auf Quantum

In bestimmten Testphasen von Weblösungen stelle ich mit Hilfe von Add-Ons oder auch über die in FF integrierten Developer-Tools öfter den Cache meines Lieblingsbrowsers Browsers ab. Im alten Firefox (etwa der unter Opensuse Leap 42.3 ausgelieferten FF-Version 52.8 ESR) gab es im Add-On "Web Developer" lange Zeit eine Möglichkeit zum dauerhaften Ab- und Anstellen des FF-Caches. Diese Möglichkeit bietet selbiges Add-On unter Quantum nicht mehr.

Bei einem Update auf Firefox Quantum unter Linux hatte sich die vorher gewählte Einstellung zur Deaktivierung des Caches im alten Firefox (Vers. 52.8, ESR) aber irgendwie verselbständigt:

Nach dem Update auf Quantum wurden keine Dateien mehr aus dem Cache gezogen - die Netzanalyse in den FF-eigenen Entwicklertools zeigte ein permanentes Nachladen aller Seitenelemente an. Das führt beim Wechseln zwischen den Seiten einer Web-Site, bei denen größere Hintergrundsbilder angezeigt werden, typischerweise zu spürbaren Verzögerungen und ggf. zu Flacker-Effekten. An diesen Effekten haben wir gemerkt, dass etwas nicht mehr stimmte.

Eigentlich wäre das auch OK - schließlich hatte ich die Einstellung ja selbst gewählt.
Irritierend ist aber, dass man in keinem Entwicklertool irgendwo eine Information zum dauerhaft deaktivierten Cache sehen würde. Mit den in FF integrierten Entwickler-Tools (unter dem Menü "Extras >> Web-Entwickler ..." findet man etwa im Bereich der "Netzwerkanalyse" eine Möglichkeit, den Cache temporär ab- und wieder anzuschalten. Eine Abschaltung für den gesamten Zeitraum, in dem man die Entwickler-Toolpalette nutzt, bietet auch der dortige Punkt "Werkzeugkasten-Einstellungen". Diese Einstellungen sind aber sitespezifisch wie temporär und verschwinden spätestens mit dem Schließen des FF-Developer-Werkzeugkastens.

Leider zeigten die genannten Werkzeuge nach dem Update auf Quantum nicht an, dass der Cache deaktiviert war. War er aber. Wegen der Einstellmöglichkeiten und der zuletzt getroffenen Wahl unter der alten FF-Version.

Die Behebung dieses Problems ist einfach, wenn man weiß, wo man zu suchen hat:

Man öffne die URL "about:config". Dort gibt man dann im Suchfeld den String "browser.cache.*.enable" ein. In meinem Fall ergab die Suche 5 verschiedene Einstell-Optionen:

browser.cache.disk.enable - browser.cache.disk.smart_size.enabled - browser.cache.memory.enable - browser.cache.offline.enable - browser.cache.offline.insecure.enable

Bei mir waren die Werte für "browser.cache.disk.enable" und "browser.cache.memory.enable" leider auf "false" gesetzt. Man stelle diese Werte auf "true" um - und schon funktioniert der FF-Cache wieder!

Für das temporäre Abschalten nutzt man die Optionen im FF-eigenen Entwickler-Werkzeugkasten. Mir reicht diese Möglichkeit zum temporären Abschalten in der Regel für das Testen von Webseiten.

Leider gibt es hier unter Quantum wohl noch Bugs: Die Kuchendiagramme des Punktes "Netzwerkanalyse starten" weisen bei vielen Seiten aus meiner Sicht fehlerhafterweise nicht aus, was unter Nutzung des Caches passieren würde. Angezeigt wird dort für bestimmte Sites "Antworten aus dem Cache:0". Obwohl die reguläre Darstellung der Downloadzeiten für die Seitenelemente mittels "Balkendiagrammen" die Nutzung des Caches mit korrekten Werten ausweist.

Dennoch viel Spaß weiterhin mit FF.

Apache Rewrite für zwei Domänen, die gemeinsame Datei-Ressourcen nutzen

Gestern wurden wir mit zwei Websites konfrontiert, die wir um einen Blog ergänzen sollten. Die Webserver-Installation, die wir vorfanden, war interessant und hat uns ein wenig beschäftigt:

Zwei Domain-Namen (alpha.de und beta.de) waren beim Hosting-Provider mit ein und demselben Webserver-Verzeichnis verbunden. Dieses wiederum wies zwei Unterverzeichnisse auf, in denen sich vor allem HTML-Seiten zu den verschiedenen Domänen befanden. Die Idee hinter diesem Setup war wohl, von den Webseiten beider Domänen aus gemeinsame Datei-Ressourcen im Hauptverzeichnis zu nutzen.

Setup zur Nutzung gemeinsamer Datei-Ressourcen durch zwei Web-Domänen auf demselben Webserver

Nennen wir das Hauptverzeichnis mal dir_domains und die Subverzeichnisse dir_alpha und dir_beta.

Die Webseiten von alpha.de und beta.de nutzen gleiche Datei-Ressourcen (CSS-Dateien, Javascripts, PHP-Programme, ...). Entsprechende Verzeichnisse fanden sich unter dir_domains:

dir_domains (Webserververzeichnis für Domains alpha/beta)
| ....
|        |__index.html
|        |...(HTML-Dateien für die Domäne beta.de)
|        | ....
         |... (HTML-Dateien für die Domäne beta.de)

Die Datei index.php übernahm die Zugangssteuerung für die Startseiten: Je nach Domain-Anforderung (alpha.de oder beta.de) des Users wurde dieser auf die jeweilige Index-Seite unter dir_alpha oder dir_beta umgelenkt. So weit, so gut - oder eher so schlecht ....

Nach Inaugenscheinnahme der beiden Domänen im Internet war mir klar, warum das so aufgesetzt worden war:

Die Webseiten unter alpha.de bzw. beta.de haben einen ganz ähnlichen Aufbau und nutzen gleiche PHP-Programme und Scripts. Da bei der Anforderung von Ressourcen wie CSS-/JS-/PHP-Dateien Domaingrenzen in der Verzeichnisstruktur des Web-Servers normalerweise nicht überschritten werden dürfen, waren die Web-Designer gezwungen gewesen, beiden Domänen dasselbe Hauptverzeichnis des gehosteten Webserver-Accounts zuzuweisen. Zur Kompensation musste eine Art Umlenkung auf die jeweiligen Verzeichnisstrukturen integriert werden. (Anmerkung am Rande: Die Domängrenzen, im Sinne von Verzeichnisgrenzen, gelten übrigens nicht für Include-Dateien, die in PHP-Programmen nachgeladen werden. Solche Include-Dateien kann man ruhig oberhalb des Domainverzeichnisses unterbringen!).

Was war am Setup schlecht?

Während die Nutzung gemeinsamer Ressourcen durchaus zu befürworten ist, war die "Umlenkung" durch das PHP-Skript schlecht gelöst. Sie funktionierte eigentlich nur für den Aufruf einer der beiden Domän-Namen selbst (Umlenkung auf die jeweilige index.html-Seiten). Die Navigation innerhalb der Webseiten einer Domäne war über relative Pfade gelöst; sobald ein User sich einmal innerhalb einer Domäne bewegte, funktionierte deshalb alles aus Nutzersicht alles bestens.

Aber: Der Aufruf einer spezifischen Seite einer Domäne über eine direkte Eingabe in die Adresszeile des Browsers - z.B. http://alpha.de/infos/impressum.html - funktionierte mit dem vorhandenen PHP-Script nicht. Das Script index.php kümmerte sich nur um die Index-Seiten der Domänen. Es wurde als Index-Datei ja nur dann aktiv, wenn eine der Domän-Adressen alpha.de oder beta.de ohne weitere Zusätze im Browser aufgerufen wurde.

Lösungsansatz über Apache Rewrite

Bei dem gehosteten Webserver handelte es sich um einen Apache-Server. Eine saubere Lösung für den gewünschten Setup-Ansatz mit geteilten Datei-Ressourcen besteht dann natürlich darin, das Apache Rewrite-Modul (mod_rewrite) zu nutzen. Die Servereinstellungen beim Provider waren so, dass die Rewrite Engine über lokale ".htaccess"-Dateien aktiviert und gesteuert werden konnte. Wir haben dann folgenden einfachen Vorschlag umgesetzt:

Inhalt der .htaccess-Datei für das Verzeichnis dir_domains:

Options +FollowSymLinks 
RewriteEngine On 
RewriteBase /

RewriteRule ^alpha/(.*)$ - [L]
RewriteRule ^beta/(.*)$ - [L]

RewriteRule ^css/(.*)$ - [L]
RewriteRule ^images/(.*)$ - [L]
RewriteRule ^php/(.*)$ - [L]
RewriteRule ^script/(.*)$ - [L]

RewriteCond %{SERVER_NAME} alpha.de [OR]
RewriteCond %{SERVER_NAME} www.alpha.de
RewriteRule ^$ alpha/index.html [L]

RewriteCond %{SERVER_NAME} alpha.de [OR]
RewriteCond %{SERVER_NAME} www.alpha.de
RewriteRule ^(.*)$ alpha/$1 [NC,L]

RewriteCond %{SERVER_NAME} beta.de [OR]
RewriteCond %{SERVER_NAME} www.beta.de
RewriteRule ^$ beta/index.html [L]

RewriteCond %{SERVER_NAME} beta.de [OR]
RewriteCond %{SERVER_NAME} www.beta.de
RewriteRule ^(.*)$ beta/$1 [NC,L]

Die ersten 2 Rewrite-Regeln sorgen dafür, dass keine Umlenkung mehr vorgenommen wird, wenn man sich bereits im Verzeichnisbereich der Seite befindet. Dieser Vorspann ist wichtiger, als man meinen möchte: die nachfolgenden Regeln würden für sich allein zu einer unbegrenzten Iteration von Rewrites führen!

Die nächsten 4 Regeln sorgen dann dafür, dass Verweise auf die gemeinsam genutzten Ressourcen-Verzeichnisse und zugehörige GET-Anforderungen an den Server unangetastet bleiben. Diese Verzeichnisse werden ja aus dem HTML-Code der Webseiten unter dem alpha- bzw. dem beta-Sub-Verzeichnis referenziert; z.B. über relative Pfade.

Danach kommen die eigentlichen Umlenkungsregeln. Wir haben hier die Grundregel befolgt, dass eine oder mehrere Rewrite Conditions sich nur und ausschließlich auf die nächste Rewrite Rule beziehen - also auf genau eine Rewrite Rule! Das wird in der hektik oft übersehen; es gibt keine native Klammerung mehrerer Rewrite Rules zu Rewrite Conditions. Allerdings kann man mit Negationen der Bedingungen und Skip-Zusätzen hinter den Rewrite-Regeln tricksen. Um das Verständnis nicht zu erschweren, haben wir hier auf solche Hacks verzichtet.

Wird keine Dateiname angegeben - ist also der Pfad hinter dem Domain-Anteil leer - wird auf die jeweilige Index-Seite umgelenkt. Sind konkrete Webseiten einer Domäne angefordert, wird auf die gewünschte Datei im jeweiligen Unterverzeichnis verwiesen.

Das war es schon; die Datei index.php kann und sollte danach gelöscht werden. Unser Kunde kann nun 2 Domänen im gleichen Webserververzeichnis nutzen und zwischen beiden Implementierungen gemeinsame Ressourcen teilen. Eigentlich eine nette kleine Geschichte, die ich vielleicht auch mal für eigene Sites nutzen werde - Apache sei Dank!