寰宇撷光

返回

1 知识分享#

1.1 什么是隐写?#

隐写术(Steganography) 这一名词早在文艺复兴时期就已出现。很多同学小时候玩过的秘密写字(紫外线照射使字迹显现)也可以说是隐写的一种。而现在的数字隐写术在是信息安全杂项门类下的一个小分支,主要研究将信息隐藏在数字载体中(一般是公开媒介)。信息隐写不仅要避免信息被接收者以外的人截获,更旨在让他人根本无法知晓信息传递的发生。信息在传递过程中的安全性和秘密性都有所保障。

在考量一种隐写方法时,我们通常从隐蔽性和鲁棒性两个角度来进行评判。其中隐蔽性指让人无法察觉隐藏信息的存在;而鲁棒性则指:在一些干扰或是刻意攻击存在的情况下,隐藏的信息能够复原的程度。采用了隐写技术的电子水印,则更注重鲁棒性的提升,以防止不法分子将版权信息从作品中去除。

而图像隐写则是以图像作为载体,将信息隐藏在图片中。根据信息隐藏的位置的不同,可以将隐写方式分为空间域隐写和变换域隐写两种。

1.2 LSB隐写术#

网上已经有很多关于LSB(最低有效位)隐写原理的分析了,大家可以直接在下面的链接参考相关内容。

简而言之,LSB隐写是一种将秘密信息嵌入到数字载体文件(如图像、音频或视频)中的隐写技术,其核心原理是利用人类感知系统对载体文件最低有效位(LSB)的不敏感性。

以24位的图像为例,每个像素由红、绿、蓝三个通道的8位值表示,每个通道的LSB(即第202^0位)对整体颜色影响极小。

用24位数值(RGB各8位)存储的图像

隐写时,秘密信息(如二进制数据)被逐位替换载体文件中每个字节的LSB,例如将秘密比特”1”嵌入到像素通道值110100101101001\textcolor{red}{\underline{0}}(十进制105)中,通过修改LSB变为110100111101001\textcolor{red}{\underline{1}}(十进制106),视觉差异几乎不可察觉。按照类似“藏尾诗”的形式连续嵌入大量信息,提取时只需读取载体文件所有字节的LSB并按顺序拼接即可恢复原始信息。

LSB隐写的容量取决于载体文件大小,但可能因统计特性异常(如LSB分布不均)而被检测,因此常结合加密或随机化嵌入位置(如通过密钥控制)增强隐蔽性。

2 项目实践#

在这之前,我写程序还从未有落实到生活中过,基本上都是写解题的代码,最多也只能说是写过一些临时的小脚本,更不用说独立地完成一个有实际作用的小项目了。于是刚好就趁这个学期Python项目作业的机会,来动手试一下从零开始要怎么完成一个项目。

而关于为什么和大家都在做的爬虫、数据可视化背道而驰,选择了一个冷门的选题,大概是源于对信息安全的兴趣。可惜没法在本科有更多的机会接触到这一学科,遂打算在有空的时候自己慢慢研究。

已经完成的项目地址:https://github.com/marshuni/LSB-Steganography

2.1 项目构思#

项目整体为一个基于Python的使用工具类脚本,可以实现以下两个功能:

  • 将文本等信息隐藏入一张图像 :在命令行中使用hide关键字,并给出载体图像路径、载荷文件路径(可以是图像或文本文件)等信息,将信息隐藏入载体图像。
  • 从包含隐藏信息的图像中取出信息:在命令行中使用extract关键字,并给出含有隐藏信息的图片文件路径,输出文件路径等信息,从而取出隐藏的内容。

以下是隐藏功能的实现流程图。可以看出程序被封装入了Converter, Crypter, LSB 三个模块,分别实现不同的功能。

除此之外还有主程序main.py 负责处理用户输入的命令,并控制上述模块对数据进行操作。

2.2 项目展示#

使用《滕王阁序》作为载体文本,将其隐藏入一个图片文件中。

原图

藏有隐藏信息的文件

不难发现,两张图片使用肉眼看不出任何区别。

藏有隐藏信息的文件

2.3 代码实现#

以下的代码为编写过程中各部分功能的原型。在项目中,我对这些原型进行了一定重构,并使用类进行了封装,将其分为以上三个模块。如果需要体验项目效果,可以在Github上下载代码并按照Readme配置运行。

将文字渲染为图片#

将文字渲染为图片再进行编码,可以增加隐写算法的鲁棒性。保证隐藏信息的图像在经过一定修改后,仍然能够辨识出其中隐藏的信息。

与其说是“渲染”为图片,实际的操作更像是新建一个图片,并在这张图片上面印上文字内容。PIL库中的ImageDraw可以实现这一功能。

但库中的函数并不能根据文本长度和图片宽度进行自动换行,因此添加了一个简易的换行流程。

将图像进行异或处理以加/解密#

最基础的LSB隐写并没有使用任何加密手段,也极易被识别出来。而常用的对称加密解决方案又过于庞大,有种杀鸡用牛刀的感觉。同时这种方法对于图像等二进制文件也并不太好用。因此采用了异或加密这一简单的手段。在不知道加密手段的情况下,还是可以提供不错的安全性的。

异或加密的原理很简单,即生成一个和图像信息完全一致的三维随机数组(宽x高x通道数),将每一个数与图像中对应的数值(范围0-255)进行按位异或操作。

在解密时,只需要知道随机数组的值,按原样运行一次算法即可。(异或运算跑两次得到原值)

但由于PIL库的局限性,它仅支持单通道、所有数值为0或1的二值化图像进行异或操作。则此时生成的随机数组为二维的(大小为宽x高),且每一个数为0或1.

在实际的项目中,为解决这一问题,我采用了numpy进行按位异或操作。同时给用户提供了“二值化”与“灰度”两种模式。具体代码可以看项目,原理是一样的。

将图像转换为二进制字节流便于隐写#

这一模块在从原型到实际代码的开发过程中遇到了较多难题,所以这里直接使用了Converter.py中的最终代码进行解释。

啊……给自己的代码写注释是一个很费脑子的过程,更何况这个注释还是后期加上的……

如果有不正确或缺漏的地方还望指出,多多包涵。

将信息存入载体图片的最低有效位#

3 参考资料#

现有项目#

在编写代码时尽管没有Ctrl+C/V,但由于能力有限,还是参考了一些大佬的代码和提供的思路。

异或加密/解密#

【项目实践】基于LSB(最低有效位)原理的图像隐写
https://astro-pure.js.org/blog/2023/lsb
Author Marshuni
Published at 2023年6月12日