CsrfToken-HowTo: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
L2r (Diskussion | Beiträge) K (→API Web) |
||
Zeile 36: | Zeile 36: | ||
define WEBapi FHEMWEB 8088 global | define WEBapi FHEMWEB 8088 global | ||
attr WEBapi csrfToken none | attr WEBapi csrfToken none | ||
attr WEBapi | attr WEBapi allowfrom 192.168.178.83|127.0.0.1 | ||
Im Forum ist ganz gut beschrieben wie man die Gestaltung des regEx für die IP Adresse machen kann -> Links | Im Forum ist ganz gut beschrieben wie man die Gestaltung des regEx für die IP Adresse machen kann -> Links |
Version vom 6. April 2017, 13:28 Uhr
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, führt aber dazu, dass man nicht mehr mit einem einfachen http Link auf das FHEMWEB zugreifen kann.
Einzeiler
Was früher so ging:
http://localhost:8083/fhem?cmd=set%20Office%20on
muss jetzt etwas ergänzt werden. Mit dem Einzeiler
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 Variante:
curl --data-raw "fwcsrf=$(curl -s -D - 'http://localhost:8083/fhem?XHR=1' | awk '/X-FHEM-csrfToken/{print $2}')" http://localhost:8083/fhem?cmd=set%20Office%20on
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"`
Man kann auch in Scripten den token zunächst abspeichern:
token=$(curl -s -D - 'http://localhost:8083/fhem?XHR=1' | awk '/X-FHEM-csrfToken/{print $2}') curl --data-raw "fwcsrf=$token" http://localhost:8083/fhem?cmd=set%20Office%20on
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 ganz gut beschrieben wie man die Gestaltung des regEx für die IP Adresse machen kann -> Links
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
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