筆試復(fù)盤:安全編碼與漏洞防御全解析)
去年三月份春招高峰期我在投軟件開發(fā)崗的時候收到了奇安信2023春招軟件開發(fā)方向的筆試邀請點進去就是這個“試卷2”。說實話做之前我以為跟普通互聯(lián)網(wǎng)公司差不多選幾道計算機基礎(chǔ)、寫兩道算法題就完了。做完才發(fā)現(xiàn)這套卷子有一套非常明顯的“安全公司開發(fā)崗”邏輯很多題目表面考的是常規(guī)知識點實際上都在往安全編碼和漏洞防御上靠。如果你準備投奇安信或者同類安全廠商的軟件開發(fā)崗這篇復(fù)盤值得認真看看。我不會把原題一字不落貼出來但會把題型結(jié)構(gòu)、考點分布、編程題解題思路、安全特色題的應(yīng)對方法都講透幫你少走彎路。1. 奇安信2023春招軟件開發(fā)試卷2整體結(jié)構(gòu)拆解1.1 試卷的題型分布和考試體驗試卷2的題型分成四塊單選、多選、編程題以及一道設(shè)計或者開放題。總時長我記得是90分鐘單選和多選加起來大概20道左右覆蓋數(shù)據(jù)結(jié)構(gòu)、操作系統(tǒng)、計算機網(wǎng)絡(luò)、數(shù)據(jù)庫以及網(wǎng)絡(luò)安全基礎(chǔ)編程題2到3道難度基本控制在LeetCode中等偏下最后一道開放題通常跟項目經(jīng)歷或某個場景設(shè)計有關(guān)不會直接給你一個標準答案去背。我是先快速掃了一遍所有題目然后跳到編程題先做再回頭做選擇題最后寫設(shè)計題。這個順序后面會單獨說先給一個結(jié)論這套卷子的選擇題信息量很大有些是多選題少選、漏選、錯選都會扣分如果前面磨太久后面編程題的時間會被嚴重擠壓。在線筆試不像面對面考試可以自己掌控節(jié)奏每一塊倒計時都是一起走的所以整體時間分配非常重要。1.2 為什么它和普通軟件開發(fā)筆試不一樣這套卷子區(qū)別于普通互聯(lián)網(wǎng)公司開發(fā)崗最大的一點是它會反復(fù)出現(xiàn)“讓你判斷一段代碼存在什么問題”的題目。比如給你一段把用戶輸入直接拼進SQL的代碼問你存在什么風(fēng)險、應(yīng)該怎么改或者給你一個文件下載接口問你能否通過路徑穿越的方式讀到非預(yù)期文件。這類題目在普通公司通常只會出現(xiàn)在安全崗但在奇安信的軟件開發(fā)卷里幾乎成了必考項。所以準備這個筆試不能只刷LeetCode還得補一補OWASP Top 10、安全編碼規(guī)范這類偏防御的知識。這里要注意一下我強調(diào)的“識別風(fēng)險”和“掌握修復(fù)方式”是指以后端開發(fā)、安全開發(fā)的角度去理解代碼健壯性而不是去學(xué)習(xí)攻擊手段。前者是工程師的基本素養(yǎng)后者方向就跑偏了。1.3 筆試環(huán)境與設(shè)備細節(jié)筆試用的是帶攝像頭監(jiān)控的在線平臺切屏次數(shù)太多會警告嚴重的時候可能直接交卷。我當時提前把瀏覽器彈窗、消息通知全部關(guān)掉用一臺閑置電腦做的手機上把所有通知也關(guān)了。網(wǎng)絡(luò)環(huán)境盡量穩(wěn)定萬一中途斷網(wǎng)答題記錄不一定能及時保存。還有個小細節(jié)在線筆試通常有倒計時提醒但沒有“最后5分鐘”的語音提醒需要自己心里有數(shù)所以每做完一大塊就瞄一眼右上角時間。2. 計算機基礎(chǔ)考點高頻題和背后原理2.1 數(shù)據(jù)結(jié)構(gòu)與算法鏈表、樹、哈希一個都沒少計算機基礎(chǔ)部分數(shù)據(jù)結(jié)構(gòu)和算法至少占了三分之一。鏈表、二叉樹、哈希表、排序這些都是常規(guī)操作。高頻題幾乎可以提前鎖定判斷鏈表是否成環(huán)、二叉樹層序遍歷、哈希表解決兩數(shù)之和、快排和堆排的時間復(fù)雜度比較。這里我想強調(diào)一個容易被忽略的細節(jié)奇安信的題目不會只讓你答“快排平均復(fù)雜度是多少”而是會給你一個具體場景問這組數(shù)據(jù)用歸并排序和快排哪個更合適。比如數(shù)據(jù)量很大但基本有序快排如果選不好基準點性能會退化成O(n2)這時候歸并排序更穩(wěn)定。所以備考不要死記結(jié)論要把每種排序的穩(wěn)定性、最好最壞復(fù)雜度、適用場景都梳理清楚。判斷鏈表是否成環(huán)是面試筆試中出現(xiàn)頻率極高的題快慢指針的寫法要能默寫出來def has_cycle(head): slow fast head while fast and fast.next: slow slow.next fast fast.next.next if slow fast: return True return False這段代碼背后的邏輯很簡單兩個人繞圈跑一個快一個慢只要有環(huán)就一定會相遇。筆試里這種題就算第一眼沒思路也要把最樸素的哈希表方案寫上至少能過一部分測試點不要留空。2.2 操作系統(tǒng)與并發(fā)從進程線程聊到死鎖和內(nèi)存操作系統(tǒng)主要考進程和線程的區(qū)別、死鎖四條件、虛擬內(nèi)存、頁面置換算法。并發(fā)相關(guān)的題也出現(xiàn)不少比如自旋鎖和互斥鎖的區(qū)別、樂觀鎖和悲觀鎖怎么選。如果時間緊可以按這個優(yōu)先級準備進程線程模型、死鎖、內(nèi)存管理。死鎖四個必要條件互斥、持有并等待、不可剝奪、循環(huán)等待基本是必考還要知道對應(yīng)的預(yù)防手段比如用資源有序分配法打破循環(huán)等待。自旋鎖和互斥鎖的區(qū)別在于自旋鎖拿不到鎖的時候會忙等待適合鎖臨界區(qū)很短的場景互斥鎖拿不到鎖就睡眠線程切換開銷相對更大。這種概念辨析題在選擇題里出現(xiàn)概率很高要能一句話講清楚。虛擬內(nèi)存部分頁表、缺頁中斷、LRU頁面置換算法最好也過一遍。奇安信筆試不會考得太深但會問“LRU的思想是什么”或者“為什么需要虛擬內(nèi)存”只要理解了“用時間換空間、按需加載”這兩點基本能應(yīng)對。2.3 計算機網(wǎng)絡(luò)TCP、HTTP、DNS是老三樣但會往安全方向延伸計算機網(wǎng)絡(luò)的選擇題集中在TCP三次握手和四次揮手、HTTP狀態(tài)碼、HTTPS加密流程、DNS解析過程。試卷里有一類題特別值得注意問哪個HTTP響應(yīng)頭可以降低XSS風(fēng)險。答案是CSP也就是Content-Security-Policy。這種題目在普通開發(fā)崗筆試里很少出現(xiàn)但在安全公司筆試里命中率很高。再比如講HTTPS普通公司可能只問“HTTPS和HTTP區(qū)別”這里會加問“TLS握手過程中客戶端如何驗證服務(wù)端證書”。你至少得知道證書鏈校驗、數(shù)字簽名和公鑰私鑰的配合關(guān)系而不是只會答“更安全”三個字。具體一點說客戶端拿到服務(wù)端證書后會用系統(tǒng)內(nèi)置的根證書去驗證這個證書是否由可信CA簽發(fā)同時確認證書沒有被篡改最后還會檢查證書域名和訪問域名是否一致。這些細節(jié)才是安全公司想看到的回答。2.4 數(shù)據(jù)庫與SQL索引、事務(wù)和安全漏洞要被串起來數(shù)據(jù)庫部分就是索引、事務(wù)隔離級別、SQL執(zhí)行計劃。索引底層為什么用B樹這是一道送分題但很多人答不好。要抓住三點樹矮層數(shù)少、非葉子節(jié)點不存數(shù)據(jù)所以單次能讀更多索引、葉子節(jié)點用鏈表串聯(lián)方便范圍查詢。奇安信筆試比較有特色的是考SQL時經(jīng)常會同時考SQL注入。比如問“使用PreparedStatement為什么能防SQL注入”。這題關(guān)鍵點在于預(yù)編譯把SQL結(jié)構(gòu)和參數(shù)分開參數(shù)在編譯階段不會改變SQL語義所以用戶輸入里的單引號、注釋符都只會被當作普通字符串。如果只答“可以防止”不給原因基本拿不到分。事務(wù)隔離級別也建議整理成一張表記清楚讀未提交、讀已提交、可重復(fù)讀、串行化分別對應(yīng)什么并發(fā)問題。數(shù)據(jù)庫題在試卷里占比不算最高但性價比很高一道選擇題兩三分鐘就能做出來比糾結(jié)一道不會的算法題劃算。3. 編程題實戰(zhàn)三道題型的實現(xiàn)思路3.1 第一道編程題通常是字符串處理我遇到的試卷2第一道編程題是字符串處理類題目本身不算難但要求原地操作或者只能用一個額外空間實現(xiàn)這類題很考代碼基本功。推薦把這幾類練熟反轉(zhuǎn)字符串、反轉(zhuǎn)字符串中的單詞、最長回文子串、字符串壓縮。如果遇到最長回文子串最簡單的是中心擴展法時間復(fù)雜度O(n2)空間O(1)筆試完全夠用。核心思路是遍歷每一個字符把它當作回文中心往兩邊擴同時處理偶數(shù)長度回文的情況。代碼可以這樣寫def longest_palindrome(s: str) - str: if not s: return start, end 0, 0 def expand(left, right): while left 0 and right len(s) and s[left] s[right]: left - 1 right 1 return left 1, right - 1 for i in range(len(s)): l1, r1 expand(i, i) l2, r2 expand(i, i 1) if r1 - l1 end - start: start, end l1, r1 if r2 - l2 end - start: start, end l2, r2 return s[start:end 1]筆試時不用追求最優(yōu)雅先把能跑通的版本寫出來再考慮優(yōu)化這個順序很重要。3.2 第二道編程題基本是樹或圖第二道題大概率是二叉樹或者圖的遍歷。層序遍歷、最近公共祖先、圖的拓撲排序、最短路徑都是???。樹的問題用BFS寫得很干凈圖的題要提前準備鄰接表寫法避免筆試現(xiàn)場邊想邊寫。層序遍歷關(guān)鍵點是用隊列記錄當前層節(jié)點數(shù)每輪循環(huán)先讀len(q)只處理這一層的節(jié)點這樣分層的處理就不會亂from collections import deque def level_order(root): if not root: return [] res [] q deque([root]) while q: level [] for _ in range(len(q)): node q.popleft() level.append(node.val) if node.left: q.append(node.left) if node.right: q.append(node.right) res.append(level) return res這個for循環(huán)里len(q)是在進入循環(huán)前求值的所以不會被后面append的新節(jié)點干擾。如果寫成while q再動態(tài)取節(jié)點分層就會亂掉。這個細節(jié)我在筆試現(xiàn)場就吃過虧調(diào)試花了十幾分鐘才發(fā)現(xiàn)問題。3.3 動態(tài)規(guī)劃題怎么不慌編程題如果出現(xiàn)第三道多半是動態(tài)規(guī)劃。我印象中試卷2里有一道最長遞增子序列的變體。LIS標準寫法是O(n2)的DP筆試時間緊張時先寫這個版本跑通就能拿大部分分如果還有時間再考慮O(n log n)的二分優(yōu)化。DP題的通用思路是三步走定義dp數(shù)組含義、找狀態(tài)轉(zhuǎn)移方程、確定初始值。LIS的狀態(tài)轉(zhuǎn)移就是dp[i] max(dp[j] 1) for j i and nums[j] nums[i]。筆試的時候別急著寫代碼先在草稿紙或者注釋里把狀態(tài)定義寫出來寫清楚之后再動鍵盤能少很多低級錯誤。記住動態(tài)規(guī)劃的分數(shù)不是憑空來的是你把轉(zhuǎn)移方程寫出來的那一刻就已經(jīng)拿到一半了。3.4 邊界處理和代碼風(fēng)格是隱性加分項編程題是通過在線判題系統(tǒng)跑的雖然不看注釋但代碼風(fēng)格和邊界處理會影響你的調(diào)試效率。我給自己定了幾條硬規(guī)矩第一所有循環(huán)前先判空第二字符串和數(shù)組操作前確認下標不越界第三遞歸一定要想清楚終止條件第四寫完先補一版時間復(fù)雜度和空間復(fù)雜度的注釋。這個習(xí)慣在后面面試講題時會很加分因為面試官能看到你對代碼的掌控力。還有一點在線平臺選擇語言后基本不能換。我建議用自己最熟的語言不要考試現(xiàn)場試新語言。Python寫算法題效率高但有些平臺的判題環(huán)境版本比較舊注意別用太高版本的語法特性比如某些Python 3.9才支持的寫法在3.6環(huán)境里就會編譯失敗。4. 網(wǎng)絡(luò)安全特色題安全公司的筆試差在哪4.1 Web安全四件套XSS、SQL注入、CSRF、路徑穿越這一節(jié)才是奇安信筆試的重頭戲。先理清幾個名詞不然選擇題會懵。我把最??嫉膸讉€漏洞整理成一張表方便對照著記漏洞類型簡單理解防御方向XSS用戶輸入被當作前端腳本執(zhí)行輸出編碼、CSP、富文本白名單SQL注入用戶輸入改變了SQL語句的語義參數(shù)化查詢、最小權(quán)限、輸入校驗CSRF借用已登錄身份發(fā)起非預(yù)期請求CSRF Token、SameSite Cookie、校驗Origin路徑穿越用../等特殊路徑讀取非預(yù)期文件路徑規(guī)范化校驗、白名單、避免拼接用戶輸入考試不會讓你去復(fù)現(xiàn)攻擊而是給你代碼片段讓你判斷風(fēng)險點、選擇修復(fù)方式。準備這塊強烈建議把OWASP Top 10每個漏洞的“危害”和“修復(fù)”看一遍。重點不是背漏洞名字而是看到代碼場景時能聯(lián)想到“這里為什么有問題”。4.2 密碼學(xué)基礎(chǔ)對稱、非對稱、哈希的區(qū)別安全公司開發(fā)崗筆試密碼學(xué)是躲不開的。AES、RSA、MD5、SHA-256、數(shù)字簽名、數(shù)字證書這些概念至少得能說出級別的區(qū)別。容易踩坑的點是MD5是哈希摘要算法不是加密算法它不可逆。加密算法是可逆的密鑰對了能還原明文。面試官特別喜歡讓人分清楚這一點。HTTPS的加密流程也??肌=?jīng)典流程是客戶端拿到服務(wù)端證書驗證證書鏈和簽名再用證書里的公鑰協(xié)商一把臨時對稱密鑰之后通信全走對稱加密。這套流程把非對稱加密用于密鑰協(xié)商、對稱加密用于實際數(shù)據(jù)傳輸兼顧安全性和性能。答題的時候可以把這個流程簡潔地用“非對稱協(xié)商、對稱通信”八個字概括然后展開細節(jié)。4.3 安全編碼與輸入校驗開發(fā)崗必須養(yǎng)成的習(xí)慣奇安信筆試里有一道文件下載相關(guān)的代碼題主要問題是直接拼接用戶傳入的文件名去讀服務(wù)器本地文件。表面上看功能正常但如果傳入帶有特殊路徑的路徑就可能發(fā)生路徑穿越讀到非預(yù)期文件。修復(fù)思路是不要信任用戶輸入。先把路徑用os.path.abspath規(guī)范化再判斷規(guī)范化后的路徑是否在允許的目錄前綴下更保險的是把可下載文件白名單化用戶只傳文件ID由后端映射到真實路徑。這個思路我在設(shè)計題里也延續(xù)了安全公司對開發(fā)者的要求從來不是“功能能跑就行”而是“異常輸入來了也能扛得住”。輸入校驗這個知識點在熱詞里頻繁出現(xiàn)說明它確實是這個崗位的核心高頻考點。4.4 安全協(xié)議和常見端口網(wǎng)絡(luò)層還會考一些安全協(xié)議和常見端口比如SSH是22、HTTP是80、HTTPS是443、MySQL是3306、Redis是6379。這套題里出現(xiàn)過“TLS和SSL的區(qū)別”“單點登錄大概流程”這類概念題準備的時候可以順帶過一下。如果你還有余力建議把OAuth2.0的授權(quán)碼模式流程看一遍安全公司做產(chǎn)品經(jīng)常涉及開放平臺和統(tǒng)一身份認證筆試和面試都很喜歡問。授權(quán)碼模式的核心是讓用戶授權(quán)后客戶端拿著授權(quán)碼去換令牌而不是直接把令牌暴露在回調(diào)地址里。這個知識點背下來不難但理解了之后講出來會更有說服力。5. 開放題與項目經(jīng)歷安全視角在哪里5.1 項目復(fù)盤別只講功能要講方案取舍整張卷子的最后一道題一般是開放題往往從簡歷項目出發(fā)問“你在項目里做了哪些事情遇到的最難的問題是什么怎么解決的”。這道題看起來軟但如果只講功能實現(xiàn)基本告別高分。我的方法是按STAR法則寫清楚背景、任務(wù)、行動、結(jié)果并且刻意突出一個點安全與穩(wěn)定性。比如你做過一個登錄注冊模塊可以講清楚密碼是怎么存儲的有沒有加鹽、用的什么哈希算法、是否考慮過暴力破解防御你做過一個文件上傳功能可以講為什么限制文件類型和后綴圖片文件為什么建議用服務(wù)端重新編碼來防止上傳惡意腳本。這些細節(jié)能直觀體現(xiàn)出你和普通開發(fā)者的差別。5.2 設(shè)計題舉例設(shè)計一個安全的登錄模塊有一種開放題是場景設(shè)計“請設(shè)計一個Web系統(tǒng)的登錄模塊要求兼顧安全性和可用性”。這個題很能體現(xiàn)安全公司開發(fā)崗和其他公司的差異。我當時列了五層傳輸層用HTTPS存儲層用bcrypt加鹽哈希應(yīng)用層加入驗證碼和失敗鎖定策略會話層使用HttpOnly加Secure的Cookie并設(shè)置過期時間最后加審計日志記錄登錄失敗和成功事件。這些方案不用寫得太長但每一條都要能說出理由。比如密碼為什么不用MD5直接存因為MD5沒有加鹽時彩虹表非常容易查為什么不用明文去數(shù)據(jù)庫比對因為一旦數(shù)據(jù)庫泄露用戶在其他平臺的主力密碼也會受影響。把“為什么”講清楚比堆專業(yè)名詞有效得多這是開放題拿高分的關(guān)鍵。5.3 軟素質(zhì)題不要背模板要有真實案例軟素質(zhì)題也可能有比如“你遇到和同事意見不一致時怎么處理”“你怎么安排學(xué)習(xí)計劃”。這種題沒有標準答案關(guān)鍵是要給出真實的經(jīng)歷并且把流程說清楚發(fā)生了什么、你意識到問題在哪、你做了什么、結(jié)果如何。寧可講一件小事也不要編一個大而空的案例面試官很容易追問編的內(nèi)容一問就露餡。比如你可以講一次Code Review中和同事對某個設(shè)計有分歧你選擇先去看對方的代碼上下文再拿出數(shù)據(jù)或者文檔佐證自己的觀點最后一起定了一個折中方案。這比說“我會尊重團隊意見”要具體得多也更容易打動對方。6. 復(fù)盤總結(jié)與備考建議6.1 時間分配編程題先拿下來如果讓我重新做這套卷子我會嚴格執(zhí)行這樣的順序拿到試卷先花2分鐘掃全卷直接跳到編程題確保算法題有充足時間然后是設(shè)計題把自己會的點寫出來最后做選擇題。選擇題雖然多但單個分值小前面磨太久會耽誤算法題的調(diào)試時間。編程題建議每道控制在20分鐘以內(nèi)超時先寫一個最樸素解法拿通過率不要死磕最優(yōu)解。在線判題往往按測試點給分部分通過也有分留空的代價最大。6.2 備考清單按優(yōu)先級準備我整理了一個備考優(yōu)先級表按這個順序復(fù)習(xí)效率會高很多優(yōu)先級內(nèi)容說明P0LeetCode熱題100覆蓋數(shù)據(jù)結(jié)構(gòu)與算法基礎(chǔ)P0計算機網(wǎng)絡(luò)、操作系統(tǒng)、數(shù)據(jù)庫基礎(chǔ)保證選擇題不丟分P1OWASP Top 10漏洞原理與修復(fù)應(yīng)對安全特色題P1密碼學(xué)基礎(chǔ)概念對稱、非對稱、哈希、證書P2項目復(fù)盤和設(shè)計題思路開放題拿分關(guān)鍵刷題工具我用的LeetCode題庫方面優(yōu)先做“熱題100”和“面試經(jīng)典150”。計算機網(wǎng)絡(luò)可以看《網(wǎng)絡(luò)是怎么連接的》作為入門操作系統(tǒng)可以看圖解系列的網(wǎng)站數(shù)據(jù)庫就是索引和事務(wù)兩座大山。資料不在多關(guān)鍵是過完之后要能自己講出體系。6.3 踩過的坑筆試環(huán)境與細節(jié)最后分享幾個我自己踩過的坑。第一在線筆試前一定要提前測一下攝像頭和麥克風(fēng)有些平臺啟動檢測很慢別等到考試開始才發(fā)現(xiàn)設(shè)備不對。第二編程題的輸入輸出格式要仔細讀尤其是多組測試數(shù)據(jù)的讀取方式有的題目要求循環(huán)讀入直到EOF有的只讀一次。我就在這上面丟過時間原因只是沒看清“一行一個測試用例”這句話。第三多選題不確定的時候?qū)幧傥鸲嗪芏喽噙x是漏選得部分分、錯選不得分拿不穩(wěn)的選項別選。另外雖然這篇文章標題是“試卷2”但奇安信不同批次的試卷題型和風(fēng)格基本一致你按這套邏輯去準備大概率不會跑偏。熱詞里搜“奇安信”和“軟件開發(fā)”的人很多說明這個方向競爭也不小提前把安全編碼、漏洞防御這些差異化內(nèi)容補起來會比你單純刷幾百道算法題更有優(yōu)勢。整套卷子做下來我的體會是安全公司的開發(fā)崗筆試最后拉分的往往不是算法題有多難而是你有沒有“安全敏感”?;卮鹪O(shè)計題時能不能想到登錄要加驗證碼、密碼要加鹽哈希、接口要做權(quán)限校驗這些細節(jié)點才是他們真正想看的。備考過程中不需要成為安全專家但至少要把“寫安全的代碼”變成下意識習(xí)慣。這個能力不僅在筆試里有用進團隊之后寫需求、做Code Review都會是很大的加分項。