Hugo 加密文章简单尝试

关于 Hugo 加密文章的简单尝试。

我在使用 Hexo 的时候,注意到有一些加密文章相关插件。加密文章,在 Wordpress 等等有服务器支持的博客程序中是一个比较容易实现的功能,用户把密码口令发送到服务器验证,然后返回相关资源。但是静态博客,无后端的情况下,解密过程是在前端的,也就是无法对尝试次数之类的进行任何限制。这其实并不算很安全的一种方案。

即使如此也有其用途。在 Hexo 插件中,使用方法就是在文章 Metadata 加上一些字段就行了。也就说这种方法如果想保密不能公开源码。在 Hugo 这里要麻烦一些,Hugo 本身似乎并没有提供插件功能,也没有不修改源码就增加内置函数的功能。倒是找到了使用 python 处理的一些方法。

因此,我进行了以下尝试。

  1. 把所有需要加密的初始文本片段,加入一个被 git 忽略的文件夹。
  2. 运行一个脚本,或者手动使用加密工具加密,然后生成的密文填入一个 txt 文件中。
  3. 生成的加密文件作为静态文件附件公开,一同随网站发布。
  4. 写了个 Hugo 的 Shortcode 用于生成用户端解密显示,填入加密文件 URL 。

这样在使用前端效果如下,口令是 hi

我使用的加密算法是 Xxtea ,这是一个很容易实现的算法。就博客文章加密场景,或许一个异或运算加密就足够了,这样能少引入一个 js 。不过 Xxtea 在网络上也有不少现成的加解密工具可以使用,也挺不错的。

Hugo shortcode 代码贴在下面,或者在我网站源码里也能找到。引入的 js 在 https://github.com/xxtea/xxtea-js

Shortcode 代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{{- $url := .Get "url" -}}
{{- $md5Hash := md5 $url -}}
{{- $md5Short := substr $md5Hash 0 8 -}}

<div id="{{- $md5Short -}}-bar" style="display: none;">
    <input id="password" placeholder="请输入口令..."></input>
    <div><button id="submitBtn">解密</button></div>
</div>
<div id="{{- $md5Short -}}">
</div>
<script type="text/javascript" src="https://cdn.ftls.xyz/js/crypto/xxtea.js"></script>
<script>
    const msg = document.getElementById("{{- $md5Short -}}");
    const submitBtn = document.getElementById("submitBtn");
    let newText = "";
    function getNewText() {
        fetch("{{ $url }}")
            .then(response => response.text())
            .then(text => {
                console.log("加载密文: " + text);
                msg.innerHTML = "已加载密文";
                newText = text;
                document.getElementById("{{- $md5Short -}}-bar").style.display = "block";
            });
    }
    getNewText();
    submitBtn.addEventListener("click", () => {
        const password = document.getElementById("password").value;
        console.log("口令: " + password, "密文: " + newText);
        try {
            let decrypt_data = XXTEA.decryptFromBase64(newText.trim(), password);
            msg.innerHTML = decrypt_data;
        } catch (error) {
            msg.innerHTML = "口令错误";
        }
    });
</script>

一个加密小程序,可以使用 Deno 运行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// XXTEA 方法 XXTEA.encryptToBase64 XXTEA.decryptFromBase64
// https://github.com/xxtea/xxtea-js  MIT license
// https://github.com/zhaidw/xxtea-ts MIT license

import XXTEA from "https://deno.land/x/xxtea@v0.1.0/mod.ts";

const fileName = "README.md";
const text = Deno.readTextFileSync(fileName);
const encrypted = XXTEA.encryptToBase64(text, "password");
Deno.writeTextFileSync(fileName + ".xxtea.txt", encrypted);


//  deno run --allow-read --allow-write test.js 

或者将一个文件夹内的文本文件,批量加密

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// deno run --allow-read --allow-write xxtea-utils.ts
console.log("XXTEA 加密 CLI")
console.log(Deno.args);

import XXTEA from "https://deno.land/x/xxtea@v0.1.0/mod.ts";

const basePath = ""

const inputFilesPath = basePath + "kkbt/xxtea/"
for (const file of Deno.readDirSync(inputFilesPath)) {
    console.log(file.name);
    if (file.isFile) {
        const text = Deno.readTextFileSync(inputFilesPath + file.name)
        const data = XXTEA.encryptToBase64(text, "password");
        Deno.writeTextFileSync(basePath + "kkbt/xxtea/output/" + file.name + ".xxtea.txt", data);
    }

}