利用AWS Lambda和iOS捷徑實現手機一鍵開小區門禁 查找 API 第一次嘗試 分析登錄過程 使用 AWS Lambda 搭建服務 配置iOS捷徑 總結

我住的小區使用了一個叫守望領域的智能門禁系統,可以通過手機App開小區門禁和單元門,但是用App開門需要經過四五步:打開App→進入開門界面→找到需要開的門→點擊開門。

加上戴口罩時候解鎖手機需要輸入密碼,導致整個流程非常耗時,經常需要站在小區門口和單元門口操作半天,有一段時間我甚至養成了攜帶實體門禁卡的習慣,實體門禁卡開門要快很多。

最近又開始忘帶門禁卡,苦惱之餘發現iOS在鎖屏界面右劃可以免解鎖直接進入spotlight界面,這個界面可以添加捷徑,如果能寫一個捷徑去調用守望領域App的API開門,就可以實現手機免解鎖一鍵開門。

查找 API

首先需要通過Charles之類的軟件查找App調用的API,配置Charles查看App請求的方式不再贅述,Google一下可以看到很多教程。直接看結果Charles的結果,可以看到api.lookdoor.cn是這個軟件所請求的API域名。

打開軟件發的請求非常多,經過操作和請求的對比可以看到,發送開門指令調用的API是:/func/hjapp/house/v1/pushOpenDoorBySn.json?equipmentId=xxxxxx 這個路徑。

詳細查看這個請求可以發現,equipmentId指的就是小區門的Id,接口使用cookie做認證,只要將cookie帶上就可以模擬開門指令。

第一次嘗試

打開iOS捷徑App,創建一個新捷徑,App調用API使用了POST請求,搜索Get contents of這個動作來實現發送POST請求。

通過Charles找到要開的門的URL填入,Method選擇POST,Headers裏填入Cookie進行認證,內容直接從Charles複製就可以,嘗試運行,it works!

接下來把這個捷徑添加到Spotlight界面,鎖屏界面右劃點一下,就可以實現一鍵開小區門禁,和打開App的四五步操作相比,確實省時省力。拿着新配好的捷徑去上班,下班回到小區想試一把一鍵開門,結果又被困到門口了,上午還正常的捷徑竟然失效了,打開一看API報登錄超時,有可能是Cookie裏的SESSION_ID過期了。

分析登錄過程

再次用Charles抓包,分析登錄相關的API,會發現主要是這兩個:

  • /func/hjapp/user/v2/getPasswordAndKey.json:獲取AES Key的API
  • /func/hjapp/user/v2/login.json?password=xxxxxx:登錄API

通過分析,用時序圖來表示這部分的交互邏輯:

登錄過程清楚了,但是其中使用AES_KEY對密碼進行加密的配置還是不清楚的,使用一個工具來嘗試通過密文和AES_KEY來解密:http://tool.chacuo.net/cryptaes

輸入密鑰和密文,使用各種配置進行解密,當能夠解出內容的時候,證明我們找到了加密的配置,可以看到BlockSize=128,padder使用的是pkcs7padding,加密模式是ECB。解密出來的字符並不是我們的密碼,看着像是md5過的,用 echo -n xxxxxx | md5sum 把密碼md5一下,對上了。看來服務端校驗的是單次md5後的密碼。

到這裏登錄邏輯已經搞清了,但是iOS捷徑無法實現AES加密,單純依託捷徑來實現開門已經不可行了,需要搭建一個後端服務來計算密文。既然躲不過麻煩要搭建服務,不如把登錄、開門整個流程都放在服務上,這樣iOS捷徑只需要一個請求就可以完成開門動作了。

考慮到登錄開門的邏輯很簡單,也就是3個HTTP請求+AES加密,直接在裸服務器上從0搭建步驟多成本高,要自己申請虛機、部署HTTP Server、Web App,還需要申請SSL證書,不僅初次搭建要搞個一兩天,後續對機器和證書的維護也需要大量時間,成本極高。

最好是有服務能直接託管一段Python代碼,第一時間想到的是Leancloud,一個Serverless服務提供商,但是實操過程中發現,由於政策要求Leancloud已經不提供域名了,綁定自己的域名也需要進行備案。這意味着只能選擇一家海外Serverless服務商,看來看去AWS Lambda應該可以滿足要求,試一下。

使用 AWS Lambda 搭建服務

AWS Lambda是一個Serverless服務,可以直接託管一段函數,省去配置服務和基礎設施的麻煩。搭建一個Python的Serverless服務需要準備這麼幾件事:

  • 新建函數,編寫代碼
  • 添加API Gateway Trigger,確保函數可以通過HTTP請求調用
  • 配置函數的運行環境,增加一個層(Layer),這個層裏打包進AES加密需要的cryptography和HTTP請求需要的requests

1. 函數代碼

首先上代碼,需要填寫自己的手機號、md5後的密碼、設備ID(可以用Charles獲取)等字段,粘貼到Lambda的在線編輯器中。

import json
import requests
import base64
import urllib.parse
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding

PHONE = ''
PASSWORD_MD5 = ''
DEVICE_ID = ''

def encrypt(key, msg):
    cipher = Cipher(algorithms.AES(str.encode(key)), modes.ECB())
    encryptor = cipher.encryptor()
    padder = padding.PKCS7(128).padder()
    msg = padder.update(str.encode(msg)) + padder.finalize()
    ct = encryptor.update(msg) + encryptor.finalize()
    return base64.b64encode(ct)

def lambda_handler(event, context):
    resp = requests.post('https://api.lookdoor.cn:443/func/hjapp/user/v2/getPasswordAesKey.json?')
    cookie = resp.headers['set-cookie']
    aes_key = resp.json()['data']['aesKey']
    password_encypted = urllib.parse.quote_plus(encrypt(aes_key, PASSWORD_MD5))
    
    url = f'https://api.lookdoor.cn:443/func/hjapp/user/v2/login.json?password={password_encypted}&deviceId={DEVICE_ID}&loginNumber={PHONE}&equipmentFlag=1'
    requests.post(url, headers={'cookie': cookie})
    
    equipment_id = event['queryStringParameters']['equipment_id']
    url = f'https://api.lookdoor.cn:443/func/hjapp/house/v1/pushOpenDoorBySn.json?equipmentId={equipment_id}'
    resp = requests.post(url, headers={'cookie': cookie})
    return resp.json()

代碼首先通過API獲取AES_KEY和SESSION_ID,然後使用AES_KEY對密碼進行加密,接下來調用登錄接口將獲取的SESSION_ID綁定到當前賬戶,接下來根據請求傳入的設備ID(門的ID)來發送開門指令。

點擊Deploy部署,然後運行測試,會出現超時的報錯,這是因爲Lambda函數默認的執行器內存大小是128MB,超時時間是3s,在配置頁面把內存改大一些,超時時間設置爲10s就可以了。

2. 添加 API Gateway Trigger

一個Lambda函數可以被多種形式觸發執行,因爲要使用捷徑通過HTTP請求調用,所以加一個API Gateway Trigger,添加後會自動爲函數生成一個URL,通過這個URL就可以直接調用函數。

3. 添加包含依賴的 Layer

代碼中使用了 requests 和 cryptography 這兩個第三方庫,Lambda不支持使用pip直接安裝這些依賴,而是需要我們在把依賴打成zip包上傳成爲容器的一層Layer,添加到函數鏡像中。需要注意的是,Lambda函數執行的環境是Linux,對於cryptography這個庫需要打包Linux版的纔可以正常使用。

由於日常使用的是Mac,所以在AWS上申請一臺Ubuntu 20的EC2實例,登錄實例後使用如下命令安裝依賴,並打包成zip文件:

mkdir python
pip install -t python cryptography
pip install -t python requests
zip -r python/*

在AWS上創建一個新的Layer,並將生成的python.zip上傳到Layer上。嘗試通過URL訪問寫好的Lambda函數,可以看到開門指令已經成功下發。

配置iOS捷徑

打開iOS捷徑App,創建一個新捷徑,搜索Get contents of這個動作,填入Lambda函數的URL和門的ID。由於API Gateway並沒有配置認證,所以其他參數默認即可。如果有安全方面的顧慮,可以自己實現一個簡單的Token認證或添加Lambda提供的JWT認證。點擊執行,接口返回成功,證明整個流程已經跑通,以後就可以用這個捷徑給自己和外賣小哥開門了。

總結

一開始本想用自定義一個iOS捷徑的方式來實現一鍵開門禁,但爲了實現SESSION_ID自動更新,不得不基於AWS Lambda搭了一個後端服務來模擬App的行爲,所幸AWS Lambda提供了低成本的構建方案,包括搭建服務和配置SSL證書都可以幾乎0成本的完成,免費套餐政策也能讓這個服務長期跑着而不產生任何實際花費。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章