在 OWASP Juice Shop 中登录注入
在 OWASP Juice Shop 中登录注入
目录
- 引言:什么是“登录注入”(SQL Injection)
- 为什么
'' OR '1'='1'有时“不好使”?(以 Juice Shop 为例) - 演示:不安全的登录实现(说明为什么会被利用)
- 安全实现:参数化查询与 ORM 的正确用法(含示例代码)
- 在 Juice Shop 上调试与验证(新手可跟着做)
- 常见误区与注意事项
- 总结
- 参考与延伸阅读
- 具体操作
1. 引言:什么是“登录注入”
SQL 注入(SQL Injection) 是一种经典的 Web 漏洞:攻击者把恶意的 SQL 片段当作输入,服务器把它拼入 SQL 语句后执行,从而实现绕过认证、读取或修改数据库等行为。
举例(理论上):
- 表单里把
email填成anything' OR '1'='1 - 若后台直接拼接字符串生成 SQL:
因为SELECT * FROM Users WHERE email = 'anything' OR '1'='1' AND password = '...';'1'='1'永远为真,查询可能返回第一条记录,实现“绕过登录”。
2. 为什么 '' OR '1'='1' 有时“不好使”?(以 Juice Shop 为例)
如果在 Juice Shop 中输入传统的 '' OR '1'='1' 类 payload,却得到错误 SQLITE_ERROR: unrecognized token 或登录失败,那通常说明:
- 后台没有直接拼接原始 SQL 字符串,而是使用了参数化查询或ORM(如 Sequelize),这些做法会把用户输入当成数据而不是 SQL 代码,从而阻止经典注入方法。
- 报错
unrecognized token多半表示 SQL 在解析时碰到语法问题(例如字符串引号未正确配对),而不是“注入成功”。
总结:现代应用通常采用更安全的数据绑定方式,所以老方法在这类环境里常常无效或被拦截。
3. 演示:不安全的登录实现(仅用于说明)
下面演示一个非常不安全的做法,目的是帮助理解为什么会被利用。
Node.js(不安全示例)
// 非常不安全的写法:直接把用户输入拼接到 SQL
app.post('/login', async (req, res) => {
const email = req.body.email;
const password = req.body.password;
const sql = `SELECT * FROM Users WHERE email = '${email}' AND password = '${password}'`;
const row = db.get(sql); // 假设 db.get 能执行 SQL 并返回行
if (row) {
req.session.user = row;
res.send('登录成功');
} else {
res.send('账号或密码错误');
}
});
- 如果
email是anything' OR '1'='1,拼接后 SQL 的结构被破坏,攻击者可能借此绕过认证。 - 这是拼接字符串生成 SQL的典型易被利用场景。
4. 安全实现(防护示例) — 参数化查询 & ORM
4.1 参数化查询(以 sqlite3 为例)
参数化查询把用户输入作为“数据”,不会改变 SQL 的语法结构,从而防止注入。
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('mydb.sqlite');
app.post('/login', (req, res) => {
const { email, password } = req.body;
// 使用 ? 占位符,并把参数作为数组传入
const sql = `SELECT * FROM Users WHERE email = ? AND password = ?`;
db.get(sql, [email, password], (err, row) => {
if (err) {
res.status(500).send('服务器错误');
return;
}
if (row) {
req.session.user = row;
res.send('登录成功');
} else {
res.send('账号或密码错误');
}
});
});
注释说明:
?是占位符,数据库驱动会把email和password当作数据安全插入,而不是拼接成 SQL 片段。- 即便用户输入包含
' OR 1=1 --之类的文本,也只会被当成字符串数据,不会改变 SQL 的逻辑。
4.2 使用 ORM(Sequelize)示例
ORM 常常提供更高层的接口,且内部默认做参数绑定。
// 假设已定义 User 模型
const { User } = require('./models');
app.post('/login', async (req, res) => {
const { email, password } = req.body;
try {
// ORM 的 where 子句会安全地把值当作参数
const user = await User.findOne({ where: { email: email, password: password } });
if (user) {
req.session.user = user;
res.send('登录成功');
} else {
res.send('账号或密码错误');
}
} catch (err) {
res.status(500).send('服务器错误');
}
});
5. 在 Juice Shop上调试与验证
前提:在 Juice Shop 实例的练习环境中操作。
步骤 1:打开浏览器开发者工具(Network 面板)
- 填入想测试的
email与password,点击登录。 - 切换到 Network → 找到
/rest/user/login(或相关请求)→ 查看 Request Payload。- 会看到发送的 JSON,例如:
{ "email": "' OR 1=1--", "password": "123" }
- 会看到发送的 JSON,例如:
步骤 2:观察响应与控制台错误
- 如果后端返回
SQLITE_ERROR: unrecognized token:说明在底层 SQL 的解析阶段出现语法错误(可能是引号不匹配或非法字符)。 - 如果后端返回
Invalid email or password:说明查询执行但未匹配到任何用户(注入未成功)。
步骤 3:尝试不同 payload
- 常见尝试(请在 Juice Shop 本地环境中试验):
admin'--admin' OR '1'='1- URL 编码版本:
%27%20OR%201%3D1--
- 在使用 ORM 时,传统 payload 常常失效或报错;Juice Shop 的多个关卡设计了不同的防护或故意弱点,所以需要有针对性地尝试。
步骤 4:注意错误栈和提示
- 如果错误堆栈里出现
Sequelize,说明应用通过 ORM 操作数据库,经典注入方式往往被阻止。 - 如果错误堆栈里直接出现 SQL 语句字符串,说明可能存在拼接 SQL 的风险区域(但仍需谨慎判断)。
6. 常见误区与注意事项
- 误区:所有网站都能用
OR 1=1绕过
事实:现代应用广泛使用参数化查询或 ORM,经典 payload 很多时候不起作用。 - 误区:出现
SQLITE_ERROR就是成功注入
事实:这是 SQL 解析错误,说明 payload 形成了非法 SQL 语法,而不是注入成功。 - 误区:学会注入就能“入侵任何站点”
事实:未经授权的测试是违法的;学习仅限靶场/授权范围内。 - 防护要点:
- 使用参数化查询 / ORM(安全绑定参数)。
- 密码使用强哈希(bcrypt/argon2),不要明文保存。
- 对输入做最小必要的校验(白名单优先),但不要把校验当作惟一防线。
- 使用安全审计与 WAF 作为补充手段。
7. 总结
- 你在 Juice Shop 中尝试的
' OR '1'='1'类 payload 无效,极可能因为后端使用了 ORM(Sequelize)或参数化查询,这些会把输入当成数据而非 SQL 代码。 - 出现
SQLITE_ERROR: unrecognized token多为语法/解析问题,而不是注入成功。 - 进行安全测试时,应先理解目标应用如何构造 SQL(拼接 vs 参数化 vs ORM),然后选择合适的测试策略。
- 始终在被授权的环境中学习并遵守法律与道德规范。
8. 参考与延伸阅读
- OWASP SQL Injection — https://owasp.org/www-project-top-ten/ (请在浏览器中搜索“OWASP SQL Injection”)
- OWASP Juice Shop 官方仓库与文档(查看其挑战说明和源码)
- 关于密码存储:bcrypt / argon2 文档
9. 具体操作
输入
`
发现报错显示了SQL语句,说明email输入框存在SQL注入漏洞

输入
' OR 1=1--

结果


浙公网安备 33010602011771号