EM-129: различия между версиями
Sirmax (обсуждение | вклад) (Новая страница: «=EM-129=») |
Sirmax (обсуждение | вклад) (→EM-129) |
||
Строка 1: | Строка 1: | ||
=EM-129= |
=EM-129= |
||
+ | В связи с нестабильностью питания решил наконец поставить хоть какое-то защитное реле что б не спалить технику <BR> |
||
+ | (в последний раз броски напряжения не пережил свитч Catalyst 3550 и я остался без PoE для все своих железок чем был сильно расстроен) |
||
+ | |||
+ | <BR> |
||
+ | По EM-129 есть практически исчерпывающий обзор от Андрея, которому нет причин не доверять: |
||
+ | * https://www.kirich.blog/obzory/tehnicheskie-obzory/892-wifi-schetchik-elektroenergii-s-funkciey-zaschity-i-upravleniya-em-129-ot-novatek-elektro.html |
||
+ | |||
+ | Однако в обзоре не упоминается ничего про программную часть устройства |
||
+ | |||
+ | =Зачем?= |
||
+ | Я принципиально не хочу отправлять свои данные в любой клауд, если это не мой клауд и привязывать устройство к чему-ьто в интернете мне кажется очень странной идеей |
||
+ | |||
+ | =Пару слов об устройстве= |
||
+ | Я не могу сказать ничего хорошего про реализацию безопастности EM-129 |
||
+ | * Поддержка HTTPS отсутвует |
||
+ | * Кабельного подключения нет |
||
+ | Этих 2 факторов вполне достаточно что бы при желании можно было взломать сеть и отснифить пароль - в качестве "защиты" могу скаазть только что надо подойти достаточно близко что бы сниффить траффик. |
||
+ | |||
+ | <BR> |
||
+ | Сигнал достаточно слабый хотя расстяние от места установки до точки доступа - примерно 5 метров и одна бетонная стена |
||
+ | <PRE> |
||
+ | /interface/wireless/registration-table/print |
||
+ | Columns: INTERFACE, MAC-ADDRESS, AP, SIGNAL-STRENGTH, TX-RATE, UPTIME |
||
+ | 2 wlan-2.4Ghz-ssid-infra-1 CC:50:E3:FA:83:55 no -81dBm@1Mbps 18Mbps 1d1h13m32s |
||
+ | </PRE> |
||
+ | |||
+ | |||
+ | =Получение данных= |
||
+ | О программной реализации интерфейса тоже можо сказать мало хорошего |
||
+ | <BR> |
||
+ | Из плюсов могу отметить только то что инерфейс работает относительно быстро и не слишком уродлив, впрочем что и не удивительно - под капотом там JavaScript который только отсылает на устройство запросы данных, вся обработка и отрисовка происходит на стороне браузера |
||
+ | |||
+ | <BR> |
||
+ | |||
+ | Из минусов - авторизация сделана максимально плохо |
||
+ | * Используется только пароль, без логина |
||
+ | * При авторизации создается Session ID (далее SID) который может быть только один и который по сути представляет собой префикс в URL для получения данных |
||
+ | * Прямое следствие этого - на устройство можно зайходить и смотреть только одному пользователю одновременно. При подключении второго SID меняется и первого "отлогинивает" |
||
+ | * BasicAuth был бы лучше в разы |
||
+ | |||
+ | |||
+ | Для того что бы понять как происходит авторизация пришлось немного покопаться в коде JS (что замечу говорит о "удачности" решения) |
||
+ | |||
+ | Авторизация происходит следующим образом |
||
+ | * Пользователь вводит пароль |
||
+ | * Браузер отправляет на устройство запрос что бы получить "соль" (случайное число) и тип устройства (похоже что аналогичный код используется не только в EM-129 но и других реле) |
||
+ | * На оснвани полученой соли, имени устройства и пользовательского пароля формируется "секретная строка" и отправляется на сервер |
||
+ | * Если строка верна то сервер возвращает SID |
||
+ | * ВСЕ! |
||
+ | |||
+ | =Код скрипта= |
||
+ | 27 строк на bash |
||
+ | |||
+ | * IP - адрес реле |
||
+ | * SALT_URL - адрес куда посылать запрос на "соль" |
||
+ | * TOKEN_URL - адрес куда послылать запрос на SID |
||
+ | * ALL_DATA_URI - суффикс запроса данных |
||
+ | |||
+ | Секретная строка формируется следующим образом: |
||
+ | |||
+ | * sha1 от строки состоящей из 3 частей |
||
+ | - имя устройства |
||
+ | - Пользовательский пароль закодированный в base64 |
||
+ | - "соль" |
||
+ | |||
+ | По шагам |
||
+ | * A__EM_129_SALT="$(curl ${SALT_URL} | jq -r '.SALT')" - послать запрос на соль и взять поле с именем "SALT" из полученного json |
||
+ | * S=$(printf ${EM_129_PASSWORD} | base64) - закодировать пароль |
||
+ | * SUM="${E__EM_129_DEVNAME}${S}${A__EM_129_SALT}" - склеять строки |
||
+ | * LOGIN="$(printf "${SUM}" | sha1sum | awk '{ print $1}')" - взять sha1 от склейки строк |
||
+ | * SID="$(curl "${TOKEN_URL}${LOGIN}"| jq -r '.SID')" - отправить запрос на SID и взять из ответа значение поля SID |
||
+ | |||
+ | Получение данных дальше |
||
+ | * ALL_DATA_URL="http://${IP}/${SID}/${ALL_DATA_URI}" - сформировать адрес куда слать запрос с учетом SID |
||
+ | * D=$(curl ${ALL_DATA_URL} | jq -r .) получить данные |
||
+ | |||
+ | Данные получаются в виде json |
||
+ | <PRE> |
||
+ | { |
||
+ | "volt_msr": 2302, |
||
+ | "freq_msr": 5000, |
||
+ | "cur_msr": 321, |
||
+ | "pows_msr": 740, |
||
+ | "enrga_msr": 32,. |
||
+ | "enrga_d_msr": 0, |
||
+ | "enrga_w_msr": 0, |
||
+ | "enrga_m_msr": 0, |
||
+ | "enrgs_msr": 39, |
||
+ | "sys_flag": 1409482896, |
||
+ | "faul_flag": 0, |
||
+ | "ar_time": 0, |
||
+ | "cost_energy": 1700, |
||
+ | "cost_unit": 16, |
||
+ | "STATUS": "OK" |
||
+ | } |
||
+ | </PRE> |
||
+ | |||
+ | Код скрипта |
||
+ | <PRE> |
||
+ | #!/bin/bash |
||
+ | |||
+ | set -eu${DEBUG:+x} |
||
+ | |||
+ | IP="192.168.31.250" |
||
+ | SALT_URL="http://${IP}/api/login?salt" |
||
+ | TOKEN_URL="http://${IP}/api/login?login=" |
||
+ | ALL_DATA_URI="api/all/get?volt_msr&freq_msr&cur_msr&pows_msr&enrga_msr&enrga_d_msr&enrga_w_msr&enrga_m_msr&enrgs_msr&sys_flag&faul_flag&ar_time&cost_energy&cost_unit" |
||
+ | |||
+ | A__EM_129_SALT="$(curl ${SALT_URL} | jq -r '.SALT')" |
||
+ | E__EM_129_DEVNAME="EM-129" |
||
+ | EM_129_PASSWORD="12345678" |
||
+ | |||
+ | S=$(printf ${EM_129_PASSWORD} | base64) |
||
+ | SUM="${E__EM_129_DEVNAME}${S}${A__EM_129_SALT}" |
||
+ | LOGIN="$(printf "${SUM}" | sha1sum | awk '{ print $1}')" |
||
+ | SID="$(curl "${TOKEN_URL}${LOGIN}"| jq -r '.SID')" |
||
+ | |||
+ | |||
+ | ALL_DATA_URL="http://${IP}/${SID}/${ALL_DATA_URI}" |
||
+ | D=$(curl ${ALL_DATA_URL} | jq -r .) |
||
+ | if [ "${D}X" != "X" ]; |
||
+ | then |
||
+ | echo "${D}" |
||
+ | else |
||
+ | echo "{}" |
||
+ | exit 1 |
||
+ | fi |
||
+ | exit 0 |
||
+ | </PRE> |
Версия 14:18, 14 августа 2022
EM-129
В связи с нестабильностью питания решил наконец поставить хоть какое-то защитное реле что б не спалить технику
(в последний раз броски напряжения не пережил свитч Catalyst 3550 и я остался без PoE для все своих железок чем был сильно расстроен)
По EM-129 есть практически исчерпывающий обзор от Андрея, которому нет причин не доверять:
Однако в обзоре не упоминается ничего про программную часть устройства
Зачем?
Я принципиально не хочу отправлять свои данные в любой клауд, если это не мой клауд и привязывать устройство к чему-ьто в интернете мне кажется очень странной идеей
Пару слов об устройстве
Я не могу сказать ничего хорошего про реализацию безопастности EM-129
- Поддержка HTTPS отсутвует
- Кабельного подключения нет
Этих 2 факторов вполне достаточно что бы при желании можно было взломать сеть и отснифить пароль - в качестве "защиты" могу скаазть только что надо подойти достаточно близко что бы сниффить траффик.
Сигнал достаточно слабый хотя расстяние от места установки до точки доступа - примерно 5 метров и одна бетонная стена
/interface/wireless/registration-table/print Columns: INTERFACE, MAC-ADDRESS, AP, SIGNAL-STRENGTH, TX-RATE, UPTIME 2 wlan-2.4Ghz-ssid-infra-1 CC:50:E3:FA:83:55 no -81dBm@1Mbps 18Mbps 1d1h13m32s
Получение данных
О программной реализации интерфейса тоже можо сказать мало хорошего
Из плюсов могу отметить только то что инерфейс работает относительно быстро и не слишком уродлив, впрочем что и не удивительно - под капотом там JavaScript который только отсылает на устройство запросы данных, вся обработка и отрисовка происходит на стороне браузера
Из минусов - авторизация сделана максимально плохо
- Используется только пароль, без логина
- При авторизации создается Session ID (далее SID) который может быть только один и который по сути представляет собой префикс в URL для получения данных
- Прямое следствие этого - на устройство можно зайходить и смотреть только одному пользователю одновременно. При подключении второго SID меняется и первого "отлогинивает"
- BasicAuth был бы лучше в разы
Для того что бы понять как происходит авторизация пришлось немного покопаться в коде JS (что замечу говорит о "удачности" решения)
Авторизация происходит следующим образом
- Пользователь вводит пароль
- Браузер отправляет на устройство запрос что бы получить "соль" (случайное число) и тип устройства (похоже что аналогичный код используется не только в EM-129 но и других реле)
- На оснвани полученой соли, имени устройства и пользовательского пароля формируется "секретная строка" и отправляется на сервер
- Если строка верна то сервер возвращает SID
- ВСЕ!
Код скрипта
27 строк на bash
- IP - адрес реле
- SALT_URL - адрес куда посылать запрос на "соль"
- TOKEN_URL - адрес куда послылать запрос на SID
- ALL_DATA_URI - суффикс запроса данных
Секретная строка формируется следующим образом:
- sha1 от строки состоящей из 3 частей
- имя устройства - Пользовательский пароль закодированный в base64 - "соль"
По шагам
- A__EM_129_SALT="$(curl ${SALT_URL} | jq -r '.SALT')" - послать запрос на соль и взять поле с именем "SALT" из полученного json
- S=$(printf ${EM_129_PASSWORD} | base64) - закодировать пароль
- SUM="${E__EM_129_DEVNAME}${S}${A__EM_129_SALT}" - склеять строки
- LOGIN="$(printf "${SUM}" | sha1sum | awk '{ print $1}')" - взять sha1 от склейки строк
- SID="$(curl "${TOKEN_URL}${LOGIN}"| jq -r '.SID')" - отправить запрос на SID и взять из ответа значение поля SID
Получение данных дальше
- ALL_DATA_URL="http://${IP}/${SID}/${ALL_DATA_URI}" - сформировать адрес куда слать запрос с учетом SID
- D=$(curl ${ALL_DATA_URL} | jq -r .) получить данные
Данные получаются в виде json
{ "volt_msr": 2302, "freq_msr": 5000, "cur_msr": 321, "pows_msr": 740, "enrga_msr": 32,. "enrga_d_msr": 0, "enrga_w_msr": 0, "enrga_m_msr": 0, "enrgs_msr": 39, "sys_flag": 1409482896, "faul_flag": 0, "ar_time": 0, "cost_energy": 1700, "cost_unit": 16, "STATUS": "OK" }
Код скрипта
#!/bin/bash set -eu${DEBUG:+x} IP="192.168.31.250" SALT_URL="http://${IP}/api/login?salt" TOKEN_URL="http://${IP}/api/login?login=" ALL_DATA_URI="api/all/get?volt_msr&freq_msr&cur_msr&pows_msr&enrga_msr&enrga_d_msr&enrga_w_msr&enrga_m_msr&enrgs_msr&sys_flag&faul_flag&ar_time&cost_energy&cost_unit" A__EM_129_SALT="$(curl ${SALT_URL} | jq -r '.SALT')" E__EM_129_DEVNAME="EM-129" EM_129_PASSWORD="12345678" S=$(printf ${EM_129_PASSWORD} | base64) SUM="${E__EM_129_DEVNAME}${S}${A__EM_129_SALT}" LOGIN="$(printf "${SUM}" | sha1sum | awk '{ print $1}')" SID="$(curl "${TOKEN_URL}${LOGIN}"| jq -r '.SID')" ALL_DATA_URL="http://${IP}/${SID}/${ALL_DATA_URI}" D=$(curl ${ALL_DATA_URL} | jq -r .) if [ "${D}X" != "X" ]; then echo "${D}" else echo "{}" exit 1 fi exit 0