头图

如何设置正确的网站安全头部

近年来,推出“安全评分卡”的公司如雨后春笋般涌现,这已开始成为企业销售过程中的一个考量因素。我从客户那里了解到,他们对于那些被评为低分的供应商持有顾虑,并且至少有一次是基于这种评分初步改变了采购决策。

经过调查,我发现这些评分公司计算公司安全得分的方式,主要是结合 HTTP 安全头的使用情况和 IP 信誉。

IP 信誉是基于黑名单和垃圾邮件列表以及公开的 IP 所有权数据。只要你的公司不发送垃圾邮件,并且能迅速检测并停止恶意软件感染,通常这些数据应该是干净的。HTTP安全头的使用情况的计算类似于 Mozilla Observatory 的工作方式。

因此,对大多数公司而言,其评分主要由公开面对的网站设置的安全头决定。

正确设置头部可以快速完成(通常无需大量测试),可以提升网站安全性,现在还可以帮助你在注重安全的客户中赢得交易。

我对这种测试方法和这些公司要求的高昂价格方案持怀疑态度。我认为它与真实的产品安全相关性并不大。然而,它确实增加了设置头部并做好这一工作的重要性。

在这篇文章中,我将介绍常被评估的头部信息,为每个头部推荐安全值,并给出一个头部设置示例。在文章的最后,我将包括常见应用程序和 Web 服务器的示例设置。

重要的安全头部

内容安全策略(Content-Security-Policy)

CSP 用于通过指定哪些资源允许加载来防止跨站脚本攻击。在此列表中,这可能是创建和维护最耗时并且最容易出现风险的项目。开发 CSP 时,请仔细测试——阻止站点以有效方式使用的内容源将破坏站点功能。

创建初稿的一个好工具是 Mozilla 实验室 CSP 浏览器扩展。在浏览器中安装此扩展,彻底浏览你想为其创建 CSP 的网站,然后在你的站点上使用生成的 CSP。理想情况下,还应该重构 JavaScript,以便没有内联脚本留下,这样你就可以移除“unsafe inline”指令。

以下是一个好的起始 CSP(在真实站点上可能需要许多修改)。在每个部分中添加你的站点包含的域名。

# 默认情况下仅允许来自当前站点的内容
# 仅允许来自当前站点和imgur.com的图片
# 不允许Flash和Java等对象
# 仅允许来自当前站点的脚本
# 仅允许来自当前站点的样式
# 仅允许来自当前站点的框架
# 限制<base>标签中的URL为当前站点
# 仅允许表单提交到当前站点
Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';

严格传输安全(Strict-Transport-Security)

此头部告诉浏览器该网站应仅通过 HTTPS 访问——当你的站点启用了 HTTPS 时,始终启用此选项。如果你使用子域,我还建议在任何使用的子域上强制执行此项。

Strict-Transport-Security: max-age=3600; includeSubDomains

X-内容类型选项(X-Content-Type-Options)

此头部确保应用程序设置的 MIME 类型被浏览器尊重。这可以帮助防止某些类型的跨站脚本绕过。

它还减少了当开发者标记页面为“HTML”但浏览器认为它看起来像 JavaScript 并尝试将其渲染为 JavaScript 时出现的意外应用行为。此头部将确保浏览器始终尊重服务器设置的 MIME 类型。

X-Content-Type-Options: nosniff

缓存控制(Cache-Control)

对于含有敏感数据的页面,如用户页面或客户结账页面,应设置为无缓存。这样做的一个原因是为了防止在共用计算机上的人通过按后退按钮或查阅历史记录时能够查看个人信息。

然而,很少变化的页面,如静态资源(图片、CSS 文件和 JS 文件),是可以缓存的。这可以在页面级别上逐个设置,或者在服务器配置中使用正则表达式来处理。

# 默认情况下不进行缓存
Header set Cache-Control no-cache

# 缓存静态资源1天
<filesMatch ".(css|jpg|jpeg|png|gif|js|ico)$">
    Header set Cache-Control "max-age=86400, public"
</filesMatch>

Expires

这个头部设置缓存当前请求应到期的时间。如果设置了 Cache-Control 的 max-age 头部,则将忽略 Expires 头部,因此我们只在测试时没有考虑到 cache-control 的情况下设置它。

出于安全考虑,我们将假设浏览器不应缓存任何内容,因此我们将其设置为始终评估为过去的日期。

Expires: 0

X-Frame-Options

此头部标明是否允许在 iFrame 中显示站点。

如果一个恶意站点将你的网站放在一个 iFrame 中,那么恶意站点可以通过运行一些 JavaScript 来执行点击劫持攻击,从而捕获在 iFrame 上的鼠标点击,然后代表用户与站点交互(不一定点击他们认为点击的位置!)。

除非你明确使用框架,否则应该始终将其设置为拒绝,如果你正在设计中使用框架与另一个站点,则应设置为同源。如果你正在与另一个站点设计使用框架,你也可以在这里列出其他域。

也应该注意,此头部已由 CSP 的 frame-ancestors 指令取代。我仍然推荐现在设置此头部来满足工具的需求,但将来这可能会被逐渐淘汰。

X-Frame-Options: deny

访问控制允许来源(Access-Control-Allow-Origin)

告诉浏览器哪些其他站点的前端 JavaScript 代码可能请求该页面。除非你需要设置这一点,否则默认通常是正确的设置。

例如,如果 SiteA 提供一些 JavaScript 想要对 siteB 发起请求,那么 siteB 必须在响应中带上指明 SiteA 被允许发起此请求的头部。如果你需要设置多个来源,查看 MDN 上的详细页面

这可能有点混淆,所以我制作了一个图表来说明这个头部的功能:

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4302ac22dbde4ecf81cbaca766505e61~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=928&h=610&s=51515&e=png&b=ffffff)

数据流与访问控制允许来源
Access-Control-Allow-Origin: http://www.one.site.com

设置 Cookie(Set-Cookie)

确保你的 cookie 仅通过 HTTPS(加密的)发送,并且它们不可通过 JavaScript 访问。只有当你的站点也支持 HTTPS 时,你才能发送 HTTPS cookie,这是你应该始终设置的。你应该始终设置以下标志:

  • Secure
  • HttpOnly

一个 cookie 定义的示例:

Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly

更多信息请参考 Mozilla 的出色文档

X-XSS-Protection

此头部指示浏览器在检测到跨站脚本攻击时停止执行。设置此项通常风险较低,但在投入生产前应进行测试。

X-XSS-Protection: 1; mode=block

Web 服务器示例配置

通常最好在服务器配置中站点范围内添加头部。Cookie 是个例外,因为它们通常在应用程序本身中定义。

在向站点添加任何头部之前,我建议首先检查 observatory 或手动查看头部以了解哪些已经设置。一些框架和服务器将自动为您设置其中的一些,因此只需实现您需要或希望更改的那些。

Apache 配置

在.htaccess 中的一个示例 Apache 设置:

<IfModule mod_headers.c>
    ## CSP
    Header set Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';

    ## General Security Headers
    Header set X-XSS-Protection: 1; mode=block
    Header set Access-Control-Allow-Origin: http://www.one.site.com
    Header set X-Frame-Options: deny
    Header set X-Content-Type-Options: nosniff
    Header set Strict-Transport-Security: max-age=3600; includeSubDomains

    ## Caching rules
    # 不缓存为默认
    Header set Cache-Control no-cache
    Header set Expires: 0

    # 缓存静态资产1天
    <filesMatch ".(ico|css|js|gif|jpeg|jpg|png|svg|woff|ttf|eot)$">
        Header set Cache-Control "max-age=86400, public"
    </filesMatch>

</IfModule>

Nginx 配置

## CSP
add_header Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';

## General Security Headers
add_header X-XSS-Protection: 1; mode=block;
add_header Access-Control-Allow-Origin: http://www.one.site.com;
add_header X-Frame-Options: deny;
add_header X-Content-Type-Options: nosniff;
add_header Strict-Transport-Security: max-age=3600; includeSubDomains;

## Caching rules
# 默认情况下不缓存
add_header Cache-Control no-cache;
add_header Expires: 0;

# 缓存静态资产1天
location ~* .(?:ico|css|js|gif|jpe?g|png|svg|woff|ttf|eot)$ {
    try_files $uri @rewriteapp;
    add_header Cache-Control "max-age=86400, public";
}

应用程序级别的头部设置

如果你无法访问 Web 服务器,或者有复杂的头部设置需求,你可能希望在应用程序本身中设置这些头部。这通常可以通过框架中间件为整个站点执行,并且可以按需为单个响应设置头部。

为简洁起见,我在例子中只包含了一个头部。通过这种方法以相同的方式添加所有需要的头部。

Node.js 和 Express:

在全局添加挂载路径:

app.use(function(req, res, next) {
    res.header('X-XSS-Protection', '1; mode=block');    
    next();
});

Java 和 Spring:

虽然我不是非常熟悉 Spring,但 Baeldung 提供了一个在 Spring 中设置头部的非常好的指南。

PHP:

我不熟悉各种 PHP 框架。寻找可以处理请求的中间件。对于单个响应,设置非常简单。

header("X-XSS-Protection: 1; mode=block");

Python / Django

Django 包含可配置的 安全中间件 ,可以为你处理所有这些设置。首先启用这些功能。

对于特定页面,你可以像处理字典一样处理响应。如果你试图以这种方式设置缓存头,应该考虑 Django 处理缓存的特殊方式。

response = HttpResponse()
response["X-XSS-Protection"] = "1; mode=block"

结论

设置 HTTP 头部是相对快速且简单的过程。你将在网站安全性方面获得相当大的提升,包括数据保护、防范跨站脚本和点击劫持攻击。

你也确保不会因为依赖这些信息的公司安全评分而失去未来的商业机会。这种做法似乎在不断增加,我预计在未来几年里,它将继续在企业销售中发挥作用。


Apifox
23 声望4 粉丝

Apifox 是 API 文档、API 调试、API Mock、API 自动化测试一体化平台。Apifox = Postman + Swagger + Mock + JMeter