缓解 node/express 静态资产请求中的反映 XSS

Mitigating reflected XSS in node/express requests for static assets

我已经 运行 针对我的 node(express)/angular 应用程序进行了渗透测试工具 (Burp),它在尝试 GET 请求时识别了一个反映的 XSS 漏洞对于静态资产(当用户与应用程序交互时发出的任何请求都没有发现明显的漏洞)。

问题详情为:

The name of an arbitrarily supplied URL parameter is copied into a JavaScript expression which is not encapsulated in any quotation marks. The payload 41b68(a)184a9=1 was submitted in the name of an arbitrarily supplied URL parameter. This input was echoed unmodified in the application's response.

This behavior demonstrates that it is possible to inject JavaScript commands into the returned document. An attempt was made to identify a full proof-of-concept attack for injecting arbitrary JavaScript but this was not successful. You should manually examine the application's behavior and attempt to identify any unusual input validation or other obstacles that may be in place.

通过向请求传递任意 url 参数来测试漏洞,如下所示:

GET /images/?41b68(a)184a9=1

回复是:

HTTP/1.1 404 Not Found
X-Content-Security-Policy: connect-src 'self'; default-src 'self'; font-src 'self'; frame-src; img-src 'self' *.google-analytics.com; media-src; object-src; script-src 'self' 'unsafe-eval' *.google-analytics.com; style-src 'self' 'unsafe-inline'
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
Strict-Transport-Security: max-age=10886400; includeSubDomains; preload
X-Download-Options: noopen
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8
Content-Length: 52
Date: Wed, 08 Oct 2015 10:46:43 GMT
Connection: close

Cannot GET /images/?41b68(a)184a9=1

你可以看到我有 CSP(使用 Helmet 来实现)和其他针对漏洞利用的保护措施。该应用程序通过 https 提供,但不需要用户身份验证。 CSP 将请求限制为仅应用程序的域加上 google 分析。

渗透测试报告建议验证输入(我是,但如果我不是,那肯定会使包括用户发送的数据在内的请求不安全?),并编码 html angular默认情况下。

我真的很难找到一种解决方案来防止或减轻这些对静态资产的请求:

更新

对此进行了一些调查后,似乎至少部分缓解措施是 escape data in url param values and sanitize the input in the url

url 的转义已经到位,因此:

curl 'http://mydomain/images/?<script>alert('hello')</script>'

returns

Cannot GET /images/?&lt;script&gt;alert(hello)&lt;/script&gt;

我还在上面放了 express-sanitized

但是,如果我curl原来的测试请求参数仍然被反射回来。

curl 'http://mydomain/images/?41b68(a)184a9=1'
Cannot GET /images/?41b68(a)184a9=1

这是您所期望的,因为 html 没有被插入到 url 中。

对静态资产的 GET 请求的响应都由 app.use(express.static('static-dir')) 处理,因此查询被传递到这里。 express.static 基于 serve-static which depends on parseurl.

问题的原因是,对于无效的 GET 请求,表达将 return 类似于:

Cannot GET /pathname/?yourQueryString

这在很多情况下都是有效的响应,即使对于提供静态资产也是如此。然而,在我的情况下,我确信对于其他人来说,对静态资产的唯一有效请求将是这样的:

GET /pathname/your-file.jpg

我有一个自定义 404 处理程序,它 return 是一个数据对象:

var data = {
    status: 404,
    message: 'Not Found',
    description: description,
    url: req.url
}; 

这仅针对 app.js 中的无效模板请求处理:

app.use('/template-path/*', function(req, res, next) {
    custom404.send404(req, res);
});

我现在已经为对静态文件夹的请求添加了显式处理程序:

app.use('/static-path/*', function(req, res, next) {
        custom404.send404(req, res);
});

我也可以选择在 404 returned:

之前删除请求查询参数
var data = {
    status: 404,
    message: 'Not Found',
    description: description,
    url: url.parse(req.url).pathname // needs a var url = require('url')
};