img-src 安全策略详解与实战中的那些坑

青燕 安全 阅读 1,905
赞 33 收藏
二维码
手机扫码查看
反馈

项目初期的技术选型

最近刚搞定一个电商项目,其中有一个需求是图片安全加载。这个需求听起来简单,但实际操作起来还是挺麻烦的。项目背景是这样的:我们需要在页面上展示大量的商品图片,但是这些图片来自不同的供应商,我们不能保证每张图片都是安全的。所以,我们需要一个机制来控制哪些域名的图片是可以加载的。

img-src 安全策略详解与实战中的那些坑

初步尝试:CSP配置

一开始,我想到的是使用CSP(Content Security Policy)来限制图片的来源。CSP是一个非常强大的工具,可以通过HTTP头部来定义内容的安全策略。我们的主要关注点是img-src指令,它用来指定哪些源可以加载图片。

我先在服务器端配置了CSP头,代码如下:

Content-Security-Policy: img-src 'self' https://trusted-source.com;

这样配置后,理论上只有同源和trusted-source.com的图片才能加载。然而,实际效果并不理想,因为很多图片的来源是动态的,无法提前确定。

最大的坑:动态图片源的问题

项目中遇到了一个大坑,就是图片的来源是动态的。有些图片是从用户上传的内容中获取的,而这些内容可能包含任意URL。这时候,静态的CSP配置就显得力不从心了。

我开始尝试用JavaScript来动态设置CSP,结果发现这根本行不通。浏览器出于安全考虑,不允许JavaScript修改CSP策略。折腾了半天发现,只能通过服务器端动态生成CSP头部来解决这个问题。

动态CSP的实现

经过一番研究,我决定在服务器端动态生成CSP头部。具体实现方法是,在请求返回时,根据图片的实际来源动态添加img-src指令。这里我用了Node.js和Express来演示:

const express = require('express');
const app = express();

app.use((req, res, next) => {
  const allowedSources = ['self', 'https://trusted-source1.com', 'https://trusted-source2.com'];
  const cspHeader = img-src ${allowedSources.join(' ')};
  res.setHeader('Content-Security-Policy', cspHeader);
  next();
});

app.get('/product/:id', (req, res) => {
  // 获取产品信息
  const product = getProductById(req.params.id);
  res.json(product);
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

这段代码中,我们在中间件中动态生成了CSP头部,并将其添加到响应头中。这样,每次请求时都会根据预设的允许来源列表来生成CSP策略。

前端处理:确保图片加载安全

虽然有了动态CSP,但前端仍然需要做一些处理来确保图片加载的安全性。我在前端使用了一个简单的函数来检查图片的来源是否合法:

function isImageSourceAllowed(src) {
  const allowedSources = ['https://trusted-source1.com', 'https://trusted-source2.com'];
  return allowedSources.some(allowedSrc => src.startsWith(allowedSrc));
}

function loadImages(images) {
  images.forEach(img => {
    if (isImageSourceAllowed(img.src)) {
      const imgElement = document.createElement('img');
      imgElement.src = img.src;
      document.body.appendChild(imgElement);
    } else {
      console.warn(Image from ${img.src} is not allowed);
    }
  });
}

// 示例图片数组
const images = [
  { src: 'https://trusted-source1.com/image1.jpg' },
  { src: 'https://untrusted-source.com/image2.jpg' },
  { src: 'https://trusted-source2.com/image3.jpg' }
];

loadImages(images);

这个函数isImageSourceAllowed会检查图片的来源是否在允许的列表中。如果不在列表中,就会输出一条警告并跳过加载该图片。

最终的解决方案

通过动态CSP和前端的双重检查,我们基本解决了图片安全加载的问题。虽然还有一些小问题没有完全解决,比如偶尔会有图片加载失败的情况,但总体来说影响不大。

总结一下,这次项目的主要收获有以下几点:

  • 动态CSP的重要性:静态的CSP配置在面对动态内容时不够灵活,需要通过服务器端动态生成CSP头部。
  • 前后端结合:仅靠CSP还不够,前端也需要做一些检查来确保安全性。
  • 性能优化:虽然动态生成CSP头部增加了服务器的负担,但可以通过缓存等手段进行优化。

回顾与反思

这个项目让我深刻体会到,安全问题不仅仅是技术问题,还需要结合业务场景来综合考虑。动态CSP虽然解决了大部分问题,但也带来了一些额外的复杂性。后续我会继续探索更简洁、更高效的解决方案。

以上是我的项目经验,希望对你有帮助,欢迎交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论