服务器崩溃¶
ID: js/server-crash
Kind: path-problem
Security severity: 7.5
Severity: warning
Precision: high
Tags:
- security
- external/cwe/cwe-248
- external/cwe/cwe-730
Query suites:
- javascript-code-scanning.qls
- javascript-security-extended.qls
- javascript-security-and-quality.qls
服务器处理来自客户端的请求,直到服务器管理员有意终止。导致服务器端未捕获异常的客户端请求会导致当前服务器响应生成失败,并且不应影响后续的客户端请求。
然而,在某些情况下,未捕获的异常会导致整个服务器突然终止。这种行为非常不可取,尤其是在它允许恶意用户随意关闭服务器的情况下,这是一种有效的拒绝服务攻击。
建议¶
确保处理客户端请求不会导致未捕获的异常突然终止整个服务器。
示例¶
以下服务器代码在将数据保存到该路径之前检查客户端提供的文件路径是否有效。可以合理地预期服务器在请求包含无效文件路径的情况下会返回错误。但是,服务器反而抛出一个异常,该异常在异步回调调用 (fs.access(...)
) 的上下文中未被捕获。这会导致整个服务器突然终止。
const express = require("express"),
fs = require("fs");
function save(rootDir, path, content) {
if (!isValidPath(rootDir, req.query.filePath)) {
throw new Error(`Invalid filePath: ${req.query.filePath}`); // BAD crashes the server
}
// write content to disk
}
express().post("/save", (req, res) => {
fs.access(rootDir, (err) => {
if (err) {
console.error(
`Server setup is corrupted, ${rootDir} cannot be accessed!`
);
res.status(500);
res.end();
return;
}
save(rootDir, req.query.path, req.body);
res.status(200);
res.end();
});
});
为了解决这个问题,服务器可以使用 try/catch
块显式捕获异常,并生成适当的错误响应。
// ...
express().post("/save", (req, res) => {
fs.access(rootDir, (err) => {
// ...
try {
save(rootDir, req.query.path, req.body); // GOOD exception is caught below
res.status(200);
res.end();
} catch (e) {
res.status(500);
res.end();
}
});
});
为了简化异常处理,建议改用 async/await 语法而不是使用回调,这允许将整个请求处理程序包装在 try/catch
块中。
// ...
express().post("/save", async (req, res) => {
try {
await fs.promises.access(rootDir);
save(rootDir, req.query.path, req.body); // GOOD exception is caught below
res.status(200);
res.end();
} catch (e) {
res.status(500);
res.end();
}
});