于炳虎 蹇詩婕 公安部第一研究所
近年來,公安移動(dòng)警務(wù)應(yīng)用簽名與備案工作有序推進(jìn),根據(jù)公安部關(guān)于移動(dòng)警務(wù)應(yīng)用上線的安全要求規(guī)定,所有移動(dòng)警務(wù)應(yīng)用均應(yīng)通過應(yīng)用簽名認(rèn)證才能正式上線使用。同時(shí),移動(dòng)警務(wù)終端需使用應(yīng)用驗(yàn)簽功能對應(yīng)用安裝文件的合法性、合規(guī)性以及簽名信息進(jìn)行驗(yàn)證,驗(yàn)證通過后予以放行安裝。通過對上線應(yīng)用進(jìn)行簽名、驗(yàn)簽,防范應(yīng)用安裝文件出現(xiàn)偽造、抵賴、冒充和篡改等安全問題。
目前,基于安卓系統(tǒng)原生應(yīng)用的移動(dòng)警務(wù)簽名備案系統(tǒng)已正式運(yùn)行兩年多,支撐全國公安用戶完成了幾千款原生應(yīng)用的簽名認(rèn)證工作,但是當(dāng)前工作缺少針對移動(dòng)警務(wù)HTML5(簡稱H5)輕應(yīng)用的簽名驗(yàn)簽功能。另一方面,H5輕應(yīng)用具有開發(fā)周期短、成本低、兼容性強(qiáng)、迭代速度快等特點(diǎn),提升了應(yīng)用場景的靈活性,移動(dòng)警務(wù)H5輕應(yīng)用市場愈發(fā)繁榮。本文研究設(shè)計(jì)的H5輕應(yīng)用簽名驗(yàn)簽機(jī)制,彌補(bǔ)了當(dāng)前移動(dòng)警務(wù)應(yīng)用簽名與備案工作的空白,滿足應(yīng)用安全體系建設(shè)的急迫需求。
H5輕應(yīng)用的開發(fā)工具鏈眾多,例如前端框架包含AngularJS、React、VueJS等,打包工具包含Webpack、Rollup、Parcel等,打包后程序結(jié)構(gòu)復(fù)雜多樣[1],簽名驗(yàn)簽機(jī)制難以參考安卓原生應(yīng)用。安卓原生應(yīng)用以APK格式固定,具有安裝校驗(yàn)流程,由操作系統(tǒng)執(zhí)行。而H5輕應(yīng)用無需安裝,由Web容器加載,程序文件按需從服務(wù)器拉取至客戶端,由客戶端引擎執(zhí)行加載渲染功能。因此在客戶端無法一次性對H5程序文件進(jìn)行全量驗(yàn)證操作。
H5輕應(yīng)用程序主要由HTML標(biāo)簽文件、CSS樣式文件、JavaScript(簡稱JS)腳本文件和其他資源文件組成,其中JS文件為核心的業(yè)務(wù)邏輯程序,其他均可歸類為靜態(tài)文件。靜態(tài)文件只用于頁面的排版展示,通常不會對系統(tǒng)或用戶造成安全威脅,因此可忽略此類文件的處理。為解決H5輕應(yīng)用簽名驗(yàn)簽問題,本文提出了一種新的簽名驗(yàn)簽機(jī)制,僅對JS文件進(jìn)行完整性驗(yàn)證和防篡改保護(hù),從而實(shí)現(xiàn)H5輕應(yīng)用的簽名驗(yàn)簽功能。
應(yīng)用簽名服務(wù)部署于簽名服務(wù)側(cè),對H5程序包進(jìn)行拆包解析,遍歷分析JS文件。針對每個(gè)JS文件,首先使用移動(dòng)警務(wù)應(yīng)用簽名專用證書,基于國密商用證書和算法,對JS文件進(jìn)行簽名處理,生成簽名數(shù)據(jù);然后將生成后的簽名數(shù)據(jù)合入至原JS代碼中的指定位置,添加移動(dòng)警務(wù)簽名校驗(yàn)標(biāo)識,也可根據(jù)需求擴(kuò)展添加自定義數(shù)據(jù)。上述操作中合入的代碼數(shù)據(jù)以不影響原代碼的執(zhí)行邏輯為原則。
完成上述JS文件的二次處理后,重新按原格式對程序進(jìn)行打包,該過程不應(yīng)改變原程序包的架構(gòu),不能影響原程序的代碼執(zhí)行。H5輕應(yīng)用簽名流程如圖1所示。
應(yīng)用驗(yàn)簽組件需集成安裝在設(shè)備客戶端側(cè),客戶端可以是基座或?yàn)g覽器,統(tǒng)稱為Web容器。H5輕應(yīng)用基于Web架構(gòu)運(yùn)行使用,按需逐個(gè)從Web服務(wù)器拉取JS程序文件至本地客戶端Web容器,通過JS引擎加載執(zhí)行。
Web容器集成驗(yàn)簽組件,對每一個(gè)拉取至本地容器的JS文件執(zhí)行數(shù)據(jù)驗(yàn)簽與屬性校驗(yàn)。驗(yàn)簽組件邏輯基于JS代碼實(shí)現(xiàn),具有跨平臺、免安裝、兼容性好等優(yōu)點(diǎn)[2]。若驗(yàn)簽執(zhí)行失敗,則禁止加載對應(yīng)JS文件,并向用戶給出相應(yīng)提示。若驗(yàn)簽執(zhí)行通過,則允許加載執(zhí)行對應(yīng)JS文件。H5輕應(yīng)用驗(yàn)簽流程如圖2所示。
JavaScript是一種具有函數(shù)優(yōu)先的輕量級、解釋型或即時(shí)編譯型的高級編程語言。在H5程序中JS承擔(dān)著與服務(wù)端的通信交互以及客戶端的邏輯處理等多項(xiàng)重要功能。因此,對JS文件進(jìn)行解析和重構(gòu)是H5輕應(yīng)用簽名驗(yàn)簽機(jī)制實(shí)現(xiàn)的關(guān)鍵,核心要求是不能破壞原程序代碼的邏輯功能。其中,涉及的JS技術(shù)原理包括JS代碼的解析重構(gòu)和代碼保護(hù)技術(shù)。
在簽名驗(yàn)簽的邏輯處理過程中,需保證JS代碼的功能完整性。JS代碼的解析是一項(xiàng)關(guān)鍵技術(shù),JS不僅是一門“解釋型”語言,也是一種使用特殊預(yù)編譯機(jī)制的編譯語言[3]。
JS程序中源代碼的執(zhí)行分為詞法分析、語法分析、代碼生成三個(gè)步驟。詞法分析包括分析參數(shù)、分析變量聲明、分析函數(shù)聲明。語法分析通過構(gòu)造抽象語法樹AST來抽象表示源代碼語法結(jié)構(gòu),以樹狀形式表現(xiàn)編程語言的語法結(jié)構(gòu)。AST在代碼生成階段被轉(zhuǎn)換為可執(zhí)行代碼。通過代碼解析可重構(gòu)JS代碼內(nèi)容,并且不破壞原代碼邏輯。
對簽名后的代碼數(shù)據(jù)采取多重安全策略,多層次保護(hù)代碼數(shù)據(jù)。通過詞法分析和語法分析分離出變量、常量、函數(shù)、關(guān)鍵字等,對變量名進(jìn)行變形,將字符串陣列化、加密,從而使賦值、讀寫等邏輯不易被察覺。再通過組合拆分、隨機(jī)插入僵尸代碼、打亂代碼流程邏輯等方式,使代碼不可被理解。最后結(jié)合反調(diào)試、域名鎖定代碼等操作,使代碼不可被分析。
采取上述技術(shù)手段對JS代碼進(jìn)行保護(hù),在不改變JS代碼原始邏輯功能的情況下,新增加密等保護(hù)措施,使其具備不可讀、不可分析、不可調(diào)試、不可篡改、不可盜用等功能。
本文通過代碼實(shí)例驗(yàn)證H5輕應(yīng)用簽名驗(yàn)簽機(jī)制的可行性。以某業(yè)務(wù)辦理的電子化流程為例,案例工程的基本功能包括業(yè)務(wù)申請、審批、辦理和完成等,用戶通過移動(dòng)警務(wù)終端使用服務(wù)的各項(xiàng)功能??蛻舳顺绦蚴褂弥髁髑岸丝蚣躒ueJS,基于H5實(shí)現(xiàn)。案例工程使用了當(dāng)前主流的前端程序架構(gòu),具有通用性和代表性。
對上述案例代碼程序進(jìn)行打包,將所有部署文件置于項(xiàng)目根目錄下的dist目錄中,展開dist目錄,可查看到如圖3所示的代碼文件。
應(yīng)用程序包包含:入口文件index.html、圖標(biāo)文件favicon.ico和static目錄。其中,static包含css樣式文件、fonts字體文件、img圖片文件和JS邏輯文件,除JS文件外,其他均為靜態(tài)資源文件。
1. 拆包遍歷與分析
將程序包dist壓縮為zip文件,提交至簽名服務(wù)側(cè)申請簽名。簽名服務(wù)獲取dist.zip文件后,解壓zip文件,并遍歷以js結(jié)尾的文件,針對每一個(gè)JS文件,進(jìn)行詞法分析和語法分析,分離出變量、常量、函數(shù),生成臨時(shí)JS文件。
2. 代碼保護(hù)與重構(gòu)
處理第一步生成的臨時(shí)JS文件,對代碼文件中的變量名進(jìn)行混淆重命名,對字符串進(jìn)行陣列化處理,并對陣列化處理結(jié)果使用國密算法SM1進(jìn)行加密。經(jīng)上述處理后,壓縮代碼生成第一次重構(gòu)后的JS文件。
3. 計(jì)算文件簽名值
使用國密算法SM3/SM2對第一次重構(gòu)后的JS文件進(jìn)行全量簽名,長度為1334~1380字節(jié)(依賴填充長度),生成簽名值SDT。
4. 計(jì)算校驗(yàn)標(biāo)識
校驗(yàn)標(biāo)識長度定義為256位,按順序排列組成。固定標(biāo)識為16字節(jié),簽名算法和版本信息為40字節(jié),簽名屬性校驗(yàn)數(shù)據(jù)為64字節(jié),簽名數(shù)據(jù)長度為8字節(jié)。為防止校驗(yàn)數(shù)據(jù)被篡改偽造,加入128字節(jié)的校驗(yàn)標(biāo)識數(shù)據(jù)的簽名值SJT。其中,簽名屬性校驗(yàn)數(shù)據(jù)包括來源、有效期等多個(gè)簽名屬性值和證書屬性值,如圖4所示。
5. 合入生成簽名文件
生成一個(gè)新的臨時(shí)JS文件,按順序插入校驗(yàn)標(biāo)識、簽名值SDT,再插入16字節(jié)固定長度的簽名魔數(shù)值,此為自定義擴(kuò)展數(shù)據(jù),用于配合校驗(yàn)標(biāo)識查找驗(yàn)證簽名數(shù)據(jù),最后插入經(jīng)代碼保護(hù)處理后的JS原始代碼。完成上述四部分?jǐn)?shù)據(jù)合入后,進(jìn)行二次壓縮,生成第二次重構(gòu)后的JS文件,整體代碼結(jié)果如圖5所示。
將驗(yàn)簽組件集成至客戶端Web容器的JS引擎中或以瀏覽器JS插件形式集成,客戶端每拉取一個(gè)JS文件時(shí),都對該文件進(jìn)行驗(yàn)簽,驗(yàn)簽通過后即可加載運(yùn)行。由于客戶端容器種類較多,實(shí)現(xiàn)原理大致相同,本文不再詳細(xì)解讀各類型容器集成驗(yàn)簽組件的方法,具體可參考瀏覽器集成JS插件的方式。接下來本文對核心的驗(yàn)簽邏輯和算法進(jìn)行梳理,針對上述簽名實(shí)現(xiàn)的案例程序,驗(yàn)簽流程包含五步,如圖6所示。
(1)Web容器獲取到加載JS文件的事件時(shí),驗(yàn)簽服務(wù)組件截獲JS文件進(jìn)行處理;
(2)獲取256位固定長度的校驗(yàn)標(biāo)識數(shù)據(jù),分割處理并驗(yàn)證校驗(yàn)數(shù)據(jù)是否有效,驗(yàn)證通過后,對校驗(yàn)標(biāo)識數(shù)據(jù)前128位進(jìn)行處理,解析得到簽名屬性數(shù)據(jù)和簽名值SDT的數(shù)據(jù)長度,計(jì)算簽名屬性校驗(yàn)數(shù)據(jù),判斷是否過期等;
(3)完成上述校驗(yàn)后,根據(jù)簽名值SDT的數(shù)據(jù)長度按序讀取指定長度的數(shù)據(jù),即為簽名值數(shù)據(jù),此時(shí)判斷后16位數(shù)據(jù)是否為簽名魔數(shù)值;
(4)通過上述所有校驗(yàn)后,移除校驗(yàn)位數(shù)據(jù)、簽名數(shù)據(jù)、簽名魔數(shù)值,得到JS文件代碼數(shù)據(jù);
(5)調(diào)用驗(yàn)簽方法,校驗(yàn)簽名數(shù)據(jù)是否正確,通過則允許加載執(zhí)行,失敗則拒絕執(zhí)行、并向用戶發(fā)送提示信息。
第四節(jié)通過實(shí)例驗(yàn)證了機(jī)制的可行性,從簽名服務(wù)側(cè)到應(yīng)用驗(yàn)簽側(cè)完成流程閉環(huán),保證設(shè)計(jì)可落地實(shí)現(xiàn)。同時(shí),在此基礎(chǔ)上,在簽名服務(wù)側(cè),增加多重代碼安全策略,進(jìn)一步加固代碼程序。主要安全策略包括:陣列字符串加密、僵尸代碼植入、瀏覽器反調(diào)試和代碼域名綁定。本節(jié)以上述案例工程中的app.js文件為例,對四種保護(hù)策略的實(shí)際效果進(jìn)行介紹。app.js原始代碼如圖7所示,為方便對比查看,未對代碼進(jìn)行壓縮并已完成脫敏處理。
陣列本質(zhì)為矩陣或數(shù)組。將JS代碼中的字符串進(jìn)行陣列化放置,可使賦值、讀寫等邏輯更不易被察覺。在簽名服務(wù)側(cè)對app.js文件進(jìn)行陣列字符串加密處理,處理結(jié)果如圖8所示。
通過向源代碼中植入隨機(jī)、無序或誤導(dǎo)性的冗余代碼,在不影響整個(gè)代碼邏輯的前提下,擾亂源代碼的整體結(jié)構(gòu),達(dá)到阻礙閱讀理解代碼的效果。在簽名服務(wù)側(cè)對app.js文件進(jìn)行僵尸代碼植入處理,處理結(jié)果如圖9所示。
瀏覽器反調(diào)試也稱為JS反調(diào)試。檢測到控制臺開啟后,可進(jìn)入反調(diào)試邏輯,在網(wǎng)頁打開調(diào)試窗口后會持續(xù)處于debugger模式,禁止調(diào)試JS代碼,進(jìn)入調(diào)試模式后的效果如圖10所示。
在JS文件頭部添加域名校驗(yàn)邏輯,綁定配置域名后,代碼只能在指定域名下運(yùn)行。如果在非目標(biāo)域名或IP的地址下請求加載和執(zhí)行JS代碼,則會拋出異常,無法正常使用。此方法可在一定程度上保護(hù)代碼不被盜用,發(fā)揮防護(hù)作用。本地測試加載JS后直接由localhost(非綁定域名)跳轉(zhuǎn)至空頁面,跳轉(zhuǎn)效果如圖11所示。
相比原生應(yīng)用,H5輕應(yīng)用具有輕量級、跨平臺、成本低、迭代快和開發(fā)周期短等特點(diǎn),在移動(dòng)警務(wù)應(yīng)用體系的建設(shè)中,越來越多的開發(fā)單位選用此種開發(fā)模式。本文基于H5輕應(yīng)用研究設(shè)計(jì)的簽名驗(yàn)簽機(jī)制與安全保護(hù)方法,應(yīng)用場景廣泛,有助于推動(dòng)移動(dòng)警務(wù)應(yīng)用安全體系的建設(shè)與管理。
基于JS實(shí)現(xiàn)的簽名驗(yàn)簽機(jī)制可對H5輕應(yīng)用程序包進(jìn)行來源性檢查和完整性驗(yàn)證,滿足移動(dòng)警務(wù)應(yīng)用上線安全要求,對源代碼進(jìn)行重構(gòu),使用國密證書及算法對代碼文件進(jìn)行數(shù)字簽名,增加校驗(yàn)標(biāo)識,防止代碼二次篡改。
代碼域名綁定使得JS代碼文件只能在指定的域名或IP下加載執(zhí)行,復(fù)制或盜用到其他網(wǎng)站下則無法加載執(zhí)行,有效保證了代碼文件的防盜性。對于公安業(yè)務(wù)系統(tǒng),權(quán)威性和唯一性是兩大重要特性,需重點(diǎn)防范代碼被非法盜用。
通過對JS源代碼采取字符串加密、字符串陣列化、僵尸代碼植入等防護(hù)策略,保證JS核心代碼難以被破解和查看。防護(hù)后的JS代碼為不可逆、不可分析、不可復(fù)制的安全代碼,從而顯著降低非法篡改、惡意利用、惡意攻擊等安全風(fēng)險(xiǎn)。
本文從實(shí)際需求出發(fā),研究設(shè)計(jì)了一套基于移動(dòng)警務(wù)H5輕應(yīng)用的簽名驗(yàn)簽機(jī)制。通過梳理H5輕應(yīng)用的程序包結(jié)構(gòu),分析代碼構(gòu)成,闡述技術(shù)原理,從應(yīng)用簽名保護(hù)到驗(yàn)簽認(rèn)證進(jìn)行完整閉環(huán)設(shè)計(jì)。方案設(shè)計(jì)充分考慮了兼容性、穩(wěn)定性、安全性和適配性,通過多重保護(hù)策略,實(shí)現(xiàn)對程序代碼的多層防護(hù),從而顯著降低非法篡改、惡意利用、惡意攻擊等風(fēng)險(xiǎn)。