Stranger Than Usual

The guests wanted answers. What was that noise? Had it scared the horses? Were the horses all right? Had the weather worsened? Did anyone check the horses?

— The Name of All Things

Nature cannot be fooled

Am 28. Januar 1986 zerbrach die Challenger, eines der Space Shuttles 73 Sekunden nach dem Start. Daraufhin gab es eine große Untersuchung, wie es dazu kommen konnte. Es stellte sich heraus: Warnungen von Ingenieuren über die Kälteempfindlichkeit von Dichtungsringen wurden ignoriert, weil man den Start aus PR-Gründen nicht verschieben wollte.

Der berühmt-berüchtigte Physiker Richard Feynman, der im Untersuchungsausschuss saß, schrieb im Bericht über das Unglück:

For a successful technology, reality must take precedence over public relations, for nature cannot be fooled.

(Wer das Blog hier schon länger liest: Das war auch mal eins meiner ständig wechselnden Zitate auf der Startseite).

Warum erzähle ich das gerade? Weil das gleiche Prinzip auch für den Klimawandel gilt. Wenn wir nicht jetzt etwas gegen den Klimawandel tun, egal wie teuer das wird, dann werden wir das, was wir jetzt einsparen in den kommenden Jahrzehnten um ein Vielfaches extra ausgeben. Da hilft keine PR und kein Kleinreden. Es. Wird. Passieren. Und es kommt mir so vor, als ob große Teile der deutschen Politik das nicht verstanden haben.

Und damit meine ich nicht die AfD, bei der ist sowieso alles verloren. Die leugnen den Klimawandel insgesamt, bei deren Wählern ist der Realitätsabstand in etwa so groß wie bei den Flacherdlern oder den Trump-Anhängern. Ich meine die anderen Parteien, vornehmlich die CDU/CSU und die FDP. Auch die anderen Parteien beckleckern sich hier nicht mit Ruhm, und auch bei den C-Parteien und der FDP gibt es vermutlich vereinzelt Leute, die das Problem ernst nehmen, aber im Großen und Ganzen haben wir ein Problem.

Verkehrswandel

Beim Verkehrswandel zum Beispiel: Wir wollen den Individualverkehr mit Autos verringern, weil der sehr energieineffizient ist. Das, was überbleibt, wollen wir auf Elektromotoren umstellen, weil die energieeffizienter sind und sich mit Strom aus erneuerbaren Quellen laden lassen.

Der erste Teil (Verringerung) muss duch mehrere Aspekte geschehen. Wir müssen die Alternativen zum Autofahren attraktiver machen als das Autofahren. Zwei Hauptalternativen: ÖPNV und Fahrräder. Beim ÖPNV haben wir das Deutschlandticket. Das war ein voller Erfolg. Nur soll das jetzt teurer werden. Damit wird es wieder unattraktiver. Auf der anderen Seite war es so erfolgreich, dass die CDU in Hessen es am liebsten ganz abschaffen wollte und ein paar Bundesländer jetzt den ÖPNV-Fahrplan kürzen wollen. So wird das nichts. Die Leute werden halt wieder auf die Autos umsteigen, außer denen, die es nicht können. Klar kostet es Geld, den ÖPNV zu unterhalten. Aber es ist eine verdammte Investition, damit uns nicht DIE FUCKING WELT ABBRENNT!

Auch bei den Fahrrädern sieht es nicht gut aus. Die Niederlande zeigen, wie man gute und sichere Fahrrad- und Fußgängerinfrastruktur baut. Und ironischerweise macht das auch das Autofahren angenehmer. Win-Win. Das kommt nicht von ungefähr, die haben da Jahrzehnte dran gearbeitet. Und Erfahrungen gesammelt. Wenn wir und bei deren Erfahrungen bedienen, können wir das viel leichter machen (es wird trotzdem lange dauern, deswegen müssen wir jetzt anfangen). Was passiert stattdessen? Jedes Mal, wenn eine Fahrradstraße eingerichtet werden soll, beschweren sich die anliegenden Geschäfte, weil sie Angst haben, das ihnen die Kunden wegfallen. Und hinterher freuen sich die anliegenden Geschäfte, weil sie mehr Kunden haben als vorher. In Berlin wurden Radwegprojekte komplett gestoppt, weil die armen unprivilegierten Autofahrer ja so unter ihnen leiden müssen. Wo ich wohne, wird gerade eine große Kreuzung neu gemacht. Die Radwege? Auch wieder nur aufgemalt, teilweise zwischen den Autospuren. Sicher fühlt man sich als Radfahrer so nicht. Und wenn man sich nicht sicher fühlt, steigt man auch weniger gerne aufs Rad.

Und der Umstieg auf Elektromobilität? 2019 sagte der damalige Bundesvorsitzende der Grünen, Robert Habeck:

Wenn Sie 2025 kein E-Mobil für unter 20.000 Euro anbieten, dann werden Sie – so fürchte ich – im Markt scheitern

„Sie“, das war der VW-Chef. Jetzt haben wir 2024, und VW ist am Markt gescheitert. Die Chinesen schaffen es nämlich, günstige Elektroautos anzubieten. VW nicht. Jetzt kaufen die Chinesen keine VWs mehr, und die Deutschen kaufen chinesische Autos. Da hilft es jetzt auch nicht, VW staatslich unter die Arme zu greifen oder einen Zoll auf chinesische Autos zu setzen. Insbesondere letzteres würde für Rachezölle aus China sorgen und die VWs in China noch teurer machen. Auch hier wird an der Realität vorbeigearbeitet.

Und dann die Diskussionen über Wasserstoff und E-Fuel. Mai Thi Nguyen-Kim hat das hier mal gut erklärt: Mit einfacher Schulphysik kann man zeigen, dass Wasserstoff und insbesondere E-Fuel viel energieineffizienter sind. Und wir brauchen den Wasserstoff für andere Sachen, z.B. zur Stahlherstellung. Den in Brennstoffzellen zu verheizen wäre Verschwendung. Auch wenn wir alles auf erneuerbare Energien umgestellt haben, haben wir nur begrenzt davon. Und E-Fuels und Verbrennermotoren werden nicht signifikant effizienter werden. Das ist einfach nicht möglich. Wir haben eine halbwegs effiziente Möglichkeit, mit Strom zu fahren: Batterien und Elektromotor. Der einzige Grund, warum man E-Fuels fördern sollte ist, um den Wählern vorzugaukeln, man könne mit den Autos so weitermachen wie bisher. Das ist es wieder: PR vor Realität.

Realität muss schwerer wiegen als PR

Der Verkehr ist nur ein Beispiel. Wenn man sich in der Klimapolitik umschaut, trifft man überall auf solche PR-vor-Realität-Politik. Das funktioniert vielleicht eine Weile. Aber wenn uns das auf den Fuß fällt, dann richtig. Wie beim Challenger-Unglück: Hätte man den Start noch einmal verschoben, wäre das schlechte PR gewesen. Also hat man ihn nicht verschoben und das Leben der Astronaut_innen verspielt. Hätten wir uns vor 25 Jahren ordentlich um den Klimawandel gekümmert, müssten wir heute nicht so drastische Einschnitte machen. Wenn wir heute nicht die drastischen Einschnitte machen, müssen wir in zehn Jahren um so drastischer eingreifen. Das wäre dann noch schlechtere Publicity, also wird das auch nicht gemacht. Am Ende würden Millionen bis Milliarden Menschen daran sterben (keine Übertreibung). Alles nur, weil wir heute aus PR-Gründen keine realitätsorientierte Politik machen. Die Natur kann man nicht hereinlegen.

Sogenannte Alternativmedizin

Vor ein paar Tagen hatte ich ein Gespräch mit einer Bekannten. Da ging es u.a. um sinnvolle, evidenzbasierte Therapien. Aber, sagte die Bekannte, die nach eigenen Angaben auch nichts von Anthroposophie hält, wenn ich Heileurythmie nicht wenigstens in Betracht ziehe, würde ich mir eine Chance nehmen.

Diese Bekannte war mir früher schon einmal mit ihrer Haltung zu sogenannter Alternativmedizin wie z.B. Homöopathie aufgefallen. Zu einer Diskussion darüber war ich in diesem Moment nervlich nicht in der Lage, ich habe es aber nur teilweise geschafft, die Diskussion abzublocken. Jetzt muss ich einmal in Ruhe meine Argumente sammeln, damit ich geistig wieder mit dem Thema abschließen kann. Deswegen dieser Blogpost.

Evidenzbasierte Medizin

Die heutige Medizin arbeitet evidenzbasiert. Das bedeutet erst einmal: Wenn ein Medikament, eine Behandlung, ein Impfstoff oder was auch immer eingesetzt werden soll, muss vorher gezeigt werden, dass es auch wirklich funktioniert. Die Idee dahinter ist ganz einfach: Wenn es eine Wirkung hat, kann man sie irgendwie nachweisen. Wenn es keine Möglichkeit gibt, eine Wirkung nachzuweisen, dann muss man davon ausgehen, dass es keine Wirkung gibt. So wie das unsichtbare rosafarbene Einhorn oder Russels Teekanne. Wenn Menschen durch etwas geheilt werden können, kann man nachweisen, dass Menschen geheilt wurden.

In der Praxis ist so ein Nachweis natürlich schwieriger. Zum einen gibt es verschiedene Möglichkeiten, warum jemand sich von einer Krankheit erholt. Es kann natürlich das Medikament sein. Es kann auch sein, dass sich der Körper von selber erholt. Selbst bei schweren Krebserkrankungen gibt es vereinzelt Fälle, in denen die Erkrankung einfach verschwindet (nennt sich Spontanremission). Deswegen braucht man viele, viele Fälle, in denen man das Medikament testet, um eine Wirksamkeit statistisch festzustellen.

Wie macht man so einen Test? Grundsätzlich würde man in der Wissenschaft zwei Testgruppen nehmen, eine ohne Behandlung und eine mit Behandlung. Abgesehen von ethischen Problemen gibt es hier aber ein sehr praktisches Problem: den Placeboeffekt: Allein die Tatsache, dass jemand so tut als ob er jemanden behandle kann dazu führen, dass sich die behandelte Person danach besser führt. Also muss man die zu testende Methode in der Medizin nicht gegen die Abwesenheit einer Behandlung, sondern gegen eine Placebobehandlung durchführen, ohne den Patienten zu sagen, was sie gerade bekommen (Blindstudie). Nur, wenn die Methode über den Placeboeffekt hinaus wirkt, kann man behaupten, dass die Methode wirklich wirksamm ist.

Die Probleme hören da nicht auf. Schließlich sind Wissenschaftler_innen und Ärzt_innen auch nur Menschen, und Menschen sind sehr gut darin, sich selbst hereinzulegen. Also macht man üblicherweise Doppelblindstudien, also weder Patient noch Arzt wissen, ob gerade ein echtes Medikament verabreicht wird. So werden die Beobachtungen der Ärztin nicht von eventueller Voreingenommenheit für oder gegen die Behandlung beeinflusst.

Das ist erst der Anfang, und in der Praxis ist das alles noch komplizierter. Wenn man in der Bewertung einer wissenschaftlichen Studie von „methodischen Mängeln“ liest, dann sind das oft irgendwelche Effekte, die dafür sorgen, dass die Vorgeingenommenheit der Wissenschaftler_innen (unbewusst) in das Ergebnis eingeflossen ist.

Der Begriff „Schulmedizin“

Evidenzbasierte Medizin wird oft als „Schulmedizin“ bezeichnet. Der Begriff tauchte in der deutschen Sprache laut Wikipedia zuerst im späten 19. Jahrhundert auf und wurde von Homöopathen als abwertender Begriff für naturwissenschaftlich- evidenzbasierte Medizin verwendet. Die Nazis griffen den Begriff später auf um ihn, wie die Nazis es gerne mal taten, in einem antisemitischen Kontext zu verwenden. Ihrer Ansicht nach war die Medizin „verjudet“ und solle durch „deutsche Medizin“ oder dergleichen ersetzt werden.

Bis heute wird der Begriff Schulmedizin hauptsächlich von den Anhängern sogenannter Alternativmedizin verwendet, um sich von ihr abzugrenzen.

Alternativen?

Der evidenzbasierten Medizin stehen unzählige Verfahren entgegen, die oft unter dem Begriff „Alternativmedizin“ zusammengefasst werden. Oft wird hier suggeriert, dass diese alternativen Verfahren viel sanfter und sicherer seien als die „Schulmedizin“, welche von „Big Pharma“ kontrolliert werde. Lustigerweise kann man das Wort „alternativ“ hier auch wirklich negativ interpretieren: Wenn der Kern der evidenzbasierten Medizin, die, nun ja, Evidenz, der Nachweis der Wirksamkeit ist, dann ist der Kern von Alternativmedizin, dass sie keinen Wirkungsnachweis hat.

Wisst ihr, wie man Alternativmedizin nennt, deren Wirksamkeit bewiesen wurde? Man nennt die Medizin.

Ich lesen dann oft jede Menge Ausglüchte, warum die Wirksamkeit nicht nachgewiesen werden kann, da wird dann von Erfahrungswerten gesprochen, ein beliebter Satz ist: „Bei mir hat es aber funktioniert!“. Auf solche Argumente gehe ich später noch einmal ein. Schauen wir und erst einmal ein paar gängige alternativmedizinische Lehren an.

Traditionelle chinesische Medizin

Traditionelle chinesische Medizin (TCM) ist ein Mischmasch aus verschiedenen historischen Behandlungsformen aus China (China ist groß und hat eine lange Geschichte). Unter Mao Zedong wurde sie dann ganz groß herausgebracht, irgendwo [citation needed] habe ich auch mal gelesen, dass das daran lag, weil es zu wenige echte Ärzte gab, um die Bevölkerung zu versorgen.

Die meisten Behandlungsmethoden davon haben keinen oder nur unzureichenden Wirksamkeitsnachweis. Es gibt das eine oder andere wirksame Mittel, das wissenschaftlich untersucht und deren aktive Bestandteile extrahiert werden konnten. Das ist jedoch kein Erfolg für die TCM, sondern für die evidenzbasierte Medizin.

Akkupunktur

Die Akkupunktur basiert, wie andere Teile der TCM auch, auf dem Konzept des Qi. Was das genau ist, ist recht kompliziert, aber die Akkupunktur basiert wohl darauf, mit Nadeln, die an bestimmten Stellen in die Haut gestochen werden, das Qi eines Menschen zu beeinflussen.

Lustigerweise konnten Studien keinen Unterschied zwischen den offiziellen Einstichpunkten und zufällig ausgewählten Einstichpunkten feststellen. Wenn ich es richtig verstanden habe gibt es aber abgesehen davon zumindest ein paar Erfolge in der Schmerztherapie. Das könnte aber auch einfach daran liegen, dass der Körper bei den Einstichen der Nadeln Endorphine zur Schmerzunterdrückung freisetzt.

Homöopathie

Die in Deutschland am weitesten verbreitete Alternativmedizin ist meines Wissens die Homöopathie. Die wurde Ende des 18. Jahrhunderts von Samuel Hahnemann begründet. Evidenzbasierte Medizin gab es damals praktisch nicht, und so war die Homöopathie damals tatsächlich wirksam, weil sie anstelle von Praktiken wie dem Aderlass durchgeführt wurde.

Die Homöopathie beruht auf zwei Annahmen:

  1. Eine Krankheit kann durch ein Mittel geheilt werden, das dieselben Symptome hervorruft wie die Krankheit
  2. je mehr man das Mittel verdünnt, desto stärker ist die Wirkung

Diese Kernannahmen alleine schon sollten disqualifizierend sein. Sie kommen nämlich praktisch aus dem Nichts. Heutzutage werden homöopathische Mittel oft so weit verdünnt, dass statistisch gesehen kein einziges Molekül des ursprünglichen Mittels mehr im Ergebnis sind. Da wird dann argumentiert, dass Wasser ein Gedächtnis habe und sich so an das Mittel erinnern könne. Spätestens da widerspricht das der Physik. Und auch dem sogenannten gesunden Menschenverstand (der, wie ich hinzufügen sollte, allerdings üblicherweise kein guter Ratgeber in der Wissenschaft ist und eher für alltägliche Situationen eingesetzt wrden sollte): Wieso kann sich das Wasser an das bisschen Quecksilber erinnern, hat aber den ganzen Kot vergessen, der irgendwann einmal mit ihm vermischt war?

Die Diskussion über den Wirkmechanismus geht aber eigentlich am Thema vorbei. Denn nur weil wir keine Erklärung dafür haben, wie es wirkt, heißt das ja noch nicht, dass es nicht wirkt. Und wirkt es?

Natürlich nicht. Homöopathie wirkt nicht über den Placeboeffekt hinaus.

Heileurythmie

Heileurythmie ist von Rudolf Steiner Rahmen seiner Anthroposophie erfunden worden. Steiner war Hellseher, und die Anthroposophie ist im Prinzip ein Sammelsurium seiner Lehren. Heileurythmie basiert auf dem Bild von Seele und Geist (oder Astralkörper oder so, ich stecke da nicht so tief drin) der Anthoposophie. Durch die richtigen rhytmischen Bewegungen soll das alles wieder ins Gleichgewicht gebracht werden oder so.

Studienlage ist auch hier: Kein Nachweis. Es gab Metastudien der Universität Witten-Herdecke. Das ist eine anthroposophische Uni, doch zur Ehrenrettung der beteiligten Wissenschaftler muss man sagen: Die haben sich davon nicht beeinflussen lassen, das Ergebnis war:

Indications, study designs and the usage of additional treatments within the identified studies were quite heterogeneous. Despite of this, EYT can be regarded as a potentially relevant add-on in a therapeutic concept, although its specific relevance remains to be clarified. Well performed controlled studies on this unique treatment are highly recommended.

Kurz: Spezifischen Wirksamkeitsnachweis gibt es nicht, Studienlage ist schlecht, aber vielleicht ist ja potentiell doch etwas dran.

Häufige Argumente für Alternativmedizin

Wie versprochen auch ein paar der häufigsten Argumente für Alternativmedizin, und warum sie Bullshit sind:

„Es hat keine Nebenwirkungen!“

Abgesehen davon, dass selbst Homöopathika vereinzelt Vergiftungen auslösen: Es ist natürlich leicht, keine Nebenwirkungen zu haben, wenn es auch keine Wirkung gibt. Was sich nicht auf den Körper auswirkt, kann sich auch nicht negativ auswirken. Die Nebenwirkung wäre höchstens, dass keine echte Therapie durchgeführt wird. Bei einer Erkältung: Kein Problem. Bei Krebs? Potentiell tödlich.

„Aber bei mir hat es doch funktioniert! Und bei meinem Hund!“

Entschuldigung, aber das ist kein Argument. Woher willst du wissen, dass die Krankheit nicht auch so vorbeigegangen wäre? Es kann einfach Zufall sein. Vielleicht auch der Placeboeffekt. Wir sind alle nur Menschen, und selbst solche Dinge wie Erinnerungen täuschen gerne. Vielleicht erinnerst du dich eher an die Fälle, in denen die Zuckerkugel mutmaßlich hat und vergisst die Fälle, in denen sie nichts gebracht hat? Das ist menschlich, so funktioniert unser Gehirn halt.

„Aber mein Heilpraktikant hört mir wengistens zu“

Auch kein Argument für Alternativmedizin, aber wir sollten umgekehrt unser Gesundheitssystem so anpassen, dass sich echte Ärzte auch Zeit für ihre Patienten nehmen können.

„Aber die Krankenkasse bezahlt es doch, also muss es wirksam sein“

Das kann auch politische oder Marketinggründe haben, weil z.B. Homöopathie so beliebt ist.

„Big Pharma will nur unser Geld und macht uns krank, um uns mehr Medikamente zu verkaufen“

Erst einmal ist das ein Verschwörungsmythos. Außerdem sind die Hersteller von Homöopathika auch „Big Pharma“.

Abschlussbemerkungen

In den letzten 150 Jahren hat sich die evidenzbasierte Medizin extrem weiterentwickelt. Wir können Diabetiker dauerhaft am Leben erhalten. Krebspatienten haben eine viel bessere Überlebenschance als früher. Selbst eine HIV-Infektion, die in meiner Kindheit noch als Todesurteil galt, kann mittlerweile so behandelt werden, dass AIDS fast vollständig verhindert wird. Und durch Imfpungen haben wir verdammt noch einmal die Pocken ausgerottet!

In derselben Zeit hat die Homöopathie (stellvertretend für andere Alternativmedizin, auf unterschiedlichen Zeitskalen) praktisch keine Fortschritte gemacht. Sie stagniert, das einzige, was mehr geworden ist sind die Studien, die ihre Wirksamkeit nicht beweisen können. Das, was der „Schulmedizin“ vorgeworfen wurde: Sie sei zu dogmatisch, zu unflexibel: Genau das ist die Homöopathie. Evidenzbasierte Medizin ist wissenschaftlich. Natürlich machen die Menschen da Fehler. Schlechte Studien, falsche Schlussfolgerungen, persönlicher Bias, der die Einführung einer besseren Therapie verzögert.

Aber der wissenschaftliche Hintergrund bedeutet eben, dass wir aus den Fehlern lernen können. Wenn die Wissenschaftler sagen: „Die Methode von vor 20 Jahren, die war schlecht, wir machen das jetzt anders“, dann bedeutet das nicht, dass die Wissenschaft nicht funktioniert, weil sie sich ja ständig umentscheidet. Es bedeutet im Gegenteil, dass sie funktioniert, weil sie auf der Basis neuer Evidenz ihre Meinung ändert.

PS: ich habe in diesem Artikel mit der Quellenarbeit ein bisschen geschludert, weil ich diesen Artikel endlich aus dem Kopf haben wollte. Deswegen einzelne Behauptungen von mir, die nicht mit einer Quelle versehen sind, gerne noch einmal überprüfen. Am großen Bild ändert das jedoch nichts.

Support für Drogenhändlerringe

Vorhin hat einer meiner Kollegen einen schönen Fall einer "Blind Idiot" Translation entdeckt:

Screenshot einer Website. Die Überschrift lautet: „Support für Drogenhändlerringe“. Darüber steht in einer grauen Box: „Diese Seite wurde von der Cloud Translation API übersetzt.“

Vor ein paar Jahren hat mir Google „pet peeve“ noch mit „Haustier ärgern“ übersetzt. Aber ich dachte, die wären mittlerweile weiter. Interessanterweise ist die Version bei archive.org aus dem Januar 2023 noch mit „DTO-Unterstützung“ korrekt übersetzt. Die aktuelle Version führt zum dem Screenshot oben. Ich habe die Seite auch mal archiviert, wenn das gefixt wird.

Tipp: nehmt immer die Doku in der Originalsprache, wenn möglich. Auch wenn die nicht maschinell übersetzt wurde. Es gibt im Zweifelsfall einfach mehr Literatur zu dem Thema in der Originalsprache.

Content-Security-Policy

Den Content-Security-Policy-HTTP-Header gibt es jetzt schon seit über einer Dekade. Was macht dieser Header? Er bietet eine Möglichkeit, für Betreiber von Webservern bestimmte Sicherheitsregeln einzuführen, die dann vom Browser (aka User-Agent) der Benutzer umgesetzt werden. Das ist eigentlich ganz schön, denn man muss sich als Webseitenbetreiber sowieso darauf verlassen, dass der Browser im Interesse des Benutzers arbeitet.

Die Kernfunktion der Content-Secuity-Policy (kurz „CSP“) ist hierbei zu kontrollieren, von welchen Quellen die Webseit Resourcen welcher Art beziehen darf. So kann man zum Beispiel angeben, dass CSS-Dateien nur von der Domain der Website selbst geladen werden dürfen, dass Bilder von der Website selbst oder von einem bestimmten CDN-Server kommen können und so weiter. Es gibt noch ein paar andere wichtige Funktionen, aber fokussieren wir uns erst einmal auf die Resourcen.

Exkurs: XSS-Attacken

Die gefährlichste Resourcen sind Scripte. Javascript ist eine Turing-Vollständige Progammiersprache und wenn eine Angreiferin es schafft, ein eigenes Script auf einer fremden Seite auszuführen, kann sie ziemlich viel anrichten, zum Beispiel Aktionen im Namen des Benutzers ausführen. Auch hier gibt es Sicherheitsmaßnahmen in Browsern, um den Schaden zu begrenzen, gefährlich ist es aber trotzdem. Solche Attacken nennt man Cross-Site-Scripting, oder XSS (fragt nicht, wo das X herkommt, ich habe keine Ahnung. Vermutlich ist es da, um Verwechslungen mit CSS zu verhindern).

XSS-Lücken passieren, wenn es Benutzern ermöglicht wird, eigenen Inhalt ungefiltert oder schlecht gefiltert auf einer fremden Website unterzubringen. Und dass Benutzer eigenen Inhalt auf fremden Websites unterbringen ist seit Jahrzehnten im Internet gang und gäbe. Nehmen wir als Beispiel mal einen Kommentar, den ich in einem Blog über eine Kommentarfunktion hinterlasse: Wenn die Seite meinen Kommentartext 1:1 übernimmt, und in meinem Kommentartext ist HTML-Code, dann wird dieser HTML-Code für alle gerendert, die sich diesen Kommentar durchlesen (oder auch nur die Seite laden, auf der er lesbar ist).

Wenn der HTML-Code jetzt ein Script enthält, wird auch dieser Code ausgeführt. Schon das Anzeigen beliebigen HTML-COdes in Kommentarn will man verhindern, aber ein Script ist, wie oben erklärt, eine Katastrophe. Deswegen ist es Standard, und wird auch von allen gängigen Frameworks und HTML-Templates unterstützt, die Benutzereingaben eben nicht 1:1 in die Seite zu schreiben, sondern spezielle HTML-Zeichen vorher zu escapen, also sie durch Platzhalter zu ersetzen, die dann nur für die Anzeige als die Zeichen dargestellt werden, die der Benutzer eigentlich eingegeben hat.

Wie man mit einer CSP XSS-Attacken verhindert

Escapen mag Standard sein, aber manchmal gibt es Bugs oder man stellt sich blöd an und man hat doch eine XSS-Lücke. Dafür (unter anderem) wurde der CSP-Header erfunden. Dort kann man wie gesagt angeben, woher Scripte stammen dürfen. Es gibt viele Optionen, das zu konfigurieren, doch kurz gesagt: Man gibt eine Liste von Domains an. Wenn die Scripte nicht auf dieser Liste stehen, werden sie nicht ausgeführt. Darunter fällt auch, dass sie nicht im HTML-Code direkt stehen dürfen. Will man das erlauben, muss man eine spezielle Anweisung 'unsafe-inline' hinzufügen. Will man auch erlauben, dass Scripte nach eigenem Gutdünken Text als Script ausführen sollen, muss man die Anweisung 'unsafe-eval' hinzufügen.

Die Namen dieser Spezialanweisung sollten aufhorchen lassen: dort steht ganz klar drin: Das ist nicht sicher. Wenn man diese Anweisungen in die CSP macht, hebelt man damit ihre Schutzwirkung fast komplett auf.

Meine Erfahrung mit CSPs

Ich habe vor etwa sieben Jahren in meiner damaligen Firma einen kleinen Vortrag über den CSP-Header gehalten und auch einen kleinen Webserver zum Demonstrieren geschrieben. Ich habe das damals gemacht, weil ich Aufmerksamkeit auf diesen damals noch nicht ganz alten Header lenken wollte, der eine zusätzliche Absicherung gegen verschiedene Attacken bringen kann.

Ich habe auch versucht, diesen Header in die Projekte einzubringen, in denen ich tätig war. Das war schwierig. Denn inline-Javascript wird oft verwendet. Es gibt zwar Wege, auch spezifisches Inline-Javascript zuzulassen, das ist aber bei bestehendem Code fast so aufwändig, wie das Javascript einfach in separat geladene Dateien auszulagern. Immerhin habe ich es geschafft, in einem Projekt einen CSP-Header in den Checkout einzubauen, also dort, wo ein Kauf abgeschlossen und bezahlt wird. Das ist ein sehr sicherheitskritischer Teil.

So zumindest die Theorie. In der Praxis haben wir den Header dann nie wirklich scharf schalten können, weil wir unbedingt einen Haufen Scripte von Drittparteien einbinden mussten, von irgendwelchen Sicherheitsbadges bis hin zum Google Tag Manager, in dem selber noch weitere Scripte geladen wurden.

Das wäre eigentlich auch kein Problem gewesen. Denn das sind Scripte von Drittparteien, die werden von Servern geladen die wir einfach in die Liste erlaubter Quellen angeben können. Richtig? Falsch. Denn diese Scripte wiederum fügen inline-Javascript in die Seite ein und erwarten dann, dass der ausgeführt wird. Oder wollen gleich direkt ein eval machen. Oder jemand aus der Marketingabteilung beschwert sich, dass das neue Script, das per Google-Tag-Manager eingebunden wurde, nicht läuft. Und schwupps – war die CSP wieder weg.

So ähnlich ist mir das auch in den beiden Projekten danach passiert. Zumindest das letzte davon hatte keine Entschuldigung, weil wir quasi auf der grünen Wiese angefangen haben. Wir hätten das von vornherein richtig machen können. Meine Rufe verhallten aber, weil es immer gerade etwas Wichtigeres zu tun gab.

Meine Hypothese war damals schon: IT-Sicherheit ist uns, als Gesellschaft, nicht wichtig genug.

Rein subjektiv: Wie sieht die Lage heute aus?

Dieses Blog hat eine strenge CSP. Ist aber auch unwichtig, weil das Blog hier statisch gerendert wird, ich die einzige Person bin, die Inhalt einfügt. Es gibt in der Website keine Kommentarfunktion, keine Kommentare von Lesern, die automatisch dargestellt werden. Es gibt auch keine sensiblen Informationen, die man mit boshaften Scripten heraustragen könnte oder Aktionen, die man durchführen könnte. Die CSP hier ist streng, aber überflüssig.

Meine Nextcloud-Instanz hat eine komplizierte CSP. Es werden zwar inline-Scripte erlaubt, aber es wird mithilfe von nonce-Anweisungen kontrolliert, dass nur gewollte Scripte ausgeführt werden.

Aber schauen wir uns doch mal wichtigere Seiten an. Wikipedia zum Beispiel hat keine CSP. Hmm. Aber gut, da kann sowieso jeder drin herumschreiben, was brauchen die schon an Sicherheit? Außerdem ist die Software alt. Aber sicher sind Bankenwebsites da besser aufgestellt, oder? Wie sieht es denn zum Beispiel mit der Sparkasse Essen aus?

script-src 'self' blob: https://morris-server.de:8801 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; media-src 'self' data: blob: https://api.sparkassen-mediacenter.de https://sparkassen-mediacenter.de https://cdn.sparkassen-mediacenter.de

Oh-oh. Was sehen meine müden Augen da? 'unsafe-inline' und 'unsafe-eval' unter script-src? Kein nonce, kein hash, fast kein Schutz mehr. Aber das ist ja alles Subjektiv, oder? Was sagen denn ordentlich angelegte Studien dazu?

Studien zur CSP-Nutzung

Da gibt es eine Studie aus 2020: Complex Security Policy? A Longitudinal Analysis of Deployed Content Security Policies. Da haben sie die Nutzung des CSP-Headers seit 2012 (also in etwa seit den Anfängen dieses Headers) auf 10000 populären Seiten verfolgt. Ergebnis: Die meisten Websites, wenn sie überhaupt eine CSP haben, setzen auf 'unsafe-inline' und 'unsafe-eval'. Ein paar andere Funktionen der CSP, die ich hier nicht erwähnt habe (hauptsächlich sicherzustellen, dass alle Resourcen per TLS geladen werden), werden immerhin mehr genutzt.

Die Autoren schreiben:

The insights gathered from our survey indicate that CSP has earned a bad reputation due to its complexity in content restriction, resulting in developers shying away from any part of CSP.

Ich muss hier sagen: eine gute CSP muss nicht komplex sein. Es sind die fucking-Überkomplexen Webseiten, die so viel Bullshit an Javascript laden, dass es unmöglich wird, eine effektive CSP einzusetzen.

In der Studie sind auch noch ein paar andere Studien referenziert, die mit anderen Schwerpunkten zu einem ähnlichen Ergebnis gekommen sind (mindestens eine davon hat sich über eine Milliarde Webseiten angeschaut).

Also was tun?

Glücklicherweise ist eine CSP nicht notwendig, um eine sichere Website zu haben. Es ist nicht so schwierig, keine XSS-Lücken einzubauen. Die Realität zeigt aber, dass es immer wieder passiert, und da wäre eine CSP wirklich hilfreich, um dir den Arsch zu retten.

Wo kann man Anfangen? Wie auch in der Studie erwähnt: In neuen Projekten ist das ganz einfach: Als erstes die CSP festlegen. Dann die Website entwickeln. So merkt man sofort, wenn man mit dem was man tut in Konflikt mit der CSP gerät. Meist kann man die Probleme ausräumen, ohne die CSP zu ändern. Wenn nicht, ändert man die CSP, indem man zum Beispiel eine weitere Quelle hinzufügt, oder, wenn es wirklich schlimm wird, ein nonce oder einen Hashwert (das würde ich aber nicht empfehlen, das macht alles nur komplexer).

In bestehenden Projekten ist das schwieriger. Dort gibt es mitunter Inline-Javascript auf eine riesiege Codebase verteilt. Hier hilft eigentlich nur, bei jeder Änderung, die sowieso gemacht werden muss, ein bisschen weniger inline-Javascript als vorher zurückzulassen. Vielleicht auch die CSP zuerst nur für besonders sicherheitsrelevante Bereiche setzen und nach und nach ausweiten. Nonces verwenden, um schnell größere Codeteile CSP-kompatibel zu machen. Nextcloud zum Beispiel hat das geschafft (oder ist zumindest auf dem Weg dorthin), wie ich anhand einiger geschlossener Tickets und meiner aktuellen Installation sehen konnte.

In beiden Fällen gilt: man muss denen mit dem Geld klarmachen, dass es wichtig ist, eine effektive CSP auf dem Server zu haben. Am besten mal bei denen vorbeischauen, wenn es mal wieder ein IT-Sicherheitsvorfall Schlagzeilen macht. Nach dem Motto: Das könnten wir sein.

Ebenfalls in beiden Fällen: Überprüft, ob ihr die Javascripte, die von externen Servern geladen werden, wirklich braucht. Das ist nicht nur wichtig für die CSP, es ist auch so ein Sicherheitsproblem für eure Seite. Und wenn ihr sie einbindet: lasst nicht zu, dass sie euch ein 'unsafe-inline' oder 'unsafe-eval' aufzwingen. Denkt darüber nach: selbst wenn ihr denen vertraut: Wenn sie unfähig sind, ihren Code ohne inline-JS oder eval zu schreiben, dann kann es mit ihren Fähigkeiten, sicheren Code zu schreiben auch nicht so weit her sein. Wollt ihr denen dann wirklich erlauben, Code auf eurer Seite auszuführen?

KittenMoji

Letztes Wochenende habe ich auf Mastodon einen Tweet gesehen, wo jemand KittenMoji erwähnt hat. KittenMoji ist eine Byte-zu-Text-Codierung. Ähnlich wie bei der Hexadezimalschreibweise oder Base64 können damit Binärdaten als Text encodiert werden.

KittenMoji verwendet, wie der Name schon nahelegt, Emojis zur Codierung. Alle verwendeten Emojis belegen, als UTF-8 codiert, jeweils vier Bytes, also 32 Bit. KittenMoji ist eine Base256-Codierung, also ein Byte wird auf genau ein Emoji abgebildet. Im Gegensatz also zu Base64, das jeweils 6 Bits mit 8 Bits encodiert oder Hexadezimalschreibweise, die jeweils 8 Bit mit 16 Bits encodiert, werden bei KittenMoji jeweils 8 Bits mit 32 Bits encodiert.

KittenMoji ist also extrem ineffizient und es gibt eigentlich keinen Grund, es zu verwenden. Ich habe ein Rust-Crate zur De-und Encodierung von KittenMoji geschrieben.

Das kittenmoji-crate

Irgendwie funktioniert mein Gehirn am besten wenn das, was ich programmiere, komplett unwichtig ist. Ich habe das crate dann auch auf crates.io, der offiziellen Rust-Paketplattform veröffentlicht. Einfach mal um zu schauen, wie das funktioniert. Wenn ich einen dummen Fehler mache: kein Problem, ist ja kein wichtiges Paket. Falls ich irgendwann einmal ein wichtiges Paket veröffentliche, weiß ich dann schon, wie das läuft.

Das kittenmoji-crate besteht im Prinzip aus sechs Funktionen: drei zum Encodieren, drei zum Decodieren. Jeweils eine Funktion, die einen byte-slice bzw. string-slice en- bzw. decodiert, eine Funktion, die einen byte-Iterator entgegennimmt und einen char-Iterator zurückgibt (fürs Encodieren, entsprechend umgekehrt fürs Decodieren) und jeweils eine Funktion, die einen bytestream liest und ihn entsprechend de- oder encodiert.

Letzteres wird in zwei Beispielprogrammen demonstriert, encode und decode, die jeweils von stdin lesen und nach stdout schreiben und die Daten dazwischen en- oder decodieren. Das funktioniert mit beliebig großen Dateien, der interne Buffer hat eine konstante Größe, der genutzte Arbeitsspeicher hält sich also in Grenzen und ist nicht von der Größe der Eingabedaten abhängig.

Optimierung des Encoders

Wer das Blog hier schon eine Weile verfolgt weiß, dass ich gelegentlich einfach mal ganz nutzlose Programme optimiere. Zum Beispiel das zeilenweise Einlesen einer Datei. Oder einen primitiven DGA-Detektor. Oder einen Brainfuckinterpreter. Mein erstes Veröffentlichtes Rust-Crate? Natürlich!

Ich habe mich hier auf die Stream-Varianten fokussiert, weil die mutmaßlich die größten Datenmengen verarbeiten und dementsprechend am meisten von einer Optimierung profitieren. Zur Messung habe ich wieder einmal Hyperfine verwendet. Encodiert habe ich… ein CD-Image einer Windows 98 SE (hey, ich habe Anfang des Jahrtausends Geld dafür ausgegeben, dann soll sich diese CD auch mal nützlich machen!).

Die Datei ist knapp 600MiB groß (626524160 Byte, um genau zu sein). Der Code sah zu diesem Zeitpunkt so aus:

pub fn encode_stream(mut input: impl Read, mut output: impl Write) -> io::Result<()> {
    let mut buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
    let mut n = input.read(&mut buf)?;
    let mut cbuf: [u8; 4] = [0; 4];
    while n > 0 {
        for c in encode(buf[..n].iter().copied()) {
            let s = c.encode_utf8(&mut cbuf);
            output.write_all(s.as_bytes())?;
        }
        n = input.read(&mut buf)?;
    }
    Ok(())
}

Es wird also in einer Schleife Daten gelesen, in einen Buffer auf dem Stack geschrieben und dann wird der oben erwähnte Iterator aufgerufen, der dann die Bytes mittels einer Lookup-Tabelle in Unicode-chars umwandelt, die dann als UTF-8 codiert und dann geschrieben werden. Wie schnell ist das?

$ hyperfine -w1 "cat ~/Dateien/CD-Images/Win98SE.iso | target/release/examples/encode  > /dev/null"
  Time (mean ± σ):      4.654 s ±  0.245 s    [User: 4.461 s, System: 0.462 s]
  Range (min … max):    4.359 s …  5.172 s    10 runs

Ich habe hier nach /dev/null geschrieben, weil ich die Schreibgeschwindigkeit meiner SSD jetzt nun wirklich nicht in die Messung einbeziehen wollte. Dennoch schwanken die Werte bei verschiedenen Messdurchläufen recht stark, also sind sie mit Vorsicht zu genießen. Ich habe das Experiment mehrere Male laufen lassen, die Werte schwanken immer im selben Bereich.

Schnell ist das jedenfalls nicht. Was könnte man also verbessern? Nun, zunächst ist mir aufgefallen, dass ich eine Menge kleiner Funktionsaufrufe habe. Der Iterator, das UTF-8-Codieren… alles für jedes einzelne Byte im Input. über 600 Millionen Bytes sind das dementsprechend viele Aufrufe. Kann man das vielleicht verkleinern? Aber natürlich. Erst einmal bin ich den Iterator losgeworden, und habe den Krams inline gemacht. Meine Lookup-Tabelle hat aber char-Werte zurückgegeben, den Aufruf von char::encode_utf8() hätte ich also trotzdem noch gebraucht. Also habe ich eine zweite Lookup-Tabelle erstellt, die stattdessen &str enthält, also kleine statische Strings, die praktischerweise schon UTF-8 codiert sind. Ergebnis:

  Time (mean ± σ):      2.519 s ±  0.070 s    [User: 2.317 s, System: 0.465 s]
  Range (min … max):    2.443 s …  2.682 s    10 runs

Diese Verbesserung kann sich sehen lassen. Aber es geht noch besser. Mit den &str-Werten ist es nämlich so: Das Programm muss zunächst auf das Array zugreifen, um sich an der richtigen Stelle eine Referenz auf den String zu holen, dieser Referenz folgen, dann eine Funktion aufrufen, um den String als byte-Slice zu interpretieren (zumindest dieser Funktionsaufruf sollte aber vom Compiler rausoptimiert werden) um dann die Daten zu schreiben. Zu viele Dereferenzieren. In vielen Fällen macht das nicht viel aus. Hier aber schon, weil es so oft gemacht wird. Außerdem muss bei den Slices mit viel mehr Daten gehandelt werden. Der Pointer? 8 Byte. Die Größenangabe? 8 Byte. Die Nutzdaten? 4 Byte.

Wie wäre es also, wenn ich eine Indirektion umgehe, und statische Arrays in die Lookuptabelle stecke? Der Encodierungscode sieht danach etwa so aus:

let mut buf: [u8; ENCODE_BUFFER_SIZE] = [0; ENCODE_BUFFER_SIZE];
let mut n = input.read(&mut buf)?;
while n > 0 {
    for b in &buf[..n] {
        output.write_all(&ALPHABET_BYTES[*b as usize])?;
    }
    n = input.read(&mut buf)?;
}
Ok(())

Und die Geschwindigkeit ist noch einmal deutlich verbessert worden:

  Time (mean ± σ):      1.379 s ±  0.023 s    [User: 1.191 s, System: 0.435 s]
  Range (min … max):    1.356 s …  1.420 s    10 runs

Ich könnte auch noch versuchen, die Anzahl der write()-Aufrufe zu verringern. Ich würde sowieso einen gebufferten Writer empfehlen, aber da muss ich halt trotzdem noch einen Haufen Funktionsaufrufe machen. Wenn ich die reduzieren kann, bin ich vielleicht trotz des Overheads, den ein zusätzlicher Buffer bringt, schneller:

let mut write_buf: [u8; ENCODE_BUFFER_SIZE * 4] = [0; ENCODE_BUFFER_SIZE * 4];
let mut n = input.read(&mut buf)?;
while n > 0 {
    for (i, b) in buf[..n].iter().enumerate() {
        let bytes = ALPHABET_BYTES[*b as usize];
        write_buf[i * 4] = bytes[0];
        write_buf[i * 4 + 1] = bytes[1];
        write_buf[i * 4 + 2] = bytes[2];
        write_buf[i * 4 + 3] = bytes[3];
    }
    output.write_all(&write_buf[..n * 4])?;
    n = input.read(&mut buf)?;
}

Das bringt tatsächlich noch einmal ein paar 100ms:

  Time (mean ± σ):     975.3 ms ±  67.0 ms    [User: 796.8 ms, System: 416.2 ms]
  Range (min … max):   924.5 ms … 1135.5 ms    10 runs

Geben wir uns damit zufrieden und schauen mal das Decodieren an.

Optimieren des Decoders

Das Größte Problem bei der Optimierung des Decoders ist: Im Gegensatz zum Encoder kann das Decodieren fehlschlagen. Encoding ist eindeutig, jedes Byte ist genau einem Emoji zugeordnet. Beim Decoden ist das anders. Als Input kommen vier-Byte-Häppchen in Frage, die entweder

  • ein gültiges KittenMoji-Emoji sind
  • gültiger UTF-8-Text, aber kein gültiges KittenMoji
  • kein gültiger UTF-8 Text sind.

Ich zeige den Code hier jetzt nicht direkt, der ist ein bisschen sperrig, aber man kann die alte Version hier im Git-Repo auf gitlab.com finden.

Nur eine kurze Zusammenfassung, was der Code macht:

  1. lese bytes in den Buffer
  2. versuche, die Bytes als UTF-8-String zu interpretieren
  3. wenn der String ungültig ist, weil am Ende bytes für einen vollständigen Codepoint fehlen (kann immer mal vorkommen, wenn man aus dem Stream liest): Nimm den kürzesten gültigen String und merk dir, wie viel im Buffer danach noch benötigt wird
  4. wenn der String ungültig ist, weil er wirklich ungültig ist: breche mit Fehler ab
  5. gehe durch alle Codepoints des strings
  6. Für jeden Codepoint: schlage in einer (vorher generierten) Hashmap nach, auf welches Byte er gemapped ist
  7. Wenn der Codepoint nicht gemapped ist: breche mit Fehler ab
  8. Wenn der Codepoint gemapped ist: schreibe das dazugehörige Byte in den Output
  9. Dann kümmere dich darum, dass die ggf. überstehenden Bytes am Ende an den Anfang des Buffers verschoben werden und lies die nächsten Bytes in den Buffer

Da sind viele Stellen, die langsam sind. Insbesondere, in der tight loop: viele Anfragen an die HashMap (inkl. vieler Berechnungen von Hashes, viele Überprüfungen auf Fehler, viele write()-Operationen. Wie langsam ist es?

Benchmark 1: cat encoded | target/release/examples/decode > /dev/null
  Time (mean ± σ):     11.583 s ±  0.520 s    [User: 11.072 s, System: 1.645 s]
  Range (min … max):   10.952 s … 12.446 s    10 runs

Oh-oh. Über 12 Sekunden. Mehr als doppelt so viel wie der unoptimierte Encoder. Naja, probieren wir mal denselben Trick: Anstatt mit Strings zu arbeiten, arbeiten wir mal direkt auf den Bytes (den Code dieses Zwischenstandes habe ich leider verworfen, deswegen kann ich den hier nicht zeigen).

  Time (mean ± σ):     12.428 s ±  0.574 s    [User: 11.899 s, System: 1.686 s]
  Range (min … max):   11.569 s … 13.488 s    10 runs

WTF, es ist langsamer geworden? Probieren wir es noch mal. Immer noch langsamer… Und obwohl wir uns die String-Überprüfung und die Decodierung des UTF-8 gespart haben. Ok, Änderungen zurücknehmen. Wir schauen uns erst einmal andere Optionen zur Optimierung an. Was ist zum Beispiel, wenn wir den Iterator über Bord werfen und direkt, ohne weitere Funktionsaufrufe, in der Map nachschlagen?

  Time (mean ± σ):     11.018 s ±  0.229 s    [User: 10.484 s, System: 1.730 s]
  Range (min … max):   10.627 s … 11.274 s    10 runs

Minimal besser. Aber vermutlich nicht einmal statistisch signifikant besser (ich habe es nicht nachgerechnet). Aber Moment: vielleicht geht es ja schneller, wenn wir den Buffer vergrößern, in den read() schreibt? So von 128 auf 1024 Bytes?

  Time (mean ± σ):      9.673 s ±  0.180 s    [User: 9.142 s, System: 1.677 s]
  Range (min … max):    9.350 s …  9.884 s    10 runs

Besser. Aber da geht noch mehr. Machen wir das Gleiche wie beim Encodieren: Schreiben wir den Output erst einmal in einen Buffer, um write()-Aufrufe zu sparen:

  Time (mean ± σ):      9.056 s ±  0.433 s    [User: 8.521 s, System: 1.674 s]
  Range (min … max):    8.457 s …  9.648 s    10 runs

Mühsam nährt sich das Eichhörnchen. Also müssen wir uns wohl noch einmal anschauen, warum das direkt-auf-Bytes-Arbeiten hier nichts gebracht hat. Meiner Vermutung ist: Es ist die Hashberechnung. Es kann sein, dass die auf einen char einfach wesentlich effizienter zu berechnen ist als auf einem array. Aber char will ich hier nicht nehmen, dann haben wir wieder die UTF-8-Überprüfung, die wir eigentlich nicht benötigen. Wir haben ohnehin nur gültige Codepoints in der Hashmap, wenn eine Bytefolge nicht gefunden wird, dann ist sie ungültig, egal ob sie gültiges UTF-8 ist oder nicht.

Aber wir können die 4-Byte-Folge ja einfach in u32 umrechnen. Normalerweise muss man sich bei so etwas um die Byte-Order kümmern. Hier aber nicht. Die Byte-Order ist mir egal, solange Hashmap und Suchstring die gleiche haben. Wenn ich also auf beiden Seiten einfach die native Byte-Order nehme müsste es auf allen Maschinen funktionieren und ist nicht mehr Aufwand für die CPU als eine einfache Kopieroperation.

for (i, bytes) in buf[..len].chunks_exact(4).enumerate() {
    // using native byte order here is ok because the decode_map does also uses native byte
    // order.
    write_buf[i] = *decode_map.get(&u32::from_ne_bytes(bytes.try_into().expect("expected 4 byte slice to be transformed to 4 byte array"))).ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidData,
            format!("unexpected byte sequence in stream: '{bytes:0x?}', either invalid utf-8 or non-kittenmoji code point"),
        )
    })?;
}

Und?

  Time (mean ± σ):      5.805 s ±  0.223 s    [User: 5.353 s, System: 1.444 s]
  Range (min … max):    5.491 s …  6.131 s    10 runs

Now we're talking! Natürlich haben wir immer noch den ganzen HashMap-Krams, der die Geschwindigkeit herunterzieht. Bevor jemand fragt: Ich habe es mit einer sortierten Liste und einer Binärsuche ausprobiert, das war deutlich langsamer. Vermutlich, weil es bei dieser Suche viele branches gibt. Die triviale Lösung, einfach die Liste zu durchsuchen ist hier noch langsamer.

Um die Fehlerbehandlung kommen wir auch so oder so nicht herum. Eine kleine Optimierung wäre noch, statt mit chunks_exact zu iterieren und die slices in arrays umzuwandeln (wieder mit Fehlerbehandlung, in diesem Fall aber das etwas effizientere expect, weil wir wissen, dass alle slices 4 bytes lang sind und der Fehlerfall nicht eintritt). Die Alternative array_chunks ist minimal besser, aber bisher nur in der nightly-Version verfügbar und nicht stabil.

Fazit

Ich habe es geschafft, das Encodieren und Decodieren für ein unglaublich ineffizientes Encoding viel effizienter zu machen. Und das besonders da, wo man es wirklich nicht verwenden will. Ein kleines Stückchen Binärdaten mit Emojis zu encodieren? Das sieht lustig aus, und jemand auf Mastodon hat darauf hingewiesen, dass z.B. Mastodon (mit Längenbeschränkung für toots) jedes Emoji als ein Zeichen zählt. Hier ist dieses Encoding also sogar effizienter als Base64, zumindest für den Benutzer.

KittenMoji wurde übrigens für Schlüsselencoding im Small Web erdacht (siehe die Dokumentation von kitten, ein Small Web development kit). Die Idee hinter dem Small Web ist es, wieder mehr selbstgehostete Websites zu, aber trotzdem den Community-Effekt von z.B. Facebook zu haben. Also ein dezentrales soziales Netzwerk, ohne die Nachteile von Facebook. Es soll einfach aufzusetzen sein, aber um ehrlich zu sein, ich bleibe lieber bei meinem guten altmodischen Blog, ohne zu viele fancy features.

Das Repo für meinen KittenMoji De-/Encoder findet ihr auf Gitlab.com. Ich betrachte das Crate soweit erst einmal als Feature-Complete, warte aber noch ein bisschen ab, bis ich die Version auf 1.0.0 setze, vielleicht fällt mir ja noch irgendetwas Wichtiges ein. Wer noch weitere Ideen zur Optimierung hat kann sie gerne ausprobieren, ich nehme Patches an. Wichtig ist, dass alles safe rust ist und keine Abhängigkeiten zum Crate hinzugefügt werden.

Wer KittenMoji in der eigenen Software verwenden möchte… Ich würde fast empfehlen: Kopiert die Quelldateien und fügt sie bei euch ein, schlagt euch nicht mit einer Abhängigkeit herum, die ich höchstwahrscheinlich nicht pflegen werde.

Drei neue oder alte Gründe zur Frustration

Das hier soll ein kurzer Rant werden um einfach mal Dampf abzulassen. Die Themen schweben teilweise seit Mitte letzter Woche im Raum, aber ich hatte nicht die Kraft, darüber zu schimpfen. Jetzt muss es aber langsam mal raus, sonst drehe ich komplett durch.

Merz macht Wahlmampf für die AfD

Da sei zu nächst die Meldung, dass Merz der Ampel ein Ultimatum bis Dienstag stellt. Diese Woche Dienstag, also vorgestern. Merz will Menschen direkt an der Grenze abweisen. „Asylbewerber“. Mit anderen Worten: KEINE HALBE WOCHE nach den FUCKING REKORDERGEBNISSEN FÜR DIE AfD IN LANDTAGSWAHLEN, wo alle so verzweifelt waren, wie eine Hasspartei wie die AfD es so weit bringen konnte MACHT MERZ WAHLKAMPF FÜR EBEN JENE HASSPARTEI. Auf dem Rücken der Schwächsten der Schwächsten. Auf derm Rücken der Leute, die nun definitiv ÜBERHAUPT NICHTS dafür können, was in unserem Land alles schief geht. Es ist wissenschaftlich belegt, dass das Aufgreifen rechter Narrative und Themen durch gemäßigte Parteien nicht die Wähler von der rechtsextremen Partei zu der gemäßigten Partei bringt, sondern umgekehrt, und in dem Zuge auch nebenbei noch rechte Narrative Salonfähig macht.

Fuck you, Merz. Du bist Teil des Problems. Wenn die AfD die Macht übernimmt, hast du kein Recht, dich darüber zu beklagen. Du hast ihnen geholfen. Aber SPD, Grüne, FDP und BSW waran auch mit von der Partie gegen Einwanderung zu hetzen. Ihr könnt euch die zweifelhaften Lorbeeren gerne teilen.

Schon wieder die Vorratsdatenspeicherung

Wenn du merkst, dass du ein totes Pferd reitest, geh zu einem Nekromanten. Dann hast du zwar ein stinkendes, untotes Pferd, aber vielleicht, nur vielleicht läuft es ja dieses Mal in die richtige Richung.

Die Vorratsdatenspeicherung ist nicht nur tot, sondern zwei Mal tot, gekippt vom Bundesverfassungsgericht und vom Europäischen Gerichtshof. Ihr Nutzen ist bestenfalls zweifelhaft, ihre Verfassungswidrigkeit von den höchsten Gerichten bestätigt.

Trotzdem will die SPD jetzt in einem Akt des blinden Aktionismus die Vorratsdatenspeicherung wieder einführen und dabei direkt auch noch den Koalitionsvertrag brechen.

Cookie-Banner

Das dritte Thema ist nur ein kleiner Aufreger, damit ich das Gefühl habe, das nicht alles furchtbar, sondern manches einfach nur ein bisschen scheiße ist. Ich habe in einem alten Artikel schon einmal meine Meinung zu den Cookiebannern dargestellt: sie sind nervig, aber nicht notwendig wenn man seinen Nutzern nicht hinterherspioniert. Sie dienen einzig und allein als Feigenblatt um behaupten zu können, die Nutzer hätten dem massiven Datensammeln zugestimmt.

Kleiner Tipp: Wenn eine Website behauptet, dass ihr „deine Privatsphäre sehr wichtig“ ist und im selben Atemzug die Zustimmung von dir haben will, deine aten mit mehr als 700 Partnern (keine Übertreibung) zu teilen, dann ist denen deine Privatsphäre nicht wichtig.

Die Bundesregierung will jetzt jedenfalls Cookie-Banner eindämmen. So weit klingt das erst einmal nicht schlecht. Die Dinger nerven ja schon. Ich hätte da einen Vorschlag: Es gibt schon diesen Do-Not-Track-HTTP-Header. Da kann man dem Browser sagen, man möchte nicht getracked werden, und dann teilt der Browser das jeder Website mit. Das Problem: keine Website hält sich dran.

Aber man könnte das ja gesetzlich festlegen: Der Header würde dann als explizite nicht-Einwilligung gelten, und somit das Cookie-Banner überflüssig machen.

Hier gibt es nur ein Problem: Die Webseitenbetreiber haben daran kein Interesse. Die wollen die Leute so lange nerven, bis sie der Datensammlung zustimmen. Und da es ja vereinzelt auch sinnvolle Dinge gibt, für die persönliche Daten verarbeitet werden können, wird es auch schwierig, das komplett zu verbieten.

Der Vorschlag der Bundesregierung geht sowieso nicht in diese Richtung. Leut deren Vorstellung soll nämlich eine Drittpartei die Zustimmungen verwalten.

  • Nachteile: hat dieselben Probleme wie oben beschrieben, außerdem hat jetzt einen Drittpartei Daten darüber, welche Seiten du besuchst. Und wenn dieser Dienst mal ausfällt, ist Land unter.
  • Vorteile: Äh… ich würde welche nennen, wenn ich nur welche wüsste.

Update

Ich habe es gerade noch einmal nachgeschlagen laut Artikel 21, Absatz 5 der DSGVO gilt:

Im Zusammenhang mit der Nutzung von Diensten der Informationsgesellschaft kann die betroffene Person ungeachtet der Richtlinie 2002/58/EG ihr Widerspruchsrecht mittels automatisierter Verfahren ausüben, bei denen technische Spezifikationen verwendet werden.

Das wäre zum Beispiel auch der Do-Not-Track-Header. Die Tatsache, dass ich den setze und trotzdem noch Cookiebanner angezeigt kriege ist der Beweis dafür, dass das nichts bringt.

Fazit

Fazit: Ich ziehe mich zurück, informiere mich nicht mehr darüber, was in der Welt so vorgeht, schreibe Programme die bunte Lichter machen und üq048wrhqaüosifj9´hq3zrhqü9wuefhqß9274gz=(&T=§)/WZHEFD)`

Karte oder bar? Ach warte…

Als ich heute zum Essen in die Mensa ging, begrüßten mich große Aushänge: Kartenzahlung sei leider nicht möglich, nur Zahlung per Studierendenausweis ginge noch. Grund: bundesweiter Kartenzahlungsausfall.

Glücklicherweise habe ich mittlerweile einen Promovierendenausweis, was aufgrund einiger bürokratischer Hürden recht lange gedauert hat. Man kann in der Mensa nämlich nicht mehr bar zahlen. Wäre das mit der Kartenzahlung in der ersten Hälfte des Jahres passiert, hätte ich heute hungrig bleiben müssen.

Grund: Die Mensa und die Cafeterien der RUB akzeptieren kein Bargeld mehr. Selbst wenn man nur ein belegtes Brötchen kaufen möchte muss das mit EC-Karte (ach warte, die haben das ja umbenannt, Girocard) oder mit einem vorher aufgeladenen Studierendenausweis ankommen. Es gibt nicht eine einzige Kasse, an der man noch bar zahlen kann.

Das hat dazu geführt, dass man heute einen Studentenausweis haben musste oder hungrig blieb. Im Ausland (Beispiel: Niederlande) werden wir Deutschen ja gerne belächelt, weil wir so am Bargeld festhalten. Aber hier ist mal ein Beispiel was passiert, wenn man Bargeld überhaupt nicht mehr verarbeiten kann.

Stra?e

Ich habe ein Paket bestellt. Der Händler hat es nebst meiner Adresse dem DPD anvertraut. Ich wohne in einer der zahlreichen Straßen, die auf „straße“ enden.

Jetzt kriege ich eine SMS von denen, dass mein Paket bald zugestellt werden soll und ich z.B. den Zustelltermin anpassen kann oder denen erlaube, es irgendwo abzustellen.

Was steht auf der Adresse? „[Soundso]stra?e“. Es ist 2024 und der Deutsche Paketdienst kann nicht mit ß umgehen? Beim Händler kann das Problem nicht liegen: das Label auf dem Paket hat eindeutig ein ß.

Im Ernst 2024. Textencoding ist ein gelöstes Problem!

Erinnerung: Infinite Screaming Generator

In Anbetracht der Wahlergebnisse der Landtagswahl Thüringen 2024 möchte ich gerne noch einmal auf den Infinite Screaming Generator hinweisen, den ich in einem Blogpost im Juni schon erwähnt habe (es gibt den auch als Mastodon-Bot).

Wenn die CDU nicht mit der AfD koalieren möchte, müssen sie meiner Rechnung nach mit dem BSW, den und den Linken koalieren. Für die AfD würde eine Koalition mit CDU oder dem BSW reichen (oder mit der Linken und der SPD, aber das kann ich mir nun wirklich nicht vorstellen).

Das BSW hat sich bisher durch ähnlich populistische Themen wie die AfD positioniert (insbesondere im Bereich Ausländerfeindlichkeit stehen sich die beiden nahe), haben sich aber klar gegen eine Koalition mit der AfD ausgeprochen. Die CDU hingegen…

Auf der einen Seite sprechen sich viele Politiker der CDU immer wieder gegen die AfD aus. Auf der anderen Seite haben einige CDU-Mitglieder immer wieder gezeigt, dass die Brandmauer gegen Rechts aus Stroh besteht. Und die CDU hat ja auch dadurch, dass sie Themen von der AfD immer wieder aufgegriffen hat, die AfD erst gesellschaftsfähig gemacht. Und eine Koalition mit der Linkspartei wird, soweit ich das einschätzen kann, in der CDU als genau so gefährlich angesehen wie eine Koalition mit der AfD.

Meine Hypothese: Innerhalb dieser Woche wird irgendein Thüringischer CDU-Politker etwas von „wir müssen pragmatisch denken“ und „wir müssen dem Wählerwillen folgen“ reden und eine Koalition mit der AfD „nicht kategorisch ausschließen“.

Dann wird es einen riesigen Shitstorm geben und dieser Politiker wird sagen, dass das nicht so gemeint war oder dass seine Worte aus dem Zusammenhang gerissen wurden.

Darüber, wie der kommende Landtag in Thüringen aussieht, mag ich nicht spekulieren. Ich weiß nur eins: Schön wird es nicht.

Straßenlärm in Witten

Seit Ende letzten Jahres wohne ich in Witten. Eigentlich ist meine Wohnung ganz gut gelegen. Aber die kleine Straße, die an dem Haus vorbeiführt hat Tempolimit 50. Dabei ist sie nun wirklich nicht so breit.

Und obwohl meine Wohnung der Straße nicht direkt zugewandt ist und sogar eine Haushälfte entfernt von ihr sitzt, ist der Straßenlärm nachts unerträglich laut. Das war im Winter kein Problem. Jetzt, im Sommer, habe ich aber praktisch immer die Tür zu meinem Schlafzimmer offen. Und meine Güte geht mir das auf die Nerven.

Und der normale Autolärm ist schon schlimm genug. Schlimmer wird es bei nasser Fahrbahn. Dann sind die Laufgeräusche deutlich schlimmer. Auch bin ich schön öfters beim kurz vor dem Einschlafen von einem LKW geweckt worden, der mit hoher Geschwindigkeit über einen kleinen Huckel fährt, den es wohl in der Straße geben muss und dann einen lauten KNALL macht.

Am Schlimmsten sind aber die Autos und Motorräder mit kaputtem Auspuff, die hier regelmäßig mitten in der Nacht mit gefühlt viel zu hoher Geschwindigkeit über die Straße brettern. Da machen die Laufgeräusche keinen Unterschied mehr, der Motor ist hier einfach lauter als bei einem normalen Auto, wenn man das Ohr direkt daran halten würde.

Nun ist natürlich die Frage: Warum ist auf dieser Straße überhaupt 50 erlaubt? Die ist nun wirklich nicht so breit. Die könnte auch gut 30 sein. Stellt sich heraus: Die Stadt Witten hat da schon einen Plan. So 20% eines Plans. Sie wollten auch Input von Bürgern der Stadt. Dummerweise haben sie dafür nicht genug Marketing gemacht, an mir ist das glatt vorübergegangen. Von Juni bis Juli konnte man Vorschläge einreichen. Ich habe diese Seite erst ende Juli entdeckt. Immerhin scheint meine Straße so oder so im Fokus zu liegen.

Ich schaue Mal, was daraus wird. Die wollen jetzt einen Plan erarbeiten. Der nächste Plan danach kommt erst 2019, also hoffe ich stark, dass dieser hier was taugt. Und einfach nur die Höchstgeschwindigkeit runtersetzen (wobei auch auch befürchte, dass die das nur nachts machen wrden) hilf halt nicht. Man muss den Fahrern auch das Gefühl vermitteln, dass man nicht so schnell fahren kann.

Man könnte zum Beispiel einen Radweg ergänzen. Momentan ist für Fahrräder explizit erlaubt, den Gehweg zu verwenden. Ein angehobener Radweg könnte die Straße so weit verkleinern, dass die Autos nicht mehr 50 fahren können. Und besser für den Radverkehr (und die Fußgänger) wäre das auch.