Overview
I recently purchased the Hozelock Cloud Controller to automatically water my garden. While trying to integrate it with OpenHab I discovered various things about the REST API Service that are concerning and useful. – https://www.hozelock.com/product/cloud-controller/
All code can be found https://github.com/martynjsimpson/HozelockAPI
Basics
The Cloud Controller is made up of two parts the “Hub” which connects to your Router/ LAN, and the Cloud Controller which connects to the Hub (and is the bit that is connected to your tap.
Discovery
By using their Cloud Support page http://cloudsupport.hoz3.com/ and entering a valid HubID you can retrieve the status of your Hub and Controller. Inspecting the traffic from the browser shows a GET request to http://hoz3.com/restful/support/hubs/{hubId} returning a JSON Array.
The first thing I noticed was there is no Authentication on the endpoint – you just need a valid HubID.
If we make a direct call to http://hoz3.com/restful/support/hubs/xxxxxx (i.e an Invalid hubId) The endPoint is nice enough to tell us the Regex of valid HubID’s
GET http://hoz3.com/restful/support/hubs/xxxxxx
{
"errorCode": 134611463,
"httpStatus": 400,
"errorMessage": "GET request to '/restful/support/hubs/xxxxxx' failed due to invalid parameter 'hubID': Expected a valid Hub ID matching [0-9A-Z]{6}",
"cause": {
"method": "GET",
"url": "/restful/support/hubs/xxxxxx",
"contentType": null
}
}
If somebody want’s to do the math on the number of permutations this allows I would appreciate it!
Oh did you notice that all of this is over HTTP? So SSL Is supported but only if you request it. It is not forced or redirected from HTTP. Throughout this post, I will probably mix HTTP and HTTPS but assume that everything is possible over HTTP and HTTPS unless I say otherwise.
Now let’s have a look at what else it supports.
Hub
OPTIONS https://hoz3.com/restful/support/hubs/{HubID}
{
"errorCode": 0,
"allowedMethods": [
"GET",
"OPTIONS"
]
}
Nothing really useful here we can do but the data response within the Hub element CAN contain some sensitive information such as location if the user has set it. (The Setup Wizards asks for your location at install time to determine weather at your address). It appears to just be City, Country, Local time and Time Zone.
Let’s keep going through the rest of the JSON Response to the GET /{hubId}
Schedules
GET https://hoz3.com/restful/support/hubs/{hubId}/schedules
Returns an array of all Schedules for the Hub. Note: This does NOT mean the schedule is applied to the controller. (Sample below with some days removed). This is set to water at Sunrise and Sunset hence two entries per day.
TimeStamps appear to be using Unix Epoch Milliseconds. -2000 appears to mean Sunrise, -1000 appears to mean Sunset (or an offset). Duration is in Milliseconds (15 mins here)
[
{
"scheduleID": "{scheduleId}",
"name": "{scheduleName}",
"description": null,
"scheduleDays": {
"Monday": {
"dayOfWeek": "Monday",
"wateringEvents": [
{
"startTime": -2000,
"endTime": 898000,
"duration": 900000,
"enabled": true
},
{
"startTime": -1000,
"endTime": 899000,
"duration": 900000,
"enabled": true
}
]
},
"Thursday": {
"dayOfWeek": "Thursday",
"wateringEvents": [
{
"startTime": -2000,
"endTime": 898000,
"duration": 900000,
"enabled": true
},
{
"startTime": -1000,
"endTime": 899000,
"duration": 900000,
"enabled": true
},
....... removed
]
}
}
}
]
You can also get a specific schedule from the array by appending the scheduleID to the URL.
GET https://hoz3.com/restful/support/hubs/{hubId}/schedules/{scheduleId}
Looking at what can be done at the Schedules element.
OPTIONS https://hoz3.com/restful/support/hubs/{hubId}/schedules/
{
"errorCode": 0,
"allowedMethods": [
"GET",
"HEAD",
"OPTIONS"
]
}
Not much of use here although HEAD could be useful. Looking at the specific Schedule element.
OPTIONS https://hoz3.com/restful/support/hubs/{hubId}/schedules/{scheduleId}
{
"errorCode": 0,
"allowedMethods": [
"DELETE",
"GET",
"HEAD",
"PATCH",
"POST",
"PUT",
"OPTIONS"
]
}
This is much more useful with POST, PUT and PATCH allowing us to create, “upsert” and update Schedule information. Even DELETE is available.
I was able to adjust the schedule using a PATCH method (just changed the name) but I am yet to fully work out the structure of create, updating and deleting a schedule.
Controllers
One or more controllers can be associated with a single hub (perhaps one for the front garden and one for the rear).
GET https://hoz3.com/restful/support/hubs/{hubId}/controllers
{
"controllers": [
{
"name": "{controllerName}",
"image": null,
"controllerID": "0",
"scheduleID": "{scheduleId}",
"schedule": {
"scheduleID": "{scheduleId}",
"name": "{scheduleName}",
"description": null,
"scheduleDays": {
"Monday": {
"dayOfWeek": "Monday",
"wateringEvents": [
{
"startTime": -2000,
"endTime": 898000,
"duration": 900000,
"enabled": true
},
{
"startTime": -1000,
"endTime": 899000,
"duration": 900000,
"enabled": true
}
]
},
....... trimmed
}
}
},
"hasWaterNowEvent": false,
"pause": null,
"adjustment": null,
"waterNowEvent": null,
"currentWateringEvent": null,
"nextWateringEvent": {
"startTime": 1556219700000,
"endTime": 1556220600000,
"duration": 900000,
"enabled": true
},
"lastCommunicationWithServer": 1556211067000,
"nextCommunicationWithServer": 1556212140000,
"batteryStatus": "OK",
"signalStrength": "GOOD",
"overrideScheduleDuration": null,
"isChildlockEnabled": false,
"isWatering": false,
"isPanelRemoved": false,
"isTested": true,
"isAdjusted": false,
"isScheduleUpToDate": true,
"isPaused": false
}
],
"inPairingMode": false,
"lastServerContactDate": 1556211067000,
"hubResetRequired": false,
"controllerResetRequired": false,
"isUresponsive": false
}
}
Again this returns an Array of all Controllers and you can address a specific Controller by appending the controllerId to the URL such as /{hubId}/controllers/{controllerId}
Both the controllers and controller/{controllerId} endpoint only support GET, HEAD and OPTIONS.
OPTIONS https://hoz3.com/restful/support/hubs/{hubId}/controllers/{controllerId}/
OR
OPTIONS https://hoz3.com/restful/support/hubs/{hubId}/controllers
{
"errorCode": 0,
"allowedMethods": [
"GET",
"HEAD",
"OPTIONS"
]
}
Actions
This section is pretty much entirely credited to anthonyangel (see credits)
An undocumented part of the API’s called Actions exists which allow you to submit tasks for the Hub (and thus controller) to pick up.
GET http://hoz3.com/restful/support/hubs/{hubId}/controllers/actions/
{
"errorCode": 0,
"actions": [
"pause",
"unpause",
"adjust",
"unadjust",
"waterNow",
"stopWatering",
"setMode",
"ping"
]
}
Using the constructs worked out by anthonyangel we can send a POST with a body to issue a waterNow command.
POST http://hoz3.com/restful/support/hubs/{hubId}/controllers/actions/waterNow
Request Body
{
"controllerIDs":[{controllerId}],
"duration":300000
}
Response
{
"errorCode": 0
}
Sure enough after the up to 20min wait time for the controller to poll the hub the alert comes up on my phone saying it is watering.
Pause – Will pause watering for x number of days
Unpause – Will remove a Pause
Adjust – I believe this endpoint is used to increase the percentage of watering time for a period of time.
UnAdjust – Undo the adjust
waterNow – Starts a watering session
stopWatering – Stops the watering
setMode – Not sure but is expecting “mode” parameter
ping – Despite ping being listed as available if you issue a POST or a GET the response says that ping is not implemented.
With all of these if you bulld the payload with just controllerIds the error response will kindly tell you what else you are missing. It helps if you have the app to hand to figure out what you put in the UI as to what might be in the API.
Conclusions
As is the way with most new IOT it appears that security generally lapses. Sure do we really care if our irrigation system is changed (maybe you can waste some water or kill my plants)? That said the model of Controller –> Hub –> “cloud” means that it should not be possible to compromise the rest of your LAN.
Basic security such as forcing SSL seems to have been missed – it doesn’t take much to enable it server side although I will admit without knowing the OS on the Hub this could be more difficult than I am allowing.
Authentication on the Endpoints even for GET operations is a must in today’s world. Relying on security by obscurity is not wise or feasible. Other people have reported no rate limiting on the API calls so brute forcing HubId’s is technically feasible. OAuth, Certificate-based authentication or even Basic Auth again is trivial to implement server-side but I admit client side on the Hub might be the reason this was never done.
One other thing to consider is should companies like Hozelock have developer portals allowing them access to API Documentation, to register OAuth application ID’s and contribute. I certainly wouldn’t have gone digging as far as I did if the documentation was readily available. This is not a substitute for actual security mind. IOT is all about integration and while the average consumer of this product is likely to not be the Developer type, there are developers out there. I want to integrate my watering system with the rest of my home automation, I want to implement Alexa/ Google Assist skills if companies don’t provide them and I certainly want to implement the number 1 missing feature on this application – automatic adjustments for weather. It’s all good telling me it has been hot for 3 days so “you might want to adjust your watering schedule” – you adjust my watering schedule, you know the weather better than me, you have the information, just do it for me.
Overall I love the product, I suck at remembering to water the garden and my beds have never looked so good. It was a pain to initially install, due to I suspect previous attempts to pair the controller to the hub, but this was fixed by doing a factory reset on everything and starting from scratch. Keep the alerts turned on so you are alerted to watering events (or lack of) and you will soon see if somebody is changing your configuration or watering your garden for you.
Hozelock like many other brands appears to be rushing into the IOT space to be “first to market” or even to play catch up against their competitors. Security is often left behind in an effort to get to market or perhaps even not considered in the design phase. It is critical that companies consider the implications of their security design decisions for IOT devices just as they would for their own internal applications.
I searched around for somewhere to report this to but couldn’t find anything.
Credits
Original inspiration to look into this comes from https://community.home-assistant.io/t/having-hozelock-cloud-controller-kit-intergration/55694/3
anthonyangel. He apparently reported the lack of security around Jul 2018 and it doesn’t look like much came of it.