當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]之前在某廠的某次項(xiàng)目開(kāi)發(fā)中,項(xiàng)目組同學(xué)設(shè)計(jì)和實(shí)現(xiàn)了一個(gè)“引以為傲”,額,有點(diǎn)夸張,不過(guò)自認(rèn)為還說(shuō)得過(guò)去的 feature,結(jié)果臨上線前被啪啪打臉,因?yàn)閷?shí)現(xiàn)過(guò)程中因?yàn)橐恍写a(沒(méi)有標(biāo)題黨,真的是一行代碼)帶來(lái)的安全漏洞讓我們丟失了整個(gè)服務(wù)器控制權(quán)(測(cè)

一行代碼引來(lái)的安全漏洞就讓我們丟失了整個(gè)服務(wù)器的控制權(quán)



之前在某廠的某次項(xiàng)目開(kāi)發(fā)中,項(xiàng)目組同學(xué)設(shè)計(jì)和實(shí)現(xiàn)了一個(gè)“引以為傲”,額,有點(diǎn)夸張,不過(guò)自認(rèn)為還說(shuō)得過(guò)去的 feature,結(jié)果臨上線前被啪啪打臉,因?yàn)閷?shí)現(xiàn)過(guò)程中因?yàn)?/span>一行代碼(沒(méi)有標(biāo)題黨,真的是一行代碼)帶來(lái)的安全漏洞讓我們丟失了整個(gè)服務(wù)器控制權(quán)(測(cè)試環(huán)境)。多虧了上線之前有公司安全團(tuán)隊(duì)的人會(huì)對(duì)代碼進(jìn)行掃描,才讓這個(gè)漏洞被扼殺在搖籃里。


下面我們就一起來(lái)看看這個(gè)事故,啊,不對(duì),是故事。


背景說(shuō)明

我們的項(xiàng)目是一個(gè)面向全球用戶的 Web 項(xiàng)目,用 SpringBoot 開(kāi)發(fā)。在項(xiàng)目開(kāi)發(fā)過(guò)程中,離不開(kāi)各種異常信息的處理,比如表單提交參數(shù)不符合預(yù)期,業(yè)務(wù)邏輯的處理時(shí)離不開(kāi)各種異常信息(例如網(wǎng)絡(luò)抖動(dòng)等)的處理。于是利用 SpringBoot 各種現(xiàn)成的組件支持,設(shè)計(jì)了一個(gè)統(tǒng)一的異常信息處理組件,統(tǒng)一管理各種業(yè)務(wù)流程中可能出現(xiàn)的錯(cuò)誤碼和錯(cuò)誤信息,通過(guò)國(guó)際化的資源配置文件進(jìn)行統(tǒng)一輸出給用戶。



統(tǒng)一錯(cuò)誤信息配置管理

我們的用戶遍布全球,為了給各個(gè)國(guó)家用戶比較好的體驗(yàn)會(huì)進(jìn)行不同的翻譯。具體而言,實(shí)現(xiàn)的效果如下,為了方便理解,以“找回登錄密碼”這樣一個(gè)業(yè)務(wù)場(chǎng)景來(lái)進(jìn)行闡述說(shuō)明。

假設(shè)找回密碼時(shí),需要用戶輸入手機(jī)或者郵箱驗(yàn)證碼,假設(shè)這個(gè)時(shí)候用戶輸入的驗(yàn)證碼通過(guò)后臺(tái)數(shù)據(jù)庫(kù)(可能是Redis)對(duì)比發(fā)現(xiàn)已經(jīng)過(guò)期。在業(yè)務(wù)代碼中,只需要簡(jiǎn)單的 throw new ErrorCodeException(ErrorCodes.AUTHCODE_EXPIRED)即可。具體而言,針對(duì)不同國(guó)家地區(qū)不同的語(yǔ)言看到的效果不一樣:

  • 中文用戶看到的提示就是“您輸入的驗(yàn)證碼已過(guò)期,請(qǐng)重新獲取”;
  • 歐美用戶看到的效果是“The verification code you input is expired, ...”;
  • 德國(guó)用戶看到的是:“Der von Ihnen eingegebene Verifizierungscode ist abgelaufen, bitte wiederholen” 。(我瞎找的翻譯,不一定準(zhǔn))
  • ……


統(tǒng)一錯(cuò)誤信息配置管理代碼實(shí)現(xiàn)

關(guān)鍵信息其實(shí)就在于一個(gè) GlobalExceptionHandler,對(duì)所有 Controller 入口進(jìn)行 AOP 攔截,根據(jù)不同的錯(cuò)誤信息,獲取相應(yīng)資源文件配置的 key,并從語(yǔ)言資源文件中讀取不同國(guó)家的錯(cuò)誤翻譯信息。

@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BadRequestException.class) @ResponseBody public ResponseEntity handle(HttpServletRequest request, BadRequestException e){
        String i18message = getI18nMessage(e.getKey(), request); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Response.error(e.getCode(), i18message));
    } @ExceptionHandler(ErrorCodeException.class) @ResponseBody public ResponseEntity handle(HttpServletRequest request, ErrorCodeException e){
        String i18message = getI18nMessage(e.getKey(), request); return ResponseEntity.status(HttpStatus.OK).body(Response.error(e.getCode(), i18message));
    }
}
不同語(yǔ)言的資源文件示例
private String getI18nMessage(String key, HttpServletRequest request) { try { return messageSource.getMessage(key, null, LanguaggeUtils.currentLocale(request));
   } catch (Exception e) { // log return key;
   }
}




基于注解的表單校驗(yàn)(含自定義注解)

還有一種常見(jiàn)的業(yè)務(wù)場(chǎng)景就是后端接口需要對(duì)用戶提交的表單進(jìn)行校驗(yàn)。以“注冊(cè)用戶”這樣的場(chǎng)景舉例說(shuō)明, 注冊(cè)用戶時(shí),往往會(huì)提交昵稱,性別,郵箱等信息進(jìn)行注冊(cè),簡(jiǎn)單起見(jiàn),就以這 3 個(gè)屬性為例。


定義的表單如下:

public class UserRegForm { private String nickname; private String gender; private String email;
}

對(duì)于表單的約束,我們有:

  • 昵稱字段:“nickname” 必填,長(zhǎng)度必須是 6 到 20 位;
  • 性別字段:“gender” 可選,如果填了,就必須是“Male/Female/Other/”中的一種。(說(shuō)啥,除了男女還有其他?對(duì),是的。畢竟全球用戶嘛,你去看看非死不可,還有更多。)
  • 郵箱:“email”,必填,必須滿足郵箱格式。

對(duì)于以上約束,我們只需要在對(duì)應(yīng)的字段上添加如下注解即可。

public class UserRegForm { @Length(min = 6, max = 20, message = "validate.userRegForm.nickname") private String nickname; @Gender(message="validate.userRegForm.gender") private String gender; @NotNull @Email(message="validate.userRegForm.email") private String email;
}

然后在各個(gè)語(yǔ)言資源文件中配置好相應(yīng)的錯(cuò)誤信息提示即可。其中, @Gender 就是一個(gè)自定義的注解。



基于含自定義注解的表單校驗(yàn)關(guān)鍵代碼

自定義注解的實(shí)現(xiàn)主要的其實(shí)就是一個(gè)自定義注解的定義以及一個(gè)校驗(yàn)邏輯。例如定義一個(gè)自定義注解 CustomParam

@Documented @Constraint(validatedBy = CustomValidator.class) @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface CustomParam { String message() default "name.tanglei.www.validator.CustomArray.defaultMessage";

    Class[] groups() default {};
    Class[] payload() default { }; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE}) @interface List {
        CustomParam[] value();
    }
}

校驗(yàn)邏輯的實(shí)現(xiàn) CustomValidator

public class CustomValidator implements ConstraintValidator<CustomParam, String> { @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { if (null == s || s.isEmpty()) { return true;
        } if (s.equals("tanglei")) { return true;
        } else {
            error(constraintValidatorContext, "Invalid params: " + s); return false;
        }
    } @Override public void initialize(CustomParam constraintAnnotation) {
    } private static void error(ConstraintValidatorContext context, String message) {
        context.disableDefaultConstraintViolation();
        context.buildConstraintViolationWithTemplate(message).addConstraintViolation();
    }
}

上面例子只為了闡述說(shuō)明問(wèn)題,其中校驗(yàn)邏輯沒(méi)有實(shí)際意義,這樣,如果輸入?yún)?shù)不滿足條件,就會(huì)明確提示用戶輸入的哪個(gè)參數(shù)不滿足條件。例如輸入?yún)?shù)xx,則會(huì)直接提示:Invalid params: xx。

這個(gè)跟第一部分的處理方式類(lèi)似,因?yàn)楝F(xiàn)有的 validator 組件實(shí)現(xiàn)中,如果違反相應(yīng)的約束也是一種拋異常的方式實(shí)現(xiàn)的,因此只需要在上述的 GlobalExceptionHandler中添加相應(yīng)的異常信息即可,這里就不詳述了。這不是本文的重點(diǎn),這里就不詳細(xì)闡述了。

場(chǎng)景重現(xiàn)

一切都顯得很完美,直到上線前代碼提交至安全團(tuán)隊(duì)掃描,就被“啪啪打臉”,掃描報(bào)告反饋了一個(gè)嚴(yán)重的安全漏洞。而這個(gè)安全漏洞,屬于很高危的遠(yuǎn)程代碼執(zhí)行漏洞。

用前文提到的自定義 Validator,輸入的參數(shù)用:“1+1=${1+1}”。

太 TM 神奇了,居然幫我運(yùn)算出來(lái)了,返"message": "Invalid params: 1+1=2"。

問(wèn)題就出現(xiàn)在實(shí)現(xiàn)自定義注解進(jìn)行校驗(yàn)的這行代碼,其實(shí),最開(kāi)始的時(shí)候,這里直接返回了Invalid params”,當(dāng)初為了更好的用戶體驗(yàn),要明確告訴用戶哪個(gè)參數(shù)沒(méi)有通過(guò)校驗(yàn),因此在輸出的提示上加上了用戶輸入的字段,也就是上面的"Invalid params: " + s,沒(méi)想到,這闖了大禍了(回過(guò)頭來(lái)想,感覺(jué)這里沒(méi)必要這么詳細(xì)啊,因?yàn)榍岸艘呀?jīng)有相應(yīng)的校驗(yàn)了,正常情況下回?cái)r住,針對(duì)不守規(guī)矩的用非常規(guī)手段來(lái)的接口請(qǐng)求,直接返回校驗(yàn)不通過(guò)就行了,畢竟不是對(duì)外提供的 OpenAPI 服務(wù))。

仔細(xì)看,這個(gè)方法實(shí)際上是ConstraintValidatorContext這個(gè)接口中聲明的,看方法名字其實(shí)能知道輸入?yún)?shù)是一個(gè)字符串模板,內(nèi)部會(huì)進(jìn)行解析替換的(這其實(shí)也符合“見(jiàn)名知意”的良好編程習(xí)慣)。(教訓(xùn):大家應(yīng)該把握好自己寫(xiě)的每一行代碼背后實(shí)際在做什么。)

/* ......
 * @param messageTemplate new un-interpolated constraint message
 * @return returns a constraint violation builder
 */ ConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate);

這個(gè) case,源碼調(diào)試進(jìn)去之后,就能跟蹤到執(zhí)行翻譯階段,在如下方法中:org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator.interpolateMessage。


再往后,就是表達(dá)式求值了。

以為就這樣就完了嗎?


剛開(kāi)始感覺(jué),能幫忙算簡(jiǎn)單的運(yùn)算規(guī)則也就完了吧,你還能把我怎么樣?其實(shí)這個(gè)相當(dāng)于暴露了一個(gè)入口,支持用戶輸入任意 EL 表達(dá)式進(jìn)行執(zhí)行。網(wǎng)上通過(guò)關(guān)鍵字 “SpEL表達(dá)式注入漏洞” 找找,就能發(fā)現(xiàn)事情并沒(méi)有想象中那么簡(jiǎn)單。

我們構(gòu)造恰當(dāng)?shù)?EL 表達(dá)式(注意各種轉(zhuǎn)義,下文的輸入?yún)?shù)相對(duì)比較明顯在做什么了,實(shí)際上還有更多黑科技,比如各種進(jìn)制轉(zhuǎn)義編碼啊等等),就能直接執(zhí)行輸入代碼,例如:可以直接執(zhí)行命令,“l(fā)s -al”, 返回了一個(gè) UNIXProcess 實(shí)例,命令已經(jīng)被執(zhí)行過(guò)了。

比如,我們執(zhí)行個(gè)打開(kāi)計(jì)算器的命令,搞個(gè)計(jì)算器玩玩~

我錄制了一個(gè)動(dòng)圖,來(lái)個(gè)演示可能更生動(dòng)一些。

這還得了嗎?這相當(dāng)于直接在公網(wǎng)上提供了一個(gè) WebShell 的功能呀,你看,想運(yùn)行啥命令就能運(yùn)行啥命令,例如 ping 本人博客地址(ping www.tanglei.name),下面動(dòng)圖(gif 圖上傳總是失敗,試試微信公眾號(hào)嵌入視頻演示一下整個(gè)過(guò)程(從運(yùn)行 ping 到 kill ping)。

這樣豈不是直接創(chuàng)建一個(gè)用戶,然后遠(yuǎn)程登錄就可以了。后果非常嚴(yán)重啊,別人想干嘛就干嘛了。


漏洞根因

我們跟蹤下對(duì)應(yīng)的代碼,看看內(nèi)部實(shí)現(xiàn),就會(huì)“恍然大悟”了。


經(jīng)驗(yàn)教訓(xùn)

幸虧這個(gè)漏洞被扼殺在搖籃里,否則后果還真的挺嚴(yán)重的。通過(guò)這個(gè)案例,我們有啥經(jīng)驗(yàn)和教訓(xùn)呢?那就是作為程序員,我們要對(duì)每一行代碼都保持“敬畏”之心。也許就是因?yàn)槟愕牟唤?jīng)意的一行代碼就帶來(lái)了嚴(yán)重的安全漏洞,要是不小心被壞人利用,輕則……重則……(自己想象吧)


此外,我們也應(yīng)該看到,程序員需要對(duì)常見(jiàn)的安全漏洞(例如XSS/CSRF/SQL注入等等)有所了解,并且要有足夠的安全意識(shí)(其實(shí)有時(shí)候研究一些安全問(wèn)題還挺好玩的)。例如:

  • 用戶權(quán)限分離:運(yùn)行程序的用戶不應(yīng)該用 root,例如新建一個(gè)“web”或者“www”之類(lèi)的用戶,并設(shè)置該用戶的權(quán)限,比如不能有可執(zhí)行 xx 的權(quán)限之類(lèi)的。本文 case,如果權(quán)限進(jìn)行了分離(遵循最小權(quán)限原則),應(yīng)該也不會(huì)這么嚴(yán)重。(本文就剛好是因?yàn)槭菧y(cè)試環(huán)境,所以沒(méi)有強(qiáng)制實(shí)施)
  • 任何時(shí)候都不要相信用戶的輸入,必須對(duì)用戶輸入的進(jìn)行校驗(yàn)和過(guò)濾,又特別是針對(duì)公網(wǎng)上的應(yīng)用。
  • 敏感信息加密保存。退一萬(wàn)步講,假設(shè)攻擊者攻入了你的服務(wù)器,如果這個(gè)時(shí)候,你的數(shù)據(jù)庫(kù)賬戶信息等配置都直接明文保存在服務(wù)器中。那數(shù)據(jù)庫(kù)也被脫走了。

如果可能的話,需要對(duì)開(kāi)發(fā)者的代碼進(jìn)行漏洞掃描。一些常見(jiàn)的安全漏洞現(xiàn)在應(yīng)該是有現(xiàn)成的工具支持的。另外,讓專(zhuān)業(yè)的人做專(zhuān)業(yè)的事情,例如要有安全團(tuán)隊(duì),可能你會(huì)說(shuō)你們公司沒(méi)有不也活的好好的,哈哈,只不過(guò)可能還沒(méi)有被壞人盯上而已,壞人也會(huì)考慮到他們的成本和預(yù)期收益的,當(dāng)然這就更加對(duì)我們開(kāi)發(fā)者提高了要求。一些敏感權(quán)限盡量控制在少部分人手中,配合相應(yīng)的流程來(lái)支撐(不得不說(shuō),大公司繁瑣的流程還是有一定道理的)。


畢竟我不是專(zhuān)業(yè)研究Web安全的,以上說(shuō)得可能也不一定對(duì),如果你有不同意見(jiàn)或者更好的建議歡迎留言參與討論。


這篇文章從寫(xiě)代碼做實(shí)驗(yàn),到錄屏做視頻動(dòng)圖等等耗時(shí)還蠻久的(好幾個(gè)周末的時(shí)間呢),原創(chuàng)真心不易,希望你能幫我個(gè)小忙唄,如果本文內(nèi)容你覺(jué)得有所啟發(fā),有所收獲,請(qǐng)幫忙點(diǎn)個(gè)“在看”唄,或者轉(zhuǎn)發(fā)分享讓更多的小伙伴看到。

特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒(méi)關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:

一行代碼引來(lái)的安全漏洞就讓我們丟失了整個(gè)服務(wù)器的控制權(quán)

長(zhǎng)按訂閱更多精彩▼

一行代碼引來(lái)的安全漏洞就讓我們丟失了整個(gè)服務(wù)器的控制權(quán)

如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉