去年三月份春招高峰期,我在投软件开发岗的时候收到了奇安信2023春招软件开发方向的笔试邀请,点进去就是这个“试卷2”。说实话,做之前我以为跟普通互联网公司差不多,选几道计算机基础、写两道算法题就完了。做完才发现,这套卷子有一套非常明显的“安全公司开发岗”逻辑,很多题目表面考的是常规知识点,实际上都在往安全编码和漏洞防御上靠。如果你准备投奇安信或者同类安全厂商的软件开发岗,这篇复盘值得认真看看。我不会把原题一字不落贴出来,但会把题型结构、考点分布、编程题解题思路、安全特色题的应对方法都讲透,帮你少走弯路。
1. 奇安信2023春招软件开发试卷2:整体结构拆解
1.1 试卷的题型分布和考试体验
试卷2的题型分成四块:单选、多选、编程题,以及一道设计或者开放题。总时长我记得是90分钟,单选和多选加起来大概20道左右,覆盖数据结构、操作系统、计算机网络、数据库以及网络安全基础;编程题2到3道,难度基本控制在LeetCode中等偏下;最后一道开放题通常跟项目经历或某个场景设计有关,不会直接给你一个标准答案去背。
我是先快速扫了一遍所有题目,然后跳到编程题先做,再回头做选择题,最后写设计题。这个顺序后面会单独说,先给一个结论:这套卷子的选择题信息量很大,有些是多选题,少选、漏选、错选都会扣分,如果前面磨太久,后面编程题的时间会被严重挤压。在线笔试不像面对面考试可以自己掌控节奏,每一块倒计时都是一起走的,所以整体时间分配非常重要。
1.2 为什么它和普通软件开发笔试不一样
这套卷子区别于普通互联网公司开发岗最大的一点,是它会反复出现“让你判断一段代码存在什么问题”的题目。比如给你一段把用户输入直接拼进SQL的代码,问你存在什么风险、应该怎么改;或者给你一个文件下载接口,问你能否通过路径穿越的方式读到非预期文件。这类题目在普通公司通常只会出现在安全岗,但在奇安信的软件开发卷里几乎成了必考项。
所以准备这个笔试,不能只刷LeetCode,还得补一补OWASP Top 10、安全编码规范这类偏防御的知识。这里要注意一下,我强调的“识别风险”和“掌握修复方式”是指以后端开发、安全开发的角度去理解代码健壮性,而不是去学习攻击手段。前者是工程师的基本素养,后者方向就跑偏了。
1.3 笔试环境与设备细节
笔试用的是带摄像头监控的在线平台,切屏次数太多会警告,严重的时候可能直接交卷。我当时提前把浏览器弹窗、消息通知全部关掉,用一台闲置电脑做的,手机上把所有通知也关了。网络环境尽量稳定,万一中途断网,答题记录不一定能及时保存。还有个小细节,在线笔试通常有倒计时提醒,但没有“最后5分钟”的语音提醒,需要自己心里有数,所以每做完一大块就瞄一眼右上角时间。
2. 计算机基础考点:高频题和背后原理
2.1 数据结构与算法:链表、树、哈希一个都没少
计算机基础部分,数据结构和算法至少占了三分之一。链表、二叉树、哈希表、排序这些都是常规操作。高频题几乎可以提前锁定:判断链表是否成环、二叉树层序遍历、哈希表解决两数之和、快排和堆排的时间复杂度比较。
这里我想强调一个容易被忽略的细节:奇安信的题目不会只让你答“快排平均复杂度是多少”,而是会给你一个具体场景,问这组数据用归并排序和快排哪个更合适。比如数据量很大但基本有序,快排如果选不好基准点,性能会退化成O(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这段代码背后的逻辑很简单,两个人绕圈跑,一个快一个慢,只要有环就一定会相遇。笔试里这种题就算第一眼没思路,也要把最朴素的哈希表方案写上,至少能过一部分测试点,不要留空。
2.2 操作系统与并发:从进程线程聊到死锁和内存
操作系统主要考进程和线程的区别、死锁四条件、虚拟内存、页面置换算法。并发相关的题也出现不少,比如自旋锁和互斥锁的区别、乐观锁和悲观锁怎么选。
如果时间紧,可以按这个优先级准备:进程线程模型、死锁、内存管理。死锁四个必要条件(互斥、持有并等待、不可剥夺、循环等待)基本是必考,还要知道对应的预防手段,比如用资源有序分配法打破循环等待。自旋锁和互斥锁的区别在于,自旋锁拿不到锁的时候会忙等待,适合锁临界区很短的场景;互斥锁拿不到锁就睡眠,线程切换开销相对更大。这种概念辨析题在选择题里出现概率很高,要能一句话讲清楚。
虚拟内存部分,页表、缺页中断、LRU页面置换算法最好也过一遍。奇安信笔试不会考得太深,但会问“LRU的思想是什么”或者“为什么需要虚拟内存”,只要理解了“用时间换空间、按需加载”这两点,基本能应对。
2.3 计算机网络:TCP、HTTP、DNS是老三样,但会往安全方向延伸
计算机网络的选择题集中在TCP三次握手和四次挥手、HTTP状态码、HTTPS加密流程、DNS解析过程。试卷里有一类题特别值得注意:问哪个HTTP响应头可以降低XSS风险。答案是CSP,也就是Content-Security-Policy。这种题目在普通开发岗笔试里很少出现,但在安全公司笔试里命中率很高。
再比如讲HTTPS,普通公司可能只问“HTTPS和HTTP区别”,这里会加问“TLS握手过程中客户端如何验证服务端证书”。你至少得知道证书链校验、数字签名和公钥私钥的配合关系,而不是只会答“更安全”三个字。具体一点说,客户端拿到服务端证书后,会用系统内置的根证书去验证这个证书是否由可信CA签发,同时确认证书没有被篡改,最后还会检查证书域名和访问域名是否一致。这些细节才是安全公司想看到的回答。
2.4 数据库与SQL:索引、事务和安全漏洞要被串起来
数据库部分就是索引、事务隔离级别、SQL执行计划。索引底层为什么用B+树,这是一道送分题但很多人答不好。要抓住三点:树矮层数少、非叶子节点不存数据所以单次能读更多索引、叶子节点用链表串联方便范围查询。
奇安信笔试比较有特色的是,考SQL时经常会同时考SQL注入。比如问“使用PreparedStatement为什么能防SQL注入”。这题关键点在于预编译把SQL结构和参数分开,参数在编译阶段不会改变SQL语义,所以用户输入里的单引号、注释符都只会被当作普通字符串。如果只答“可以防止”不给原因,基本拿不到分。
事务隔离级别也建议整理成一张表记清楚:读未提交、读已提交、可重复读、串行化,分别对应什么并发问题。数据库题在试卷里占比不算最高,但性价比很高,一道选择题两三分钟就能做出来,比纠结一道不会的算法题划算。
3. 编程题实战:三道题型的实现思路
3.1 第一道编程题通常是字符串处理
我遇到的试卷2,第一道编程题是字符串处理类,题目本身不算难,但要求原地操作或者只能用一个额外空间实现,这类题很考代码基本功。推荐把这几类练熟:反转字符串、反转字符串中的单词、最长回文子串、字符串压缩。
如果遇到最长回文子串,最简单的是中心扩展法,时间复杂度O(n²),空间O(1),笔试完全够用。核心思路是遍历每一个字符,把它当作回文中心往两边扩,同时处理偶数长度回文的情况。代码可以这样写:
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]笔试时不用追求最优雅,先把能跑通的版本写出来,再考虑优化,这个顺序很重要。
3.2 第二道编程题基本是树或图
第二道题大概率是二叉树或者图的遍历。层序遍历、最近公共祖先、图的拓扑排序、最短路径都是常客。树的问题用BFS写得很干净,图的题要提前准备邻接表写法,避免笔试现场边想边写。
层序遍历关键点是用队列记录当前层节点数,每轮循环先读len(q),只处理这一层的节点,这样分层的处理就不会乱:
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循环里len(q)是在进入循环前求值的,所以不会被后面append的新节点干扰。如果写成while q再动态取节点,分层就会乱掉。这个细节我在笔试现场就吃过亏,调试花了十几分钟才发现问题。
3.3 动态规划题怎么不慌
编程题如果出现第三道,多半是动态规划。我印象中试卷2里有一道最长递增子序列的变体。LIS标准写法是O(n²)的DP,笔试时间紧张时先写这个版本,跑通就能拿大部分分;如果还有时间再考虑O(n log n)的二分优化。
DP题的通用思路是三步走:定义dp数组含义、找状态转移方程、确定初始值。LIS的状态转移就是dp[i] = max(dp[j] + 1) for j < i and nums[j] < nums[i]。笔试的时候别急着写代码,先在草稿纸或者注释里把状态定义写出来,写清楚之后再动键盘,能少很多低级错误。记住,动态规划的分数不是凭空来的,是你把转移方程写出来的那一刻就已经拿到一半了。
3.4 边界处理和代码风格是隐性加分项
编程题是通过在线判题系统跑的,虽然不看注释,但代码风格和边界处理会影响你的调试效率。我给自己定了几条硬规矩:第一,所有循环前先判空;第二,字符串和数组操作前确认下标不越界;第三,递归一定要想清楚终止条件;第四,写完先补一版时间复杂度和空间复杂度的注释。这个习惯在后面面试讲题时会很加分,因为面试官能看到你对代码的掌控力。
还有一点,在线平台选择语言后基本不能换。我建议用自己最熟的语言,不要考试现场试新语言。Python写算法题效率高,但有些平台的判题环境版本比较旧,注意别用太高版本的语法特性,比如某些Python 3.9才支持的写法,在3.6环境里就会编译失败。
4. 网络安全特色题:安全公司的笔试差在哪
4.1 Web安全四件套:XSS、SQL注入、CSRF、路径穿越
这一节才是奇安信笔试的重头戏。先理清几个名词,不然选择题会懵。我把最常考的几个漏洞整理成一张表,方便对照着记:
| 漏洞类型 | 简单理解 | 防御方向 |
|---|---|---|
| XSS | 用户输入被当作前端脚本执行 | 输出编码、CSP、富文本白名单 |
| SQL注入 | 用户输入改变了SQL语句的语义 | 参数化查询、最小权限、输入校验 |
| CSRF | 借用已登录身份发起非预期请求 | CSRF Token、SameSite Cookie、校验Origin |
| 路径穿越 | 用../等特殊路径读取非预期文件 | 路径规范化校验、白名单、避免拼接用户输入 |
考试不会让你去复现攻击,而是给你代码片段让你判断风险点、选择修复方式。准备这块强烈建议把OWASP Top 10每个漏洞的“危害”和“修复”看一遍。重点不是背漏洞名字,而是看到代码场景时能联想到“这里为什么有问题”。
4.2 密码学基础:对称、非对称、哈希的区别
安全公司开发岗笔试,密码学是躲不开的。AES、RSA、MD5、SHA-256、数字签名、数字证书这些概念至少得能说出级别的区别。容易踩坑的点是:MD5是哈希摘要算法,不是加密算法,它不可逆。加密算法是可逆的,密钥对了能还原明文。面试官特别喜欢让人分清楚这一点。
HTTPS的加密流程也常考。经典流程是:客户端拿到服务端证书,验证证书链和签名,再用证书里的公钥协商一把临时对称密钥,之后通信全走对称加密。这套流程把非对称加密用于密钥协商、对称加密用于实际数据传输,兼顾安全性和性能。答题的时候可以把这个流程简洁地用“非对称协商、对称通信”八个字概括,然后展开细节。
4.3 安全编码与输入校验:开发岗必须养成的习惯
奇安信笔试里有一道文件下载相关的代码题,主要问题是直接拼接用户传入的文件名去读服务器本地文件。表面上看功能正常,但如果传入带有特殊路径的路径,就可能发生路径穿越,读到非预期文件。
修复思路是不要信任用户输入。先把路径用os.path.abspath规范化,再判断规范化后的路径是否在允许的目录前缀下;更保险的是把可下载文件白名单化,用户只传文件ID,由后端映射到真实路径。这个思路我在设计题里也延续了,安全公司对开发者的要求从来不是“功能能跑就行”,而是“异常输入来了也能扛得住”。输入校验这个知识点在热词里频繁出现,说明它确实是这个岗位的核心高频考点。
4.4 安全协议和常见端口
网络层还会考一些安全协议和常见端口,比如SSH是22、HTTP是80、HTTPS是443、MySQL是3306、Redis是6379。这套题里出现过“TLS和SSL的区别”“单点登录大概流程”这类概念题,准备的时候可以顺带过一下。
如果你还有余力,建议把OAuth2.0的授权码模式流程看一遍,安全公司做产品经常涉及开放平台和统一身份认证,笔试和面试都很喜欢问。授权码模式的核心是让用户授权后,客户端拿着授权码去换令牌,而不是直接把令牌暴露在回调地址里。这个知识点背下来不难,但理解了之后讲出来会更有说服力。
5. 开放题与项目经历:安全视角在哪里
5.1 项目复盘别只讲功能,要讲方案取舍
整张卷子的最后一道题一般是开放题,往往从简历项目出发,问“你在项目里做了哪些事情,遇到的最难的问题是什么,怎么解决的”。这道题看起来软,但如果只讲功能实现,基本告别高分。
我的方法是按STAR法则写清楚背景、任务、行动、结果,并且刻意突出一个点:安全与稳定性。比如你做过一个登录注册模块,可以讲清楚密码是怎么存储的,有没有加盐、用的什么哈希算法、是否考虑过暴力破解防御;你做过一个文件上传功能,可以讲为什么限制文件类型和后缀,图片文件为什么建议用服务端重新编码来防止上传恶意脚本。这些细节能直观体现出你和普通开发者的差别。
5.2 设计题举例:设计一个安全的登录模块
有一种开放题是场景设计:“请设计一个Web系统的登录模块,要求兼顾安全性和可用性”。这个题很能体现安全公司开发岗和其他公司的差异。我当时列了五层:传输层用HTTPS,存储层用bcrypt加盐哈希,应用层加入验证码和失败锁定策略,会话层使用HttpOnly加Secure的Cookie并设置过期时间,最后加审计日志记录登录失败和成功事件。
这些方案不用写得太长,但每一条都要能说出理由。比如密码为什么不用MD5直接存,因为MD5没有加盐时彩虹表非常容易查;为什么不用明文去数据库比对,因为一旦数据库泄露,用户在其他平台的主力密码也会受影响。把“为什么”讲清楚,比堆专业名词有效得多,这是开放题拿高分的关键。
5.3 软素质题:不要背模板,要有真实案例
软素质题也可能有,比如“你遇到和同事意见不一致时怎么处理”“你怎么安排学习计划”。这种题没有标准答案,关键是要给出真实的经历,并且把流程说清楚:发生了什么、你意识到问题在哪、你做了什么、结果如何。宁可讲一件小事,也不要编一个大而空的案例,面试官很容易追问,编的内容一问就露馅。
比如你可以讲一次Code Review中和同事对某个设计有分歧,你选择先去看对方的代码上下文,再拿出数据或者文档佐证自己的观点,最后一起定了一个折中方案。这比说“我会尊重团队意见”要具体得多,也更容易打动对方。
6. 复盘总结与备考建议
6.1 时间分配:编程题先拿下来
如果让我重新做这套卷子,我会严格执行这样的顺序:拿到试卷先花2分钟扫全卷,直接跳到编程题,确保算法题有充足时间;然后是设计题,把自己会的点写出来;最后做选择题。选择题虽然多,但单个分值小,前面磨太久会耽误算法题的调试时间。
编程题建议每道控制在20分钟以内,超时先写一个最朴素解法拿通过率,不要死磕最优解。在线判题往往按测试点给分,部分通过也有分,留空的代价最大。
6.2 备考清单:按优先级准备
我整理了一个备考优先级表,按这个顺序复习效率会高很多:
| 优先级 | 内容 | 说明 |
|---|---|---|
| P0 | LeetCode热题100 | 覆盖数据结构与算法基础 |
| P0 | 计算机网络、操作系统、数据库基础 | 保证选择题不丢分 |
| P1 | OWASP Top 10漏洞原理与修复 | 应对安全特色题 |
| P1 | 密码学基础概念 | 对称、非对称、哈希、证书 |
| P2 | 项目复盘和设计题思路 | 开放题拿分关键 |
刷题工具我用的LeetCode,题库方面优先做“热题100”和“面试经典150”。计算机网络可以看《网络是怎么连接的》作为入门,操作系统可以看图解系列的网站,数据库就是索引和事务两座大山。资料不在多,关键是过完之后要能自己讲出体系。
6.3 踩过的坑:笔试环境与细节
最后分享几个我自己踩过的坑。第一,在线笔试前一定要提前测一下摄像头和麦克风,有些平台启动检测很慢,别等到考试开始才发现设备不对。第二,编程题的输入输出格式要仔细读,尤其是多组测试数据的读取方式,有的题目要求循环读入直到EOF,有的只读一次。我就在这上面丢过时间,原因只是没看清“一行一个测试用例”这句话。第三,多选题不确定的时候宁少勿多,很多多选是漏选得部分分、错选不得分,拿不稳的选项别选。
另外,虽然这篇文章标题是“试卷2”,但奇安信不同批次的试卷题型和风格基本一致,你按这套逻辑去准备,大概率不会跑偏。热词里搜“奇安信”和“软件开发”的人很多,说明这个方向竞争也不小,提前把安全编码、漏洞防御这些差异化内容补起来,会比你单纯刷几百道算法题更有优势。
整套卷子做下来,我的体会是:安全公司的开发岗笔试,最后拉分的往往不是算法题有多难,而是你有没有“安全敏感”。回答设计题时能不能想到登录要加验证码、密码要加盐哈希、接口要做权限校验,这些细节点才是他们真正想看的。备考过程中不需要成为安全专家,但至少要把“写安全的代码”变成下意识习惯。这个能力不仅在笔试里有用,进团队之后写需求、做Code Review,都会是很大的加分项。