日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

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

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



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


下面我們就一起來看看這個事故,啊,不對,是故事。


背景說明

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



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

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

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

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


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

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

@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));
    }
}
不同語言的資源文件示例
private String getI18nMessage(String key, HttpServletRequest request) { try { return messageSource.getMessage(key, null, LanguaggeUtils.currentLocale(request));
   } catch (Exception e) { // log return key;
   }
}




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

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


定義的表單如下:

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

對于表單的約束,我們有:

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

對于以上約束,我們只需要在對應(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;
}

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



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

自定義注解的實現(xiàn)主要的其實就是一個自定義注解的定義以及一個校驗邏輯。例如定義一個自定義注解 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();
    }
}

校驗邏輯的實現(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();
    }
}

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

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

場景重現(xiàn)

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

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

太 TM 神奇了,居然幫我運算出來了,返"message": "Invalid params: 1+1=2"。

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

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

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

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


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

以為就這樣就完了嗎?


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

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

比如,我們執(zhí)行個打開計算器的命令,搞個計算器玩玩~

我錄制了一個動圖,來個演示可能更生動一些。

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

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


漏洞根因

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


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

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


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

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

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


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


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

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

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

長按訂閱更多精彩▼

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

如有收獲,點個在看,誠摯感謝

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

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

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當(dāng)下,工業(yè)電機作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設(shè)計中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計成為提升電機驅(qū)動性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實際應(yīng)用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設(shè)計、生...

關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設(shè)計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

LED驅(qū)動電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動電源
關(guān)閉