给 Web 端网站添加水印

背景

前段时间在公司遇到有人通过后台管理系统通过截图的方式泄漏数据。于是提出了一个需求,给后台网站添加水印的同时希望用户看不见,但是我们能通过截屏的图片解析出水印的内容,这样就可以知道是会泄漏的。

经过调研

在 web 端加水印的方式一般都是通过 canvas 绘制水印,然后通过 css 的 background 属性设置背景图片,并且平铺开来。

我们可以通过下面的 JavaScript 代码来实现一个简单的水印,代码如下:

function watermark({ width = 100, height = 100, content, debug = false }) {
  // 通过 canvas 绘制水印内容
  const createWaterMark = content => {
    const canvas = document.createElement('canvas')
    canvas.width = width
    canvas.height = height
    const ctx = canvas.getContext('2d')
    ctx.rotate(-10 * Math.PI / 180)
    ctx.font = 'bold 24px serif'
    ctx.fillStyle = debug ? 'rgba(255,0,0,1)' : 'rgba(255,0,0,0.005)'
    const lines = content.split(',')
    for (const line of lines) {
      ctx.fillText(line, 0, 50)
      ctx.translate(0, 30)
    }
    return canvas.toDataURL()
  }

  // 创建一个 `div#watermark` 元素并添加到 `body` 上
  let watermarkDiv = document.querySelector('#watermark')
  if (!watermarkDiv) watermarkDiv = document.createElement('div')
  const base64Url = `url(${createWaterMark(content)})`
  watermarkDiv.setAttribute('id', 'watermark')
  watermarkDiv.setAttribute('style', `
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 9999999999;
    pointer-events: none;
    background-repeat: repeat;
    background-image: ${base64Url};
  `)
  document.body.appendChild(watermarkDiv)
}

本页面已经加载了此 watermark 方法

watermark({ content: 'yuler', debug: true })

可以看见水印,然后让我们运行透明模式

watermark({ content: 'yuler' })

可以看见透明样式的水印的样式,就无法通过肉眼看见水印。

因为给设置一个透明度很高的水印,这样用户就可以看不见,但是通过截屏的方式水印依然在上面。 然后我通过 PhotoShop 调节曲线,就可以看到水印的内容。

这里我保存好了,一个曲线配置 watermark.acv,我们可以通过这个曲线配置来显示出水印

通过 PhotoShop 工具

通过 ffmpeg

同时可以通过 ffmpeg 来实现 PhotoShop 调节图片曲线的功能。

ffmpeg -i input.png -vf "curves=psfile=watermark.acv:interp=pchip" -update 1 output.png

输出结果

inputoutput

结论

既然可以通过 ffmpeg 命令行来将透明水印显示出来,那么我就用 Rails 写了个简单的 web 应用。仓库地址:yuler/watermark 稍后会部署到 watermark.yuler.dev 域名下