CsrfToken-HowTo: Unterschied zwischen den Versionen
(Abschnitt FHEM Perl eingebaut) |
(Einleitung geändert und erweitert. Struktur geändert.) |
||
Zeile 1: | Zeile 1: | ||
{{SEITENTITEL:csrfToken-HowTo}} | {{SEITENTITEL:csrfToken-HowTo}} | ||
==Vorbemerkung== | |||
FHEM hat mit der Version 5.8 eine Sicherheitsmaßnahme scharfgeschaltet, den csrfToken. | FHEM hat mit der Version 5.8 eine Sicherheitsmaßnahme scharfgeschaltet, den csrfToken. | ||
Dieser Token wird bei jedem Neustart von FHEM neu gebildet. | Dieser Token wird bei jedem Neustart von FHEM neu gebildet. | ||
Dieses Feature erhöht die Sicherheit, führt aber dazu, dass man nicht mehr mit einem | Dieses Feature erhöht die Sicherheit (siehe Links ganz unten), führt aber dazu, dass man nicht mehr (wie in älteren Anleitungen beschrieben), mit einem statischen http Link Steuerbefehle an das [[FHEMWEB]] senden kann. | ||
'''Beispiel''' | |||
Schalte den "Schalter" Office auf on. Das ging früher ohne CsrfToken einfach durch diesen HTML String - egal ob im Browser, in Windows, auf dem Mac, auf der Kommandozeile mit wget usw. | |||
:<syntaxhighlight lang="html">http://hostname:8083/fhem?cmd=set%20Office%20on</syntaxhighlight> | |||
Mit CsrfToken müsste diese Zeile so aussehen (Beispiel, der Token ändert sich bei jedem Start von FHEM): | |||
:<syntaxhighlight lang="html">http://hostname:8083/fhem?cmd=set%20Office%20on&fwcsrf=csrf_196525024154371</syntaxhighlight> | |||
Ist der CsrfToken falsch (weil durch Neustart geändert) erfolgt keine Reaktion Schalters und es gibt eine (ähnliche) Fehlermeldung im FHEMLog: | |||
2019.10.24 12:08:13 3: FHEMWEB WEB CSRF error: csrf_104345683644172 ne csrf_148832911104462 for client WEB_192.168.178.20_54197 ... | |||
''Diese Fehlermeldung kann selten auch so beim Neustart auftauchen, wenn ein Browser Fenster mit altem Token nach einem "shutdown restart" einfach nur einen Refresh macht.'' | |||
Soll ein http: Link zum Steuern von FHEM verwendet werden, gibt es folgende Lösungsansätze: | |||
==Codebeispiele== | |||
===Einzeiler Shell=== | |||
Mit diesem Code | |||
:<syntaxhighlight lang="bash">curl -s -D - 'http://localhost:8083/fhem?XHR=1' | awk '/X-FHEM-csrfToken/{print $2}'</syntaxhighlight> | :<syntaxhighlight lang="bash">curl -s -D - 'http://localhost:8083/fhem?XHR=1' | awk '/X-FHEM-csrfToken/{print $2}'</syntaxhighlight> | ||
Zeile 29: | Zeile 38: | ||
:<syntaxhighlight lang="bash">curl "http://fhem.example.org:8083/fhem?cmd=set%20Office%20on&XHR=1&fwcsrf="`curl -s -D - 'http://fhem.example.org:8083/fhem?XHR=1' | awk '/X-FHEM-csrfToken/{print $2}' | tr -d "\r\n"`</syntaxhighlight> | :<syntaxhighlight lang="bash">curl "http://fhem.example.org:8083/fhem?cmd=set%20Office%20on&XHR=1&fwcsrf="`curl -s -D - 'http://fhem.example.org:8083/fhem?XHR=1' | awk '/X-FHEM-csrfToken/{print $2}' | tr -d "\r\n"`</syntaxhighlight> | ||
==Innerhalb FHEM mit Perl== | ===Innerhalb FHEM mit Perl=== | ||
FHEM kennt natürlich seinen Token und man hat einfachen Zugriff mit der Variable $FW_CSRF. Diese Varibale enthält nicht den Token an sich, sondern den kompletten String zum Anhängen an eine URL in der Form: &fwcsrf=csrf_387849633005507 | FHEM kennt natürlich seinen Token und man hat einfachen Zugriff mit der Variable $FW_CSRF. Diese Varibale enthält nicht den Token an sich, sondern den kompletten String zum Anhängen an eine URL in der Form: &fwcsrf=csrf_387849633005507 | ||
Zeile 36: | Zeile 45: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Raw Import in der Shell== | ===Raw Import in der Shell=== | ||
Will man ganze Definitionsblöcke von der System Kommandozeile importieren, kann man dieses Script verwenden. Im übergebenen Dateinamen sind Zeilenweise die FHEM Befehle gespeichert. Bei Bedarf kann man auch einen anderen Hostnamen und ein anderes Port angeben. Der Token wird zu Beginn ermittelt. Die Leerzeichen werden für HTTP durch %20 ersetzt. | Will man ganze Definitionsblöcke von der System Kommandozeile importieren, kann man dieses Script verwenden. Im übergebenen Dateinamen sind Zeilenweise die FHEM Befehle gespeichert. Bei Bedarf kann man auch einen anderen Hostnamen und ein anderes Port angeben. Der Token wird zu Beginn ermittelt. Die Leerzeichen werden für HTTP durch %20 ersetzt. | ||
:<syntaxhighlight lang="bash"> | :<syntaxhighlight lang="bash"> | ||
Zeile 65: | Zeile 74: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== | ===Python=== | ||
Falls mal jemand aus python (hier für python 2.7) heraus fhem ansteuern möchte | Falls mal jemand aus python (hier für python 2.7) heraus fhem ansteuern möchte | ||
Zeile 157: | Zeile 136: | ||
print('URLError: %s' % urllib2.URLError.reason) | print('URLError: %s' % urllib2.URLError.reason) | ||
return False</nowiki> | return False</nowiki> | ||
==Alternative Konfiguration== | |||
===API Web=== | |||
Falls man ohne den Token arbeiten will, könnte man ein eigenes API Web erstellen und den Zugriff darauf beschränken. In vorhandenen Scripten / Applikationen müsste dann lediglich der Port geändert werden. | |||
define WEBapi FHEMWEB 8088 global | |||
attr WEBapi csrfToken none | |||
attr WEBapi allowfrom 192.168.178.83|127.0.0.1 | |||
Im Forum ist beschrieben wie man die Gestaltung des regEx für die IP Adresse machen kann ({{Link2Forum|Topic=23994}}). | |||
Einen regEx Builder für IP-Ranges findet man unter | |||
[http://www.analyticsmarket.com/freetools/ipregex] | |||
===csrfToken festlegen=== | |||
Dies kann man tun, falls die dynamische Abfrage zur Laufzeit des Tokens nicht möglich ist. | |||
attr WEB.* csrfToken <beliebige Folge aus Zeichen und Zahlen> | |||
Damit können feste URLs verwendet werden: | |||
<nowiki>http://localhost:8083/fhem?cmd=set%20Office%20on&fwcsrf=<fester token></nowiki> | |||
===csrfToken abschalten=== | |||
Dies sollte man als erste Hilfe tun, aber unbedingt darüber nachdenken wie man die Applikation umstellt. | |||
attr WEB.* csrfToken none | |||
===Featurelevel=== | |||
Eine weitere temporäre Notfallmaßnahme wäre den Featurelevel nach dem Update einfach wieder zurückzudrehen | |||
attr global featurelevel 5.7 | |||
==Links== | ==Links== |
Version vom 24. Oktober 2019, 14:25 Uhr
Vorbemerkung
FHEM hat mit der Version 5.8 eine Sicherheitsmaßnahme scharfgeschaltet, den csrfToken. Dieser Token wird bei jedem Neustart von FHEM neu gebildet.
Dieses Feature erhöht die Sicherheit (siehe Links ganz unten), führt aber dazu, dass man nicht mehr (wie in älteren Anleitungen beschrieben), mit einem statischen http Link Steuerbefehle an das FHEMWEB senden kann.
Beispiel
Schalte den "Schalter" Office auf on. Das ging früher ohne CsrfToken einfach durch diesen HTML String - egal ob im Browser, in Windows, auf dem Mac, auf der Kommandozeile mit wget usw.
http://hostname:8083/fhem?cmd=set%20Office%20on
Mit CsrfToken müsste diese Zeile so aussehen (Beispiel, der Token ändert sich bei jedem Start von FHEM):
http://hostname:8083/fhem?cmd=set%20Office%20on&fwcsrf=csrf_196525024154371
Ist der CsrfToken falsch (weil durch Neustart geändert) erfolgt keine Reaktion Schalters und es gibt eine (ähnliche) Fehlermeldung im FHEMLog:
2019.10.24 12:08:13 3: FHEMWEB WEB CSRF error: csrf_104345683644172 ne csrf_148832911104462 for client WEB_192.168.178.20_54197 ...
Diese Fehlermeldung kann selten auch so beim Neustart auftauchen, wenn ein Browser Fenster mit altem Token nach einem "shutdown restart" einfach nur einen Refresh macht.
Soll ein http: Link zum Steuern von FHEM verwendet werden, gibt es folgende Lösungsansätze:
Codebeispiele
Einzeiler Shell
Mit diesem Code
curl -s -D - 'http://localhost:8083/fhem?XHR=1' | awk '/X-FHEM-csrfToken/{print $2}'
kann man den aktuellle csrfToken aus dem Header extrahieren und muss ihn nur noch an den Aufruf anhängen.
Erste (Vorzugs) Variante:
curl --data "fwcsrf=$(curl -s -D - 'http://localhost:8083/fhem?XHR=1' | awk '/X-FHEM-csrfToken/{print $2}')" http://localhost:8083/fhem?cmd=set%20Office%20on
Oder mit mehr Komfort: Nur Einmal die Angabe des Hostnamen und den FHEM Befehl normal mit Leerzeichen angeben:
h='Name oder IP:PortNr'; c='FHEM Befehl'; curl --data "fwcsrf=$(curl -s -D - http://$h/fhem?XHR=1 | awk '/X-FHEM-csrfToken/{print $2}')" http://$h/fhem?cmd=$(echo $c|sed 's/ /%20/g')
Zweite Variante, in dieser wird cr+lf am Ende des ermittelten csrfTokens abgeschnitten:
curl "http://fhem.example.org:8083/fhem?cmd=set%20Office%20on&XHR=1&fwcsrf="`curl -s -D - 'http://fhem.example.org:8083/fhem?XHR=1' | awk '/X-FHEM-csrfToken/{print $2}' | tr -d "\r\n"`
Innerhalb FHEM mit Perl
FHEM kennt natürlich seinen Token und man hat einfachen Zugriff mit der Variable $FW_CSRF. Diese Varibale enthält nicht den Token an sich, sondern den kompletten String zum Anhängen an eine URL in der Form: &fwcsrf=csrf_387849633005507
Beispiel (funktioniert auch in der FHEM Kommandozeile)
{my $button_test = "<a href='http://localhost:8083/fhem?cmd=set%20Office%20on$FW_CSRF'>test</a>"}
Raw Import in der Shell
Will man ganze Definitionsblöcke von der System Kommandozeile importieren, kann man dieses Script verwenden. Im übergebenen Dateinamen sind Zeilenweise die FHEM Befehle gespeichert. Bei Bedarf kann man auch einen anderen Hostnamen und ein anderes Port angeben. Der Token wird zu Beginn ermittelt. Die Leerzeichen werden für HTTP durch %20 ersetzt.
#!/bin/bash # fhemraw Import analog Raw Definition if [ $# -eq 0 ] ; then echo fhemraw bitte so verwenden echo fhemraw <dateiName> [<hostName>] [<portNummer>] exit 1 fi if [ -z $2 ] ; then fhemhost=localhost else fhemhost=$2 fi if [ -z $3 ] ; then fhemhost=$fhemhost:8083 else fhemhost=$fhemhost:$3 fi token=$(curl -s -D - "http://$fhemhost/fhem?XHR=1" | awk '/X-FHEM-csrfToken/{print $2}') while read line do curl --data "fwcsrf=$token" http://$fhemhost/fhem?cmd=$(echo $line|sed 's/ /%20/g') done < $1
Python
Falls mal jemand aus python (hier für python 2.7) heraus fhem ansteuern möchte
import sys import urllib2 import urllib import ssl import urlparse BASEURL = 'https://user:password@server_ip:8083/fhem?' url = BASEURL + 'cmd=set+licht+on' def get_token(url): nurl = urlparse.urlsplit(url) username = nurl.username password = nurl.password url = url.replace(username + ':' + password + '@', '') url = url.replace(" ", "%20") ssl._create_default_https_context = ssl._create_unverified_context p = urllib2.HTTPPasswordMgrWithDefaultRealm() p.add_password(None, url, username, password) handler = urllib2.HTTPBasicAuthHandler(p) opener = urllib2.build_opener(handler) urllib2.install_opener(opener) try: uu = urllib2.urlopen( url=url, data=None, timeout=10 ) token = uu.read() token = token[token.find('csrf_'):] token = token[:token.find("\'")] return token except urllib2.URLError, urllib2.URLError.reason: print('URLError: %s' % urllib2.URLError.reason) return False def fire_command(url): # type: (object) -> object if "@" in url: token = get_token(BASEURL) data = {'fwcsrf': token} data = urllib.urlencode(data) nurl = urlparse.urlsplit(url) username = nurl.username password = nurl.password url = url.replace(username + ':' + password + '@', '') url = url.replace(" ", "%20") ssl._create_default_https_context = ssl._create_unverified_context p = urllib2.HTTPPasswordMgrWithDefaultRealm() p.add_password(None, url, username, password) handler = urllib2.HTTPBasicAuthHandler(p) opener = urllib2.build_opener(handler) urllib2.install_opener(opener) try: urllib2.urlopen( url=url, data=data, timeout=10 ) except urllib2.URLError, urllib2.URLError.reason: print('URLError: %s' % urllib2.URLError.reason) return False
Alternative Konfiguration
API Web
Falls man ohne den Token arbeiten will, könnte man ein eigenes API Web erstellen und den Zugriff darauf beschränken. In vorhandenen Scripten / Applikationen müsste dann lediglich der Port geändert werden.
define WEBapi FHEMWEB 8088 global attr WEBapi csrfToken none attr WEBapi allowfrom 192.168.178.83|127.0.0.1
Im Forum ist beschrieben wie man die Gestaltung des regEx für die IP Adresse machen kann (Thema). Einen regEx Builder für IP-Ranges findet man unter [1]
csrfToken festlegen
Dies kann man tun, falls die dynamische Abfrage zur Laufzeit des Tokens nicht möglich ist.
attr WEB.* csrfToken <beliebige Folge aus Zeichen und Zahlen>
Damit können feste URLs verwendet werden:
http://localhost:8083/fhem?cmd=set%20Office%20on&fwcsrf=<fester token>
csrfToken abschalten
Dies sollte man als erste Hilfe tun, aber unbedingt darüber nachdenken wie man die Applikation umstellt.
attr WEB.* csrfToken none
Featurelevel
Eine weitere temporäre Notfallmaßnahme wäre den Featurelevel nach dem Update einfach wieder zurückzudrehen
attr global featurelevel 5.7
Links
- Automatisch das korrekte Token in Weblink einbinden: Beitrag
- Wikipedia - Cross Site Request Forgery (csrf)
- Blog-Beitrag zu csrf-Token und FHEM