Python(Django)でパスワード付きのExcelファイルを生成する

Django

はじめに

この記事ではPythonでパスワード付きのExcelファイルを生成する方法を説明しています。
実際にはDjangoで利用しています。

openpyxlライブラリはExceファイルの読み書きができますが、パスワードはWorkbookやWorksheetにしか設定できません。
そのため、Excelファイルにパスワードを設定するために「secure-spreadsheet」を使います。
「secure-spreadsheet」はnpmでインストールするJavaScript製のコマンドラインプログラムでPython、PHP、Rubyで使えるようです。

「secure-spreadsheet」の利点はOSに依存しない、Microsoft Offceをインストールしなくても使えることです。

環境

pipenvの仮想環境を使っています。

 - Debian bullseye 64bit
 - Python 3.9.8
 - Django 4.0.0

Djangoのディレクトリ構造は以下の通りです。

/home/www/wsgi/proj
   ┣ -- .venv
   ┣ -- proj
   ┣ -- app
   ┣ -- npm
   ┣ -- templates
   ┣ -- static
   ┣ -- db.sqlite3
   (以下、省略)


secure-spreadsheetのインストール

Djangoのプロジェクトディレクトリにnpmというディレクトリを作成してからインストールします。
ローカルにインストールするので「-g」オプションは付けません。
OSがDebianなのでユーザとグループはwww-dataです。

GitHub - ankane/secure-spreadsheet: Encrypt and password protect sensitive CSV and XLSX files
Encrypt and password protect sensitive CSV and XLSX files - ankane/secure-spreadsheet
sudo apt update
sudo apt install npm

cd /home/www/wsgi/proj
sudo mkdir npm
cd npm
sudo npm install secure-spreadsheet

cd ..
sudo chown -R www-data.www-data /home/www/wsgi/proj/npm


適当なExcelファイル(test.xlsx)を準備して、パスワードを「sss」として動作テストを行います。
生成した「output.xlsx」をパスワードを入力して開ければ成功です。


cd /home/www/wsgi/proj
cat test.xlsx | ./npm/node_modules/.bin/secure-spreadsheet --password sss --input-format xlsx > output.xlsx

secure-spreadsheetをアップデートするには以下のコマンドを実行します。

cd /home/www/wsgi/proj
sudo npm update secure-spreadsheet

Excelのパスワードに使える文字列や記号

Excelのパスワードに使える最大の文字数は15文字です。
また、使える文字種は以下のようです。
・文字: A~Z、a~z
・数字: 0~9
・記号: ! @ # $ % ^ & * – _ + = [ ] { } | \ : ‘ , . ? / ` ~ ” ( ) ; < >

Protect a document with a password - Microsoft Support
How to password protect a Word document to prevent unauthorized access.

Django(Python)で使ってみる

パスワード生成方法はPythonのドキュメントにサンプルがあります。

secrets --- Generate secure random numbers for managing secrets
ソースコード: Lib/secrets.py secrets モジュールを使って、パスワードやアカウント認証、セキュリティトークンなどの機密を扱うのに適した、暗号学的に強い乱数を生成することができます...

本格運用するなら、パスワードの生成方法は不完全です。
以下が参考になるかもしれません。

Build software better, together
GitHub is where people build software. More than 100 million people use GitHub to discover, fork, an...

試したところ、コマンドラインで起動するせいか一部の記号が使えませんでした。
回避方法があれば良いのですが調査中です。

(いろいろ省略)
# Excelファイルの書き込み
from openpyxl import Workbook
from openpyxl.styles.fonts import Font
#from openpyxl.styles.colors import Color
from openpyxl.styles.borders import Border, Side
from openpyxl.styles import PatternFill

# パスワード生成
import string
import secrets
import subprocess

SECURE_SPREADSHEET = '/home/www/wsgi/proj/npm/node_modules/.bin/secure-spreadsheet'
# settings.pyに記述すると汎用性が上がる
#from django.conf import settings
#SECURE_SPREADSHEET = getattr(settings, "SECURE_SPREADSHEET", None)

(いろいろ省略)

class SampleView(ListView):
    (いろいろ省略)
    #
    # Excelファイル作成
    # 行と列の指定は「1」から始まる。
    #
    def create_excelfile(self, fname):
        wb = Workbook()
        ws = wb.active

        # 印刷設定
        # 用紙の向き:横
        # 縦・横の印刷とも1ページにまとめる
        ws.page_setup.orientation = ws.ORIENTATION_LANDSCAPE
        ws.page_setup.fitToWidth  = 1
        ws.page_setup.fitToHeight = 1
        ws.sheet_properties.pageSetUpPr.fitToPage = True

        ws.cell(row=1, column = 1).value = 'テストデータ'
        wb.save(fname)

    #
    # パスワード生成
    #
    def pass_gen(self, size):
        chars = string.ascii_uppercase + string.ascii_lowercase + string.digits
       #chars += '!@#$%^&*()-_+=[]{}<>?'
        chars += '!#^*()-_+=[]{}'
        #return ''.join(secrets.choice(chars) for x in range(size))

        # アルファべットと数字からなり、小文字を少なくとも1つと数字を少なくとも3つ含む。
        while True:
            password = ''.join(secrets.choice(chars) for x in range(size))
            if ( any(c.islower() for c in password )
                    and any(c.isupper() for c in password)
                    and sum(c.isdigit() for c in password) >= 3):
                break
        return password

    #
    # Excelファイルにパスワードを設定
    #
    def password_excelfile(self, fname, passwd):
        cmd = 'cat ' + str(fname) + ' | ' + str(SECURE_SPREADSHEET) + ' --password ' + '"' + passwd + '"' + ' --input-format xlsx'
       #cmd = 'cat ' + str(fname) + ' | ' + str(SECURE_SPREADSHEET) + ' --password ' + passwd + ' --input-format xlsx'
        result = subprocess.check_output(cmd, shell=True)
        with open(fname, 'wb') as f:
            f.write(result)

    #
    # Submitの処理
    #
    def post(self, request, *args, **kwargs):
        fname_excel = '/home/www/wsgi/proj/media/test.xlsx'
        (いろいろ省略)
        # Excelファイルを生成する。
        self.create_excelfile(fname_excel)
        # パスワードを12文字で生成するように指定。
        self.password_excelfile(fname_excel, self.pass_gen(12))
        (いろいろ省略)

Windowsの場合(win32com)

OSがWindowsの場合は、Excelがインストールされていればwin32comモジュールを使うとパスワードが設定できるようです。

pythonでエクセルファイルにパスワードを設定したい
!(0bd70e025a85aac244e25a9d84d3993d.png) エクセルを開いた際に、pythonコード内でパスワードを設定してエクセルを保存したい。 py

その他

popenを使ってもできるとの情報がありますが試していません。

Add password in xlsx using secure-spreadsheet
im try to put password in my excel file def excel_file test = Axlsx::Package.new do |p| p.workbook.a...

さいごに

生成したExcelにパスワードを設定することができました。
お役に立てば幸いです。
なお、「secure-spreadsheet」のサイトに掲載されているサンプルは、テキスト(CSVファイル)をbytes文字列として入力して、パスワード付きのExcelファイルに変換するものです。

Comments