Z-Wave-Me / home-automation

Z-Way Home Automation engine

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

strange behavior of zway API over loopback interface (authentication)

sebres opened this issue · comments

ZWay API (ZAutomation/api/v1) seems to bypass authentication for several URLs if connecting using loopback interface (e. g. request from localhost or 127.0.0.1).
I assume it could be some host-related authentication (e. g. allow local connections without session or logon info), just if it is intended so, as you see it is not always valid (so hardly usable), for instance some URLs, e. g. /ZAutomation/api/v1/namespaces/devices_all returns 401 for some reasons.

Here is an excerpt of serveral commands for clarity from python (using API of my asynchronous zway-bridge for HA, but the same is happening with curl, see below), doing that without authentication and without ZWAYSession, using 127.0.0.1:8083 as z-way-server host:

>>> r = lambda **kwargs: dict(filter(lambda v: v[0] in ('code', 'message'), zwr(**kwargs).json().items()))

>>> r(area='namespaces/devices_all')
{'code': 401, 'message': '401 Unauthorized'}

>>> r(area='devices')
{'code': 200, 'message': '200 OK'}

>>> r(area='devices/ZWayVDev_zway_5-1-38')
{'code': 200, 'message': '200 OK'}

>>> r(area='devices/ZWayVDev_zway_5-1-38', cmd='update')
{'code': 200, 'message': '200 OK'}

>>> r(area='devices/ZWayVDev_zway_5-1-38', cmd='startUp')
{'code': 200, 'message': '200 OK'}

Moreover some devices are "partially" available then using this way (for instance /ZAutomation/api/v1/devices doesn't show those devices in the list, but access by device-id returns it pretty well).
In following example I see *_5-1-38 but no *_8-1-38 in device list, but *_8-1-38 is accessible by /ZAutomation/api/v1/devices/ZWayVDev_zway_8-1-38 (here as a diff "without auth" vs. "with auth"):

 >>> [v['id'] for v in zwr(area='devices').json()['data']['devices'] if re.search(r'[58]-1-38$', v['id'])]
-['ZWayVDev_zway_5-1-38']
+['ZWayVDev_zway_5-1-38', 'ZWayVDev_zway_8-1-38']

 >>> r(area='devices/ZWayVDev_zway_8-1-38')
 {'code': 200, 'message': '200 OK'}

Here are verbose outputs for two requests using curl against localhost and remote address:

curl -v http://127.0.0.1:8083/...

I have not found any setting (neither in smarthome nor in expert application) allowing or prohibiting this per device/entity.
Is some option available to control such a permissions?

$ curl -v http://127.0.0.1:8083/ZAutomation/api/v1/devices/ZWayVDev_zway_5-1-38
curl: /opt/z-way-server/libs/libcurl.so.4: no version information available (required by curl)
*   Trying 127.0.0.1:8083...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8083 (#0)
> GET /ZAutomation/api/v1/devices/ZWayVDev_zway_5-1-38 HTTP/1.1
> Host: 127.0.0.1:8083
> User-Agent: curl/7.64.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: Mongoose/6.4
< Content-Type: application/json; charset=utf-8
< X-API-VERSION: 2.0.1
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS
< Access-Control-Allow-Headers: Authorization, Accept-Ranges, Content-Encoding, Content-Length, Content-Range, Content-Type, ETag, X-API-VERSION, Date, Cache-Control, If-None-Match, Content-Language, Accept-Language, X-ZBW-SESSID, ZWAYSession
< Access-Control-Expose-Headers: Authorization, Accept-Ranges, Content-Encoding, Content-Length, Content-Range, Content-Type, ETag, X-API-VERSION, Date, Cache-Control, If-None-Match, Content-Language, Accept-Language, X-ZBW-SESSID, ZWAYSession
< Connection: keep-alive
< Date: Sun, 24 Jan 2021 21:17:08 GMT
< Content-Length: 490
<
* Connection #0 to host 127.0.0.1 left intact
{
  "data":{"creationTime":1589802005,"creatorId":1,"customIcons":{},"deviceType":"switchMultilevel",
    ...},
  "code":200,"message":"200 OK","error":null
}
curl -v http://192.168.178.10:8083/...
$ curl -v http://rpi:8083/ZAutomation/api/v1/devices/ZWayVDev_zway_5-1-38
*   Trying 192.168.178.10:8083...
* Connected to rpi (192.168.178.10) port 8083 (#0)
> GET /ZAutomation/api/v1/devices/ZWayVDev_zway_5-1-38 HTTP/1.1
> Host: rpi:8083
> User-Agent: curl/7.71.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< Server: Mongoose/6.4
< Content-Type: application/json; charset=utf-8
< X-API-VERSION: 2.0.1
< WWW-Authenticate: Basic realm="Z-Way ZAutomation API"
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS
< Access-Control-Allow-Headers: Authorization, Accept-Ranges, Content-Encoding, Content-Length, Content-Range, Content-Type, ETag, X-API-VERSION, Date, Cache-Control, If-None-Match, Content-Language, Accept-Language, X-ZBW-SESSID, ZWAYSession
< Access-Control-Expose-Headers: Authorization, Accept-Ranges, Content-Encoding, Content-Length, Content-Range, Content-Type, ETag, X-API-VERSION, Date, Cache-Control, If-None-Match, Content-Language, Accept-Language, X-ZBW-SESSID, ZWAYSession
< Connection: keep-alive
< Date: Sun, 24 Jan 2021 21:22:07 GMT
< Content-Length: 77
<
{"data":null,"code":401,"message":"401 Unauthorized","error":"Not logged in"}
As you can see neither authorization data nor ZWAYSession is provided, but it seems to be disallowed only on remote address and even open for loopback address. If it is intended behavior it is doubtful practice by default (I did not reconfigure something to allow this).

Although I doesn't consider this as a direct security issue (therefore reported here and not per mail), but it can indeed open some vulnerability or an attack vector (for instance if it used as an upstream behind some reverse proxy like nginx and co, opened for outside via some remote address), especially if it is unknown for user behavior.
At least I'll expect some documentation (I found nothing) and perhaps better a configuration parameter like allow_localhost_auth (and disabled host-transparent authentication by default), so everyone will be able to activate this if it is really needed.

But also if it is not a "bug" but feature, it is a bit inconsistent, because as already said some URIs (and/or devices) are disallowed using this way, so if it works, it does that only partially.

Do you know about local user? Check users with Local role

Sure, I will take a look, thanks!
Just if it is such a user, how it is possible that for this connectivity method:

  • some URIs in technical APIs allowed, another not (e. g. devices - 200, namespaces/devices_all - 401)?
  • some devices are not visible in lists (ok, probably due to some permissions), but can be accessed and controlled with device-id?
  • some APIs are completely prohibited (e. g. ZAutomation allowed, ZWaveAPI and JS/Run do not)?
  • Each API is assigned to an access role. The namespaces API is for Admin's only:
    this.router.get("/namespaces/:namespace_id", this.ROLE.ADMIN, this.getNamespaceFunc);
  • The access might be a security issue. Not sure - need to check it. The same filtering function devicesByUser is used in both APIs
  • ZWaveAPI and JS are for Admin only. ZWaveAPI can be public - check in settings for the Z-Wave Binding app. This is for easy integration with third party API