Featured image of post Encrypt Your Article in Hugo

使用'hugo encryptor'加密博客文章

Tip

通过本文,你将了解:

  • 如何更方便的使用Li4n0/hugo_encryptor
  • “PY_SSIZE_T_CLEAN macro must be defined for ‘#’ formats” 报错的解决方法
  • 简易修改

Note

建议先参考本文利用Github Action将博客部署到Github Page上获得最佳体验。

前言

博客偶尔想放些涉及个人生活比较深入的东西,但又只希望与接触比较深的人或者部分相关人士查看。这种情况下设置个大家心知肚明的密码最好。

原理

简单来说,hugo-encryptor加密流程如下:

  1. 利用shortcode渲染待加密内容
  2. 用一段python程序对渲染结果(/public文件夹下所有文件)进行匹配,加密待加密内容
  3. 部署 因此,如果只是配置了shortcode而没有运行加密程序的话,文章内容不会被加密。而具体配置过程也因此分成了配置主题配置部署两部分。

为主题引入hugo_encryptor

原作者给出的办法个人感觉不是很好看:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
### Step 1: Install all the requirements of Hugo-Encryptor

    $ git clone https://github.com/Li4n0/hugo_encryptor.git
    $ cd hugo_encryptor
    $ chmod +x hugo-encryptor.py
    $ pip install -r requirements.txt

### Step 2: Create a symlink (Optional)

    $ ln -s /absolute/path/to/hugo_encryptor/hugo-encryptor.py hugo-encryptor.py

### Step 3: Symlink `shortcodes/hugo-encryptor.html` into the shortcode directory of your blog:

    $ mkdir -p /path/to/your/blog/layouts/shortcodes
    $ ln -s /absolute/path/to/hugo_encryptor/shortcodes/hugo-encryptor.html /path/to/your/blog/layouts/shortcodes/hugo-encryptor.html

我更倾向于将其直接放入主题中去,我的方法如下:

  1. 找到主题对应的layouts/partials/shortcodes文件夹,在里面新建一个文件夹hugo-encryptor.html

  2. 将以下代码复制粘贴入其中:

     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
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    
    {{ $_hugo_config := `{ "version": 1 }` }}
    {{/*
        ## Hugo Encrypt
        ### Params:
        - `password`:
    
            require param
        - Simple
    
            
    
    
    
    <div class="hugo-encryptor-container">
      <div class="hugo-encryptor-prompt">
        
          <p>文章的部分内容被密码保护:</p>
        
      </div>
      <div class="hugo-encryptor-form">
        <input
          class="hugo-encryptor-input"
          placeholder='请输入密码'
        />
        <input
          class="hugo-encryptor-button"
          type="button"
          value='点击验证'
          onclick="_click_handler(this)"
        />
      </div>
      <div
        class="hugo-encryptor-cipher-text"
        data-password="your password"
        style="display: none;"
      >
        <span style="display: none;">--- DON'T MODIFY THIS LINE ---</span>
        <p>your content</p>
    
      </div>
    </div>
    
    */}}
    {{/* DEFAULTS */}}
    
    <div class="hugo-encryptor-container">
      <div class="hugo-encryptor-prompt">
        {{ if eq .Site.Params.hugoEncryptorLanguage "en-us" }}
          <p>Part of this article is encrypted with password:</p>
        {{ else }}
          <p>文章的部分内容被密码保护:</p>
        {{ end }}
      </div>
      <div class="hugo-encryptor-form">
        <input
          class="hugo-encryptor-input"
          placeholder='{{ if eq .Site.Params.hugoEncryptorLanguage "en-us" }}Please input the password{{ else }}请输入密码{{ end }}'
        />
        <input
          class="hugo-encryptor-button"
          type="button"
          value='{{ if eq .Site.Params.hugoEncryptorLanguage "en-us" }}Click to verify{{ else }}点击验证{{ end }}'
          onclick="_click_handler(this)"
        />
      </div>
      <div
        class="hugo-encryptor-cipher-text"
        data-password="{{ .Get 0 }}"
        style="display: none;"
      >
        <span style="display: none;">--- DON'T MODIFY THIS LINE ---</span>
        {{ .Inner }}
      </div>
    </div>
    
  3. 找到主题对应header部分的模板,在其末尾添加:

     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
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
    <script>
    const _do_decrypt = function (encrypted, password) {
        let key = CryptoJS.enc.Utf8.parse(password);
        let iv = CryptoJS.enc.Utf8.parse(password.substr(16));
    
        let decrypted_data = CryptoJS.AES.decrypt(encrypted, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        return decrypted_data.toString(CryptoJS.enc.Utf8);
    };
    
    const _click_handler = function (element) {
        let parent = element.parentNode.parentNode;
        let encrypted = parent.querySelector(
            ".hugo-encryptor-cipher-text").innerText;
        let password = parent.querySelector(
            ".hugo-encryptor-input").value;
        password = CryptoJS.MD5(password).toString();
    
        let index = -1;
        let elements = document.querySelectorAll(
            ".hugo-encryptor-container");
        for (index = 0; index < elements.length; ++index) {
            if (elements[index].isSameNode(parent)) {
                break;
            }
        }
    
        let decrypted = "";
        try {
            decrypted = _do_decrypt(encrypted, password);
        } catch (err) {
            console.error(err);
            alert("Failed to decrypt.");
            return;
        }
    
        if (!decrypted.includes("--- DON'T MODIFY THIS LINE ---")) {
            alert("Incorrect password.");
            return;
        }
    
        let storage = localStorage;
    
        let key = location.pathname + ".password." + index;
        storage.setItem(key, password);
        parent.innerHTML = decrypted;
    }
    
    window.onload = () => {
        let index = -1;
        let elements = document.querySelectorAll(
            ".hugo-encryptor-container");
    
        while (1) {
            ++index;
    
            let key = location.pathname + ".password." + index;
            let password = localStorage.getItem(key);
    
            if (!password) {
                break;
    
            } else {
                console.log("Found password for part " + index);
    
                let parent = elements[index];
                let encrypted = parent.querySelector(".hugo-encryptor-cipher-text").innerText;
                let decrypted = _do_decrypt(encrypted, password);
                elements[index].innerHTML = decrypted;
            }
        }
    };
    </script>
    

Warning

请注意,此时文章还未被加密,需完成python的配置才行。

即可完成主题部分的配置。

配置部署

Note

建议先参考本文利用Github Action将博客部署到Github Page上获得最佳体验。本地运行属实麻烦,下文假设你使用Github Action将博客部署到Github Page上。

我们再回顾一下hugo-encryptor的工作原理:
  1. 利用shortcode渲染待加密内容
  2. 用一段python程序对渲染结果(/public文件夹下所有文件)进行匹配,加密待加密内容
  3. 部署

上一小节中,我们已经实现了1. 利用shortcode渲染待加密内容,而由于我们使用Github Action,我们可以将23两步结合起来,实现全自动加密,避免忘记运行加密程序导致隐私泄漏。

步骤如下:

  1. 在博客仓库创建一个文件夹hugo_encryptor,将Li4n0/hugo_encryptor中的hugo-encryptor.pyrequirements.txt放入其中。
  2. 对原有的部署工作流如此修改:
 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
...
env:
  ...
  HUGO_ENCRYPTOR_PATH: ./hugo_encryptor
  ...
jobs:
  build:
    steps:
      ...
      - uses: actions/setup-python@v4
        with:
          python-version: '3.9'

      - name: Install Python and Dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y python3 python3-pip
          pip3 install -r ${{ env.HUGO_ENCRYPTOR_PATH }}/requirement.txt          

      - name: Build Hugo site
        run: hugo --minify --gc

      - name: Encrypt private posts
        run: |
          python3 ${{ env.HUGO_ENCRYPTOR_PATH }}/hugo-encryptor.py          
        env:
          HUGO_ENCRYPTOR_PATH: ./hugo_encryptor  # Adjust this path based on where your `hugo_encryptor.py` script is located
      ...

其中,python版本选择3.9最佳,3.10及以上会报错:PY_SSIZE_T_CLEAN macro must be defined for '#' formats。此时可以选择以下两种做法:

  • 回退3.9版本python(推荐)
  • 重新用pip卸载再安装pycryptodome

至此,基本配置结束。

 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
name: Deploy Hugo site to Pages

on:
  # Runs on pushes targeting the default branch
  workflow_dispatch:
  push:
    branches: ["master"]

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Environment variables available to all jobs and steps in this workflow
env:
  HUGO_ENV: production
  HUGO_VERSION: "0.115.4"
  GO_VERSION: "1.20.5"
  NODE_VERSION: "18.15.0"
  TINA_CLIENT_ID: ${{ vars.TINA_CLIENT_ID }}
  TINA_TOKEN: ${{ vars.TINA_TOKEN }}
  HUGO_ENCRYPTOR_PATH: ./hugo_encryptor

jobs:
  build:
    runs-on: ubuntu-latest

    permissions:
      contents: write  # Allow write access to commit changes to the repository

    steps:
      - uses: actions/checkout@v4
        with:
          submodules: 'recursive'

      - name: Cache Hugo resources
        uses: actions/cache@v4
        env:
          cache-name: cache-hugo-resources
        with:
          path: resources
          key: ${{ env.cache-name }}

      - uses: actions/setup-go@v5
        with:
          go-version: "^1.17.0"
      - run: go version

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: "latest"
          extended: true

      - uses: actions/setup-python@v4
        with:
          python-version: '3.9'


      - name: Install Python and Dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y python3 python3-pip
          pip3 install -r ${{ env.HUGO_ENCRYPTOR_PATH }}/requirement.txt          

      - name: Build Hugo site
        run: hugo --minify --gc

      - name: Encrypt private posts
        run: |
          python3 ${{ env.HUGO_ENCRYPTOR_PATH }}/hugo-encryptor.py          
        env:
          HUGO_ENCRYPTOR_PATH: ./hugo_encryptor  # Adjust this path based on where your `hugo_encryptor.py` script is located

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v1
        with:
          path: ./public

      - name: Echo CNAME
        run: echo ${{ secrets.CNAME }} > ./public/CNAME

      - name: Deploy Web
        uses: peaceiris/actions-gh-pages@v3
        with:
          PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
          EXTERNAL_REPOSITORY: HaleyCH/HaleyCH.github.io
          PUBLISH_BRANCH: master
          PUBLISH_DIR: ./public
          commit_message: ${{ github.event.head_commit.message }}

为encryptor添加密码提示

为了让目标受众知道我设置的密码是什么,有必要增加密码提示功能,这比较简单,只需将

1
2
3
{{ else }}
    <p>文章的部分内容被密码保护:</p>
{{ end }}

改为

1
2
3
{{ else }}
    <p>文章的部分内容被密码保护{{ if .Get 1 }},提示 “{{ .Get 1 }}”{{ end }}:</p>
{{ end }}

即可。最终成果如下所示(密码hugo)。

文章的部分内容被密码保护,提示 “此博客用的框架”:

至此,我们完成了hugo-encryptor的配置与简单修改。

comments powered by Disqus