Fehlerhafte CORS und Status-Code implementierung

Begonnen von Real-TTX, 25 Februar 2017, 01:20:44

Vorheriges Thema - Nächstes Thema

Real-TTX

Hallo,

habe aus aktuellem Anlass, mit der Implementierung des CSRF-Token, einige Probleme bei den Requests - würde hierfür folgende Änderungen vorschlagen:

CORS
1. Beim lesen von Custom-Headers über CORS, muss es erst vom Server erlaubt werden -Handling über Preflight Requests - Hierfür zuständig ist der Header: Access-Control-Expose-Headers
https://developer.mozilla.org/de/docs/Web/HTTP/Headers/Access-Control-Expose-Headers

Hinweis: Als Workaround lese ich den Token, aus dem Body-Tag (Übertrage den ganzen Unfug an den Client) - Hier übrigendes teilweise inkonsequent mit einfachen ' - manche anderen Attribute sind mit einem Doppelten-Anführungszeichen versorgt.


Falsche Requests
1. Man sollte sich überlegen, ob man nicht richtige HTTP Status-Codes ausgibt - Ich habe im falle eines "cmd?=set device xyz&fwcsrf=TheRightToken" und "cmd?=jsonlist2%20Filter&fwcsrf=AnyWrongToken" den identischen Output (HTTP-Status: 200, No Content). Im zweiten Fall wäre eigentlich 400 - Bad Request oder 401 - Unauthorized IMHO richtig. Nicht immer nur 200...

I. Fehlerhafte Commands "list unkownDevice" sollte z.B. 400 sein.

II. Fehlerhaftes Token wie oben geschrieben  sollte z.B. 400, 401 sein.

Hinweis: Kein Workaround möglich.


Ich hoffe ich bin nicht ganz auf dem Schlauch gestanden...  ;)


Viele Grüße
Server: 3x Supermicro A1SAi-2750F, FHEM @ Debian-VM
Bandwidth: 800 Mbit / 100 Mbit, Failover LTE
Homematic: 2x HM-MOD-RPI-PCB (via Pi3 socat)
Z-Wave: Z-Wave.Me USB Stick (via Pi3 socat)
RFXTrx: RFXCom (via Pi3 socat)

rudolfkoenig

CORS: Ich habe nichts gegen dem Vorschlag, wenn jemand einen getesteten Patch hat, werde ich es einbauen. Erklaerung fuer andere: CORS ist per Voreinstellung deaktiviert und wird es auch so bleiben, da man es im Normalfall nicht benoetigt.

Falsche Requests: bei falschen CSRF finde ich 403 sinnvoll. Falls noch jemand sich dafuer ausspricht, dann werde ich es implementieren.

Bei list unknowndevice bleibt es bei 200: bei FHEM Befehlen (ja, es gibt auch andere neben list) gibt es zu viele Sonderfaelle, die nicht sinnvoll mit HTTP Codes beschrieben werden koennen. Abgesehen davon sollte sowas formatiert im Browser angezeigt werden, und nicht eine Browser-Fehlerseite produzieren.

Real-TTX

ZitatCORS: Ich habe nichts gegen dem Vorschlag, wenn jemand einen getesteten Patch hat, werde ich es einbauen. Erklaerung fuer andere: CORS ist per Voreinstellung deaktiviert und wird es auch so bleiben, da man es im Normalfall nicht benoetigt.
Ich würde es mir bei Gelegenheit mal anschauen - bin leider im FHEM-Code nicht so fit...  Daher bin ich natürlich auch erfreut, wenn es jemand anders übernimmt  :P


ZitatFalsche Requests: bei falschen CSRF finde ich 403 sinnvoll. Falls noch jemand sich dafuer ausspricht, dann werde ich es implementieren.
Super, wird sicher noch jemand für sein ;-) Allerdings ist 403 imho nicht richtig. Bei 403 - Forbidden ist eingentlich sowas gemeint: "Du hast ne gültige Sitzung oder auch nicht - aber diesen Inhalt darfst du definitiv nicht sehen".

Docs:

10.4.4 403 Forbidden

The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated. If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, it SHOULD describe the reason for the refusal in the entity. If the server does not wish to make this information available to the client, the status code 404 (Not Found) can be used instead.


Source: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html



Beim "list unkowndevice" kann ich Recht geben. Der Aufwand wäre ggf. zu hoch.

Nach als Info:
Die Browser-Fehlerseite ist eigentlich zu vernachlässigen - Ich kenne eigentlich nur den IE der "kurze HTTP-Fehlermeldungen" anzeigt bei 400/Client Fehlern.
Desweiteren könnte man das z.B. nur für XHR=1 machen -man wäre schon ein bisschen näher an einer REST-API.


Vielen Dank & Lieben Gruß
Server: 3x Supermicro A1SAi-2750F, FHEM @ Debian-VM
Bandwidth: 800 Mbit / 100 Mbit, Failover LTE
Homematic: 2x HM-MOD-RPI-PCB (via Pi3 socat)
Z-Wave: Z-Wave.Me USB Stick (via Pi3 socat)
RFXTrx: RFXCom (via Pi3 socat)

Thorsten Pferdekaemper

Zitat von: Real-TTX am 25 Februar 2017, 10:18:25-man wäre schon ein bisschen näher an einer REST-API.
Warum sollte man da hinwollen?
Gruß,
   Thorsten
FUIP

Real-TTX

Was ist das für ein Einwand? Warum nicht?

IMHO ist ne REST-API das einzig richtige....
Server: 3x Supermicro A1SAi-2750F, FHEM @ Debian-VM
Bandwidth: 800 Mbit / 100 Mbit, Failover LTE
Homematic: 2x HM-MOD-RPI-PCB (via Pi3 socat)
Z-Wave: Z-Wave.Me USB Stick (via Pi3 socat)
RFXTrx: RFXCom (via Pi3 socat)

Thorsten Pferdekaemper

Zitat von: Real-TTX am 25 Februar 2017, 11:12:48IMHO ist ne REST-API das einzig richtige....
Diese Diskussion hatte ich schon einmal im geschäftlichen Bereich. Da haben auch erstmal alle gemeint, dass das doch das einzig wahre wäre. Dummerweise war es für die Anwendung gar nicht möglich.
Warum denkst Du denn, dass es das einzig wahre wäre? Meiner Meinung nach ist das nur mal wieder so ein kleiner Hype, der Aufwand ohne Nutzen verursacht.
...oder erkläre doch mal, wo man bei FHEM davon einen Nutzen hätte.
Gruß,
   Thorsten
FUIP

Real-TTX


Wir haben im Geschäft auch eine REST-API. Diese allerdings seit knapp 15 Jahren. Man hatte in der Vergangenheit versucht auf SOAP umstellen... Aber naja.. SOAP ist Vergangenheit und REST ist gerade so hoch wie nie.

Dass eine REST (Representational State Transfer) für eine Anwendung garnicht möglich ist. Kann sein, ich mir allerdings aktuell nicht Vorstellen - Ich konnte in meinen Anwendungsfällen bis jetzt alles in Ressourcen abbilden. Befehle werden z.B. mit POST (Create) auf eine Resource z.B. /User/Orders abgesetzt, zum eine Bestellung aufgeben.

Für mich ist es das einzig richtige, weil ich schon viele viele Schnittstellen gesehen habe und der Gedanke, dass alles eine Resource abbildet, ist wie ich finde ein verdammt guter Ansatz: Skalierbarkeit, Übersicht, Kompatibilität, Eine Ressource kann ein Object auf der Client-Seite abbilden, uvm.

Der nutzen in FHEM? So finde ich den Vergleich zwischen folgendem schon recht eindeutig :


fhem?cmd=jsonlist2%20ROOM%3DMyRoom"
fhem?cmd=jsonlist2%20DeviceThermostat"

und

/fhem/rooms/MyRoom
/fhem/rooms/MyRoom/devices/DeviceThermostat
/fhem/rooms/MyRoom/devices/DeviceThermostat/Readings/MyReading


Oder findest du es Aktuell besser und schöner? Wenn nicht, habe ich nicht ganz Verstanden was das für ein Kommentar von "Warum sollte man da hinwollen" werden sollte? Warum nicht? Warum nicht etwas verbessern? In der Heimautomatisierung finden sich Beispiele z.B. openHAB REST API.

Oder was sind deiner Meinung nach die Nachteile einer REST-API ?

Viele Grüße
Server: 3x Supermicro A1SAi-2750F, FHEM @ Debian-VM
Bandwidth: 800 Mbit / 100 Mbit, Failover LTE
Homematic: 2x HM-MOD-RPI-PCB (via Pi3 socat)
Z-Wave: Z-Wave.Me USB Stick (via Pi3 socat)
RFXTrx: RFXCom (via Pi3 socat)

justme1968

#7
der vergleich ist in der tat ziemlich eindeutig. die aktuelle fhem version erlaub bei jsonlist2 beliebige filter. d.h. man kann raum übergreifend beliebig viele devices nach ziemlich komplexen kriterien auf einen schlag auflisten.

das geht bei deiner variante erst mal garnicht.

ein vernünftiges frontend fragt den system status nur ein mal beim start auf einen schlag ab und aktualisiert sich dann per longpoll oder websocket event basiert und zeitnah.

ein rest api hat sicher sinnvolle einsatzmöglichkeiten. es ist aber kein allheilmittel und nicht die allein selig machende warheit.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Real-TTX

Ich sage ja nicht, dass es schlecht ist?

Zitatdas geht bei deiner variante erst mal garnicht.

Selbstverständlich, darfst du bei REST Parameter anhängen: /fhem/devices/?filter=SameAsJsonlist2

Wird ja nicht nur von Frontends verwendet....  War nur eine Idee.....
Server: 3x Supermicro A1SAi-2750F, FHEM @ Debian-VM
Bandwidth: 800 Mbit / 100 Mbit, Failover LTE
Homematic: 2x HM-MOD-RPI-PCB (via Pi3 socat)
Z-Wave: Z-Wave.Me USB Stick (via Pi3 socat)
RFXTrx: RFXCom (via Pi3 socat)

rudolfkoenig

Ich habe 401 eingebaut, und fhemweb.js erweitert, dass es bei 401 versucht ein neues token zu holen, d.h nach einem FHEM-Neustart sollte in den meisten (allen?) Faellen kein Browser-Refresh notwendig sein.

ZitatSelbstverständlich, darfst du bei REST Parameter anhängen: /fhem/devices/?filter=SameAsJsonlist2
Inwiefern ist das, was man z.Zt. mit FHEM machen kann, kein REST?

Real-TTX

Super!!

Vielen Dank!  8)

Ja, wenn du es so implementiert hast, ist in allen Fällen kein Browser-Refresh nötig!


Inwiefern ist das, was man z.Zt. mit FHEM machen kann, kein REST?

REST ist eine Struktur / Layout der Schnittstelle. Im Vergleich zu SOAP (oder ähnlichem), bei denen Funktionen definiert werden, gibt es bei REST nur noch Ressourcen / stehen diese im Vordergrund die je nach Zugriff mit GET / POST / PUT / DELETE / HEAD (In den meisten Fällen) abgefragt werden. Der HTTP-Statuscode gibt den status an. Bei Rest wollte man alles nutzen, was man hatte (Method, Status-Code, Header, URI) um eine Schnittstelle zu definieren.
Aktuell gibt es den Parameter "cmd" welcher keine spezifische Ressource darstellt, sondern alles sein kann. Der einfachste Fall um daraus eine REST-Api zu machen wäre z.B. */fhem/command/parameter1/parameter2 - ist ein bisschen weit hergeholt, aber auch hier könne man es sich als Resource vorstellen: GET /fhem/list/myDevice/ (Weit hergeholt - da Rückgabe nicht klar definiert - von text/plain bis application/javascript-xx?? (json) alles sein kann).

Einige REST Beispiele:

POST /devices/  : Erzeugt ein Gerät, Daten im Request-Stream (Name, Typ, ...)
GET /devices/ : Gibt eine vollständige Liste mit. Optional mit Parametern die die Ressource z.B. Filtern
PUT /devices/MyDevice/ : Verändert ein Device, Daten im Request-Stream
HEAD /devices/MyDevice/ : Prüft ob das Device existiert.
GET /devices/MyDevice/Attributes/ : Gibt alle Attribute zurück
PUT /devices/MyDevice/Attributes/room : Verändert den Raumname

Viele Grüße

PS: Wollte jetzt kein Fass aufmachen  ;)

Server: 3x Supermicro A1SAi-2750F, FHEM @ Debian-VM
Bandwidth: 800 Mbit / 100 Mbit, Failover LTE
Homematic: 2x HM-MOD-RPI-PCB (via Pi3 socat)
Z-Wave: Z-Wave.Me USB Stick (via Pi3 socat)
RFXTrx: RFXCom (via Pi3 socat)

Thorsten Pferdekaemper

Hi,
vielleicht habe ich hier etwas reflexhaft zu ablehnend reagiert. Du hast da wohl einen wunden Punkt erwischt. Bei meiner Arbeit ging es vor etwa zwei Jahren auch darum, eine Schnittstelle zwischen zwei Systemen zu bauen. Im Prinzip ging (oder geht es immer noch) darum, einen recht komplexen Datentyp mit mehr als 100 Variablen, die in Listen und anderen Strukturen vorliegen, als Request zu schicken. Als Response kommt dann ebenfalls wieder ein genause komplexer Datentyp zurück. Wir hatten schon eine ähnliche Schnittstelle, die so etwas direkt per XML über POST als Request und ebenfalls XML als Response erledigt. Also alles ganz einfach.
Jetzt kam aber eine Standardisierung: Ab sofort sollten alle Schnittstellen mit REST funktionieren. Das einzusetzende Framework kann die ganzen Standard-Methoden (GET, POST, PUT etc.). Allerdings gibt es keine Methode, bei der man einen komplexen Datentyp als Request schicken und als Response zurück bekommen kann. Man kann entweder etwas kompexes schicken (POST, PUT), was eine "einfache" Antwort liefert oder eine "einfache" Anfrage schicken mit einer komplexen Antwort (GET). Zumindest ist das in diesem Framework so, aber ich denke, dass das so ist, weil man davon ausgeht, dass es immer eine "Ressource" gibt, die man etwas fragt oder mit der man etwas macht. In meinem Fall ist das allerdings nicht wirklich so, so dass man sich künstlich eine Ressource zusammendefinieren müsste.
Also, es hätte nur die zwei Möglichkeiten gegeben: Entweder ein GET und den komplexen Request in die URI verpacken. Ich glaube, dass das schon an der maximalen Länge der URI gescheitert wäre. Außerdem ist das ja auch nicht gerade im Sinne des Erfinders, eine "einfache" Schnittstelle zu haben und es dann sehr kompliziert zu machen, damit es einfach bleibt.
Die zweite Möglichkeit wäre gewesen, per POST erst einmal eine Ressource zu erschaffen, die den Request "enthält" und sie dann per GET abzufragen, um die Response zu erhalten. Damit das ganze stateless bleibt muss man das ganze Teil dann auch noch irgendwo speichern. Blöderweise will man aber eigentlich gar nichts anlegen, weshalb man noch ein DELETE hinterherschicken muss. Man hat also drei Abfragen statt nur einer und muss unnötigerweise noch etwas speichern. Die Performance-Probleme hätten wahrscheinlich das ganze Projekt gekillt. Wenn dann noch was im Prozess schiefgeht muss man auch noch einen Mechanismus bauen, der die Leichen wegräumt.
Die darauf folgende Diskussion hat mich gefühlt ein Jahr meines Lebens gekostet. Deshalb reagiere ich auf REST etwas "allergisch". Es kann allerdings sein, dass das REST Prinzip dafür gar nichts kann und es hier nur um ein bestimmtes Framework geht.

Zitat von: Real-TTX am 25 Februar 2017, 17:08:02Man hatte in der Vergangenheit versucht auf SOAP umstellen...
Wie war das nochmal mit aus der Toilette trinken weil man das Bier nicht mag?

ZitatDass eine REST (Representational State Transfer) für eine Anwendung garnicht möglich ist. Kann sein, ich mir allerdings aktuell nicht Vorstellen - Ich konnte in meinen Anwendungsfällen bis jetzt alles in Ressourcen abbilden. Befehle werden z.B. mit POST (Create) auf eine Resource z.B. /User/Orders abgesetzt, zum eine Bestellung aufgeben.
Siehe oben.

Zitatund der Gedanke, dass alles eine Resource abbildet, ist wie ich finde ein verdammt guter Ansatz:
Warum? Das schränkt doch nur ein.

Zitat
Der nutzen in FHEM? So finde ich den Vergleich zwischen folgendem schon recht eindeutig :


fhem?cmd=jsonlist2%20ROOM%3DMyRoom"
fhem?cmd=jsonlist2%20DeviceThermostat"

und

/fhem/rooms/MyRoom
/fhem/rooms/MyRoom/devices/DeviceThermostat
/fhem/rooms/MyRoom/devices/DeviceThermostat/Readings/MyReading


Oder findest du es Aktuell besser und schöner?
Im ersten Moment dachte ich: Ja, Deine Beispiele sehen tatsächlich in Deiner Version schöner aus. Der zweite Gedanke war dann: Und was noch? Die FHEM-Version hat den großen Vorteil, dass ich es genauso auch innerhalb von FHEM verwende. Wenn ich in meinem UI die Konfigurationsdaten eines Geräts brauche, dann mache ich genauso ein "get <device> config" wie ich das von FHEM gewohnt bin. Ich muss mir dann nicht überlegen, wie nochmal der Pfad zu den Devices war und welchen Filter ich setzen muss, um genau die Konfigurationsdaten zu bekommen.

ZitatWenn nicht, habe ich nicht ganz Verstanden was das für ein Kommentar von "Warum sollte man da hinwollen" werden sollte? Warum nicht? Warum nicht etwas verbessern?
Vor Allem weil es Aufwand ist und ich nicht verstehe, welches Problem (außer fehlender Schönheit) dadurch gelöst wird.

ZitatIn der Heimautomatisierung finden sich Beispiele z.B. openHAB REST API.
Na und? Wollen wir das nicht besser machen?

Zitat
Oder was sind deiner Meinung nach die Nachteile einer REST-API ?
So pauschal kann man das nicht sagen, nur vielleicht dass sie einen einschränkt. Ich finde vor Allem für FHEM keine Vorteile.

Zitat von: Real-TTX am 26 Februar 2017, 01:29:34PS: Wollte jetzt kein Fass aufmachen  ;)
Zu spät...  :)

Bitte das alles nicht persönlich nehmen. Ich habe überhaupt nichts dagegen, wenn jemand für FHEM eine REST-API bauen will.

Gruß,
   Thorsten


FUIP

Real-TTX

Hi,

Nochmal vorab: Es war nur ein Vorschlag / Anmerkung! Das Fass sollten wir schnell wieder zu machen ;) Aber deine letzten Fragen will ich jetzt mal nicht so blöde offen stehen lassen - will dich ja nicht ignorieren 8)

Für mich gibt es aktuell keine Nachteile von REST - Es mag sein, dass das ein oder andere vielleicht nicht ganz so gut über REST abgebildet werden kann - bezogen auf dein Beispiel. Wobei man natürlich beim erstellen einer Ressource mit POST, selbstverständlich die neu erstellte komplexe Ressource zurückgeben darf / sogar sollte. Aber kann sein, dass ich es jetzt nicht geblickt habe  ::)


ZitatWarum? Das schränkt doch nur ein.
Genau da gehen die Meinungen auseinander, ich finde genau das Gegenteil.

Zitat... noch? Die FHEM-Version hat den großen Vorteil, dass ich es genauso auch innerhalb von FHEM verwende. ...
Ja, hier gebe ich dir uneingeschränkt recht!

ZitatNa und? Wollen wir das nicht besser machen?
s. Oben - Mir persönlich gefällt einfach eine REST-API (Es ist jetzt nicht so, dass ich den von OH wirklich gut finde... Mir gings nur um die Aufteilung)


Soooooo, nachdem wir hoffentlich jetzt keine offenen Dinge mehr haben, wünsche ich noch einen schönen Abend  :)

Server: 3x Supermicro A1SAi-2750F, FHEM @ Debian-VM
Bandwidth: 800 Mbit / 100 Mbit, Failover LTE
Homematic: 2x HM-MOD-RPI-PCB (via Pi3 socat)
Z-Wave: Z-Wave.Me USB Stick (via Pi3 socat)
RFXTrx: RFXCom (via Pi3 socat)

rudolfkoenig

ZitatMir persönlich gefällt einfach eine REST-API
Man kann mit $data{FWEXT}{"/REST"} einen Namensraum "reservieren", und dann landen alle WEB-Requests, die mit  /fhem/REST anfangen, in der dazugehoerigen Funktion (FUNC), und FHEMWEB macht nichts mehr. Es gibt dafuer 20+ Beispiele in der "offiziellen" Distribution. Du darfst also gerne dein REST-Api Modul bauen, und dann kann die Vorhandene bleiben, wie sie ist :)

PatrickR

Mahlzeit!

Großartig, nach einigem Debugging muss ich feststellen, dass das Problem in TabletUI mit der CSRF-Implementierung hier schon gelöst und behandelt wurde.

Dann steuere ich mal den Patch bei:

# diff -uNr /root/01_FHEMWEB.pm.org 01_FHEMWEB.pm
--- /root/01_FHEMWEB.pm.org 2017-02-26 13:30:46.000000000 +0100
+++ 01_FHEMWEB.pm 2017-02-28 20:15:01.453108173 +0100
@@ -422,7 +422,8 @@
               "Access-Control-Allow-Methods: GET POST OPTIONS\r\n".
               "Access-Control-Allow-Headers: Origin, Authorization, Accept\r\n".
               "Access-Control-Allow-Credentials: true\r\n".
-              "Access-Control-Max-Age:86400\r\n" : "");
+              "Access-Control-Max-Age:86400\r\n".
+              "Access-Control-Expose-Headers: X-FHEM-csrfToken\r\n": "");
    $FW_headerlines .= "X-FHEM-csrfToken: $defs{$FW_wname}{CSRFTOKEN}\r\n"
         if($defs{$FW_wname}{CSRFTOKEN});


Der Patch läuft bei mir und löst das TabletUI-Problem.

Patrick
lepresenced - Tracking von Bluetooth-LE-Tags (Gigaset G-Tag) mittels PRESENCE

"Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning." - Rich Cook