尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从OWASP Juice Shop靶场实战,掌握Web安全漏洞的代码级防御

从OWASP Juice Shop靶场实战,掌握Web安全漏洞的代码级防御 1. 项目概述为什么开发者需要深入“玩坏”一个靶场如果你是一名Web开发者可能对“安全”这个词既熟悉又陌生。熟悉的是每次代码评审或上线前总会有人提一句“注意安全”陌生的是安全漏洞到底是什么样子它们是如何在代码中“诞生”的以及修复它们到底意味着要改动哪些具体的代码行。OWASP Juice Shop这个靶场在我看来是弥合这种认知鸿沟的最佳工具。它不是一个简单的漏洞列表展示台而是一个精心构建的、功能完整的“问题”电商应用里面的每一个漏洞都对应着真实业务场景下开发者可能写出的一段“问题代码”。很多开发者接触安全是从扫描报告开始的。报告上冷冰冰地写着“SQL注入高危”、“跨站脚本XSS中危”然后附上一个模糊的URL。你照着修复指南可能只是机械地在某个输入框前后加上encodeURIComponent()或者使用参数化查询但心里并不清楚为什么这里会有漏洞攻击者到底是怎么利用的我这样改真的够了吗Juice Shop靶场则把整个攻击链和防御代码都摆在了你面前。你可以先以“黑客”视角利用漏洞完成挑战比如用SQL注入免费获取商品或用XSS盗取管理员的Cookie然后再切换到“开发者”视角去查看并修改背后的源代码从根本上堵上这个漏洞。这个过程的价值在于“复盘”。它不是告诉你一个抽象的安全原则而是让你亲手“制造”并“修复”一个具体的Bug。当你看到一段因为字符串拼接而导致的SQL注入代码并亲手将其重构为使用预编译语句Prepared Statement时你对“输入验证”和“参数化查询”的理解会比读十篇安全文章都深刻。这正是我想通过这篇文章分享的核心从开发者的第一视角深入Juice Shop靶场中几个最具代表性的“真实”代码漏洞不仅看现象更要剖析其代码根源并给出从代码层面出发的、可落地的修复建议。这不仅仅是安全人员的功课更是每一位编写业务代码的开发者的必修课。2. 核心漏洞类型与代码级根源剖析Juice Shop靶场覆盖了OWASP Top 10中绝大多数漏洞类型。但从开发者视角看我们可以将这些漏洞归结为几类常见的“代码坏味道”或设计缺陷。理解这些根源比记忆单个漏洞更重要。2.1 信任边界模糊未经验证的用户输入这是绝大多数漏洞的“万恶之源”。在代码中它体现为将来自客户端浏览器、API调用者的任何数据未经充分清洗和验证就直接用于敏感操作。Juice Shop里大量的漏洞都源于此。典型代码模式// 反面案例直接使用用户输入拼接SQL查询Juice Shop中多处存在 const userInput req.body.productId; const query SELECT * FROM Products WHERE id ${userInput}; db.get(query, (err, row) { ... }); // 反面案例直接将用户输入插入DOM导致XSS const userComment req.body.comment; document.getElementById(comment-section).innerHTML p${userComment}/p;在这两段代码中productId和comment都来自HTTP请求体req.body被开发者无条件地信任了。服务器或浏览器假设用户会乖乖地输入一个数字或一段无害文本但攻击者会输入 OR 11--用于SQL注入或scriptalert(document.cookie)/script用于XSS。代码层面的根源缺乏输入验证层代码中没有对输入数据的类型、长度、格式、取值范围进行强制约束。例如productId本应是一个正整数但代码没有用parseInt()转换并检查范围而是直接当作字符串处理。上下文混淆开发者没有区分数据所处的“上下文”。同样是用户输入的comment如果放在HTML标签内HTML上下文、HTML属性内如href属性、JavaScript代码中JavaScript上下文或SQL查询中SQL上下文其危险字符和转义规则是完全不同的。混用上下文是XSS的常见原因。过度依赖客户端验证仅在浏览器端用JavaScript进行验证是绝对不安全的因为攻击者可以绕过浏览器直接发送恶意请求。服务器端必须进行最终且权威的验证。2.2 身份与权限控制缺失这类漏洞的代码体现为在执行一个操作或访问一项资源前没有严格检查“当前用户是谁”认证以及“他是否有权做这件事”授权。典型代码模式// 反面案例仅通过隐藏的表单字段或URL参数来判断权限Juice Shop中“越权访问”漏洞 app.get(/api/orders/:orderId, (req, res) { // 问题没有验证当前登录用户是否是此订单的所有者 const order db.get(SELECT * FROM Orders WHERE id ${req.params.orderId}); res.json(order); }); // 反面案例前端根据用户角色隐藏按钮但后端接口未做校验Juice Shop中“管理员功能暴露”漏洞 // 前端if (user.role admin) showAdminPanelButton(); // 后端app.post(/api/admin/deleteAllUsers, (req, res) { ... }); // 任何登录用户调用都成功第一段代码允许用户通过猜测或修改orderId来查看他人的订单水平越权。第二段代码虽然前端隐藏了管理员功能但后端对应的API接口没有进行角色校验导致普通用户只要知道接口地址就能直接调用并执行管理员操作垂直越权。代码层面的根源授权检查与业务逻辑分离授权逻辑检查用户角色、资源所有权没有作为业务逻辑执行前的必经步骤而是被遗漏或与业务代码混杂不清。依赖不可信客户端信息进行授权使用客户端传来的用户ID、角色标识等作为授权依据。这些信息可以被篡改。正确的做法是授权应基于服务器端会话Session中存储的、经过认证的用户标识。默认拒绝原则缺失代码默认假设请求是合法的只在“特殊情况下”才做检查。安全的设计应该是“默认拒绝”即除非明确授权否则一律拒绝。2.3 不安全的数据处理与依赖这类漏洞涉及数据在存储、传输、使用过程中的安全性以及对外部组件/库的盲目信任。典型代码模式// 反面案例使用弱哈希算法存储密码Juice Shop早期版本或类似场景 const hashedPassword md5(password); // MD5已被证明可快速碰撞 db.run(INSERT INTO Users (email, password) VALUES (?, ?), [email, hashedPassword]); // 反面案例敏感信息如API密钥硬编码在客户端代码中 // config.js 前端文件 const API_SECRET sup3r_s3cr3t_k3y_123; // 会被任何用户查看源代码获取第一段代码使用了不安全的MD5算法且没有加盐Salt使得密码在数据库泄露后极易被彩虹表破解。第二段代码将本应保存在服务器端的密钥暴露给了所有客户端攻击者可以直接利用此密钥调用后端服务。代码层面的根源使用了过时或不安全的算法/配置开发者可能因为“省事”或“不知道有更好的选择”而使用了已知存在弱点的算法如MD5、SHA1、DES或配置如SSL弱加密套件。缺乏“最小权限”和“秘密管理”意识代码中包含了完成功能所需之外的不必要权限或敏感信息。密钥、密码等秘密应该通过环境变量、密钥管理服务等安全方式注入而非硬编码。对外部依赖第三方库的版本和安全状况不敏感项目引入了存在已知漏洞的NPM包或库文件但没有及时更新。注意以上三类根源并非孤立存在一个复杂的漏洞往往是它们共同作用的结果。例如一个存储型XSS漏洞首先是“信任边界模糊”未过滤用户输入然后将危险数据“不安全地存储”到了数据库最后在另一个页面“缺乏输出编码”地展示出来形成了完整的攻击链。3. 典型漏洞场景深度复盘与修复实战接下来我们选取Juice Shop中几个极具代表性的漏洞从漏洞利用攻击者视角到代码审计开发者视角最后进行代码修复完成一次完整的复盘。3.1 SQL注入漏洞从字符串拼接到底层原理漏洞场景Juice Shop的“商品搜索”或“登录”功能。攻击者可以在搜索框输入特定的Payload窃取数据库信息甚至绕过登录。攻击者视角利用在搜索框输入apple UNION SELECT username, password FROM Users--提交后原本查询商品名的SQL语句被篡改联合查询UNION出了用户表中的账号密码。如果密码哈希值强度弱可被离线破解。开发者视角代码审计 找到后端的搜索处理代码例如在routes/products.js中// 漏洞代码示例 router.get(/search, (req, res) { const query SELECT * FROM Products WHERE name LIKE %${req.query.q}% AND deletedAt IS NULL; db.all(query, (error, products) { // ... 返回结果 }); });漏洞根因分析req.query.q是用户可控的输入它被直接拼接进了SQL查询字符串。当用户输入apple UNION ... --时最终的SQL变成SELECT * FROM Products WHERE name LIKE %apple UNION SELECT username, password FROM Users--% AND deletedAt IS NULL--在SQL中表示注释它注释掉了后面的% AND ...使得UNION查询能够顺利执行。这里暴露了两个问题一是直接拼接二是错误处理可能不够可能会将数据库错误信息返回给用户帮助攻击者调整Payload。修复实战代码层面 修复的核心是使用参数化查询Prepared Statements或查询构造器Query Builder。它们能确保用户输入被当作“数据”而非“代码”的一部分来处理。// 修复后代码 - 使用参数化查询 router.get(/search, (req, res) { const searchTerm %${req.query.q}%; // 预处理搜索词添加通配符 const query SELECT * FROM Products WHERE name LIKE ? AND deletedAt IS NULL; db.all(query, [searchTerm], (error, products) { // 使用占位符 ?输入作为参数传入 if (error) { // 重要记录错误到服务器日志但返回给用户通用的错误信息 console.error(Database search error:, error); return res.status(500).json({ error: Search failed }); } res.json(products); }); });修复要点解析参数化查询SQL语句模板中使用?作为占位符。数据库驱动会负责将后续传入的参数[searchTerm]安全地插入到这些位置即使参数中包含SQL元字符如单引号也会被正确转义为普通字符。输入预处理我们在将用户输入req.query.q作为参数前对其进行了格式化添加%通配符。这是一个业务逻辑处理与安全转义分离。安全的错误处理捕获数据库错误但只向用户返回通用信息避免泄露数据库结构等敏感信息。详细的错误应记录在服务器端日志中供开发者排查。实操心得仅仅使用参数化查询有时还不够。如果查询中的表名或列名需要动态生成非常罕见且危险参数化查询无法处理因为占位符只能用于数据值。这种情况下必须使用“白名单”机制进行严格校验。例如如果排序字段sortBy来自用户输入不能直接拼接而应该检查sortBy是否在[name, price, createdAt]这个白名单中。3.2 跨站脚本XSS漏洞上下文是防御的关键漏洞场景Juice Shop的“用户评价”、“联系方式”等允许用户提交内容并展示的功能。攻击者视角利用在评价框输入scriptalert(document.cookie)/script或更隐蔽的img srcx onerroralert(1)。提交后这段脚本被保存到数据库。当其他用户或管理员浏览该评价页面时恶意脚本在其浏览器中执行可能窃取Cookie、发起恶意请求等。开发者视角代码审计 找到前端渲染用户评价的代码// 漏洞代码示例 - 使用innerHTML直接插入未转义的内容 function displayReview(review) { const reviewElement document.createElement(div); reviewElement.innerHTML h4${review.author}/h4 p${review.comment}/p !-- 危险review.comment可能包含HTML/JS -- small评分: ${review.rating}/small ; document.getElementById(reviews-container).appendChild(reviewElement); }漏洞根因分析review.comment是来自数据库的用户数据它被直接拼接进HTML字符串并赋值给innerHTML。浏览器会将其中的script等标签解析为可执行的代码。这里的问题在于开发者混淆了“数据”和“代码”的边界将不可信的数据放入了HTML代码上下文中。修复实战代码层面 防御XSS的核心是输出编码Output Encoding根据数据将要放置的“上下文”选择正确的编码或转义方式。// 修复后代码 - 使用文本节点或安全的API function displayReview(review) { const reviewElement document.createElement(div); const authorHeader document.createElement(h4); authorHeader.textContent review.author; // 使用textContent自动转义 const commentPara document.createElement(p); commentPara.textContent review.comment; // 使用textContent自动转义 const ratingSpan document.createElement(small); // 对于评分它本身是数字但安全起见也使用textContent ratingSpan.textContent 评分: ${review.rating}; reviewElement.appendChild(authorHeader); reviewElement.appendChild(commentPara); reviewElement.appendChild(ratingSpan); document.getElementById(reviews-container).appendChild(reviewElement); } // 或者如果必须使用HTML字符串不推荐则必须手动编码 function escapeHtml(unsafe) { return unsafe .replace(//g, amp;) .replace(//g, lt;) .replace(//g, gt;) .replace(//g, quot;) .replace(//g, #039;); } // 然后在拼接时使用 p${escapeHtml(review.comment)}/p修复要点解析使用安全的DOM API优先使用document.createElement和textContent/innerText来构建DOM。textContent属性会将所有内容视为纯文本自动处理特殊字符这是最安全的方式。避免使用innerHTML除非你确实需要处理富文本HTML如一个博客编辑器否则永远不要使用innerHTML来插入用户数据。如果必须使用必须在服务器端或客户端使用严格的净化库如DOMPurify来处理。理解上下文HTML上下文标签之间如上例使用textContent或对, , , , 进行HTML实体编码。HTML属性上下文如href用户数据需要对引号等进行编码更佳实践是使用setAttribute方法。JavaScript上下文如scriptvar data 用户数据;/script极其危险应避免将用户数据直接放入JS。必须使用JSON序列化JSON.stringify并注意不在script标签内使用。URL上下文如a href用户数据需要验证协议只允许http://,https://,mailto:等并使用encodeURIComponent进行编码。注意事项现代前端框架如React、Vue、Angular在默认情况下都提供了内置的XSS防护因为它们使用声明式的数据绑定和虚拟DOM通常会自动对插值表达式进行转义。但是这并非绝对安全。当你使用v-htmlVue、dangerouslySetInnerHTMLReact或绕过安全绑定的API时就等于关闭了这层防护必须万分小心。永远不要将用户输入传递给这些危险的API。3.3 不安全的反序列化与逻辑漏洞漏洞场景Juice Shop的“购物车”或“优惠券”功能。攻击者通过篡改客户端传来的序列化数据如Cookie、LocalStorage中的购物车对象来操纵商品价格、数量或应用未授权的折扣。攻击者视角利用发现网站将购物车内容以JSON字符串形式存储在客户端的Cookie中。使用浏览器开发者工具编辑Cookie将某商品的价格从10.99改为0.01或将优惠券折扣率从0.19折改为-1买一送一。刷新页面或提交订单后端直接使用了被篡改的数据导致逻辑错误。开发者视角代码审计 找到处理购物车或订单的后端代码// 漏洞代码示例 - 信任并直接解析客户端传来的序列化数据 app.post(/api/checkout, (req, res) { const cartData JSON.parse(req.cookies.cart); // 直接从Cookie解析 let total 0; cartData.items.forEach(item { // 直接使用客户端传来的价格进行计算 total item.price * item.quantity; }); // ... 创建订单 });漏洞根因分析信任客户端数据代码假设Cookie中的cart数据是真实、未被篡改的。价格、数量等关键业务数据应该由服务器端权威决定而不应依赖客户端。缺乏完整性校验没有机制如数字签名、HMAC来验证序列化数据在传输和存储过程中是否被修改。反序列化风险在某些语言如Java、Python中反序列化不受信任的数据可能导致远程代码执行RCE。在Node.js的JSON.parse中虽然RCE风险较低但依然可能导致服务器崩溃通过解析畸形JSON或逻辑错误。修复实战代码层面 修复的核心原则是服务器端应持有所有关键业务数据的“真相源”。// 修复后代码 - 服务器端重建购物车状态 app.post(/api/checkout, async (req, res) { try { // 1. 从Cookie或Session中获取用户提交的商品ID和数量列表非完整对象 const { userId } req.session; const clientCart JSON.parse(req.cookies.cart || {}); // 2. 服务器端根据商品ID从数据库查询真实的价格、库存等信息 const productIds clientCart.items.map(item item.productId); const validProducts await db.getAll( SELECT id, price, name FROM Products WHERE id IN (?) AND deletedAt IS NULL, [productIds] ); // 3. 在服务器端重建购物车使用数据库中的权威价格 const serverCart { items: validProducts.map(dbProduct { const clientItem clientCart.items.find(c c.productId dbProduct.id); return { productId: dbProduct.id, name: dbProduct.name, price: dbProduct.price, // 使用服务器端价格 quantity: Math.max(1, Math.min(clientItem.quantity || 1, 10)) // 限制数量范围 }; }), }; // 4. 计算总价 const total serverCart.items.reduce((sum, item) sum (item.price * item.quantity), 0); // 5. 可选为了用户体验可以将重建后的购物车签名后存回Cookie const signedCart signCartData(serverCart); // 使用HMAC等算法签名 res.cookie(cart, signedCart, { httpOnly: true }); // ... 使用serverCart和total创建订单 res.json({ success: true, total: total }); } catch (error) { console.error(Checkout error:, error); res.status(400).json({ error: Invalid cart data }); } }); // 简单的签名验证函数示例 function signCartData(cartObj) { const payload JSON.stringify(cartObj); const signature crypto.createHmac(sha256, process.env.CART_SECRET) .update(payload) .digest(hex); return ${payload}.${signature}; } function verifyCartData(signedString) { const [payload, signature] signedString.split(.); const expectedSig crypto.createHmac(sha256, process.env.CART_SECRET) .update(payload) .digest(hex); return signature expectedSig ? JSON.parse(payload) : null; }修复要点解析服务器端状态重建不再信任客户端传来的完整业务对象如包含价格的商品项。客户端只传递最小必要信息如商品ID和期望数量服务器根据这些ID从自己的数据库查询最新的、权威的商品信息价格、名称、库存重新构建购物车对象。业务逻辑校验在重建过程中加入业务规则校验。例如检查商品是否有效、数量是否在合理范围内防止负数或过大值、库存是否充足等。数据完整性保护进阶如果需要在客户端存储复杂状态以提升体验如单页应用可以对序列化后的数据进行签名HMAC。服务器在读取时验证签名确保数据未被篡改。但请注意签名只能防篡改不能防窥视敏感信息仍不应存放在客户端。输入验证与错误处理对解析JSON可能抛出的异常进行捕获并返回适当的错误信息避免应用崩溃。实操心得逻辑漏洞往往是最难通过自动化工具发现的因为它们违背的是业务规则而非技术规范。修复这类漏洞要求开发者在设计功能时就清晰地定义“数据的权威来源在哪里”和“哪些检查必须在服务器端执行”。一个简单的自查清单是任何涉及“钱”、“权限”、“状态变更”的操作其核心决策数据价格、折扣、用户角色、状态机必须来自服务器端可信源并经过完整的业务规则校验。4. 开发者日常安全编码习惯养成复盘了具体漏洞我们还需要将安全的意识融入到日常的每一行代码中。以下是一些可以立刻实践的安全编码习惯4.1 将安全工具集成进开发流水线安全不应该只是上线前的“大扫除”而应该贯穿开发始终。静态应用程序安全测试SAST在代码提交或构建阶段使用工具如SonarQube, ESLint with security plugins, Brakeman for Ruby自动扫描源代码查找潜在的安全漏洞模式如直接使用eval()、不安全的正则表达式等。依赖项检查SCA使用npm audit、OWASP Dependency-Check或Snyk等工具定期检查项目所依赖的第三方库是否存在已知漏洞CVE。将检查命令集成到CI/CD流程中阻止包含高危漏洞的构建产物部署。动态应用程序安全测试DAST与交互式测试IAST对运行中的应用程序如测试环境进行自动化漏洞扫描使用ZAP、Burp Suite等工具模拟攻击者的行为。IAST工具则能在代码运行时从内部监控漏洞提供更精准的定位。实操建议在项目的package.json中配置脚本{ scripts: { lint:security: eslint . --config .eslintrc.security.js, audit: npm audit --audit-levelhigh, prepush: npm run lint:security npm run audit } }并考虑将安全扫描作为合并请求Merge Request的必须通过检查项。4.2 代码评审中加入安全视角代码评审Code Review是捕获安全漏洞的绝佳时机。除了关注功能正确性和代码风格评审者应有意识地从安全角度提问。安全评审清单示例输入处理这个API端点/函数的所有输入URL参数、请求体、Headers、Cookie都经过验证了吗验证规则类型、范围、长度、格式是否足够严格输出处理用户可控的数据在输出到HTML、JSON、日志或命令行时是否进行了正确的编码或转义数据库操作是否使用了参数化查询或ORM的安全方法有没有拼接SQL字符串的地方身份与授权这个操作是否检查了当前用户的身份和权限权限检查是基于服务器端的会话信息吗错误处理错误信息是否会泄露系统内部细节堆栈跟踪、数据库结构、文件路径是否返回了统一的、无害的错误响应敏感数据代码中是否有硬编码的密码、API密钥日志里是否会意外记录敏感信息如信用卡号、密码依赖与配置引入的新依赖是否有已知的安全问题配置文件如.env是否被加入了.gitignore4.3 持续学习与威胁建模安全领域在不断发展新的攻击手法和漏洞类型层出不穷。定期关注安全动态订阅OWASP Top 10的更新、关注CVE公告、阅读安全团队的技术博客。Juice Shop靶场本身也会持续更新加入新的挑战。为你的应用进行简单的威胁建模在项目设计阶段或重大功能迭代前花一点时间进行威胁建模。可以问自己几个简单问题资产我的应用里最有价值的数据是什么用户数据、支付信息、商业秘密威胁源谁可能想攻击它脚本小子、竞争对手、有组织的犯罪攻击面他们可能从哪些地方入手登录接口、文件上传、API端点、第三方集成脆弱点我的代码和架构中哪些地方比较脆弱复杂的输入解析、老旧组件、过度的权限缓解措施针对每个可能的攻击路径我有什么防御措施输入验证、输出编码、权限控制、日志监控这个过程不需要非常正式哪怕只是在白板上画一画数据流图标识出信任边界也能极大地提升你对系统安全状况的认知。5. 从靶场到实战构建应用安全基础防线通过Juice Shop的复盘我们看到了漏洞在代码中的具体形态。将这些经验应用到真实项目中意味着要构建多层次的安全防线。这不仅仅是修复单个Bug更是建立一套可持续的安全开发流程和基础架构。5.1 安全编码规范与框架选型团队应制定并遵守一份《安全编码规范》将最佳实践文档化。同时选择本身就注重安全的框架和库能事半功倍。后端框架现代主流框架如Spring Security for Java, Helmet for Express.js, Django for Python都内置了许多安全特性如CSRF保护、安全的Cookie设置、点击劫持防护等。务必了解并正确配置这些特性而不是禁用它们。前端框架如前所述React、Vue等默认提供XSS防护。使用它们官方的状态管理、路由库避免直接操作DOM。数据库ORM使用Sequelize、TypeORM、Hibernate等ORM或查询构造器它们通常强制或强烈推荐使用参数化查询从根源上避免SQL拼接。配置示例Express.js Helmetconst express require(express); const helmet require(helmet); const app express(); // 使用Helmet设置一系列安全相关的HTTP头 app.use(helmet({ contentSecurityPolicy: { // 内容安全策略有效缓解XSS directives: { defaultSrc: [self], styleSrc: [self, unsafe-inline], // 谨慎允许内联样式 scriptSrc: [self], // 只允许加载同源脚本 imgSrc: [self, data:, https://trusted-cdn.com], }, }, hsts: { maxAge: 31536000, includeSubDomains: true }, // 强制HTTPS })); // 其他安全中间件 app.use(express.json({ limit: 10kb })); // 限制请求体大小防止DoS const rateLimit require(express-rate-limit); const limiter rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }); // 限流 app.use(/api/, limiter);5.2 纵深防御与监控响应单一防线被突破是可能的因此需要纵深防御Defense in Depth。网络层使用WAFWeb应用防火墙作为第一道防线过滤常见的攻击流量如SQL注入、XSS扫描器流量。应用层如上所述做好输入验证、输出编码、权限控制。数据层对敏感数据密码进行强哈希加盐存储使用bcrypt、scrypt、Argon2对传输中的数据进行TLS加密。运维层保持操作系统、运行时、数据库、所有依赖库的及时更新。使用最小权限原则运行服务。监控与响应日志集中化与分析记录所有重要的操作登录、支付、数据导出、错误和异常访问模式。使用ELK Stack或类似工具进行集中管理和分析。设置安全警报对多次登录失败、异常时间访问、敏感操作频率过高等行为设置阈值告警。制定应急响应计划当真的发生安全事件时团队应该做什么如何隔离、调查、修复和通知提前规划好能减少损失。5.3 将安全视为功能需求最后也是最根本的一点是在团队文化和流程中将“安全”视为与“功能”、“性能”、“用户体验”同等重要的非功能性需求。在需求阶段考虑安全在编写用户故事或需求文档时加入安全验收标准Security Acceptance Criteria。例如“作为一个用户我修改个人资料时只能修改自己的资料系统应阻止我通过修改URL参数来修改他人资料。”安全培训常态化定期为开发团队组织内部的安全编码培训分享像Juice Shop这样的实战案例复盘。鼓励开发人员去考取基础的安全认证如CISSP Associate, CompTIA Security。建立安全冠军网络在开发团队中培养对安全有热情和知识的“安全冠军”Security Champion他们可以充当团队内的安全顾问在代码评审中提供安全视角并传播安全最佳实践。Juice Shop靶场是一个绝佳的起点但它不是终点。真正的安全始于每一行被谨慎编写的代码成于每一个被认真执行的安全流程最终固化在团队对安全持续重视的文化之中。当你下次编写一个接收用户输入的函数或设计一个API端点时不妨多问自己一句“如果我是攻击者我会怎么利用这里” 这种攻防思维的建立或许是Juice Shop带给开发者最宝贵的礼物。
返回列表