はじめに
この記事では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です。
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
・記号: ! @ # $ % ^ & * – _ + = [ ] { } | \ : ‘ , . ? / ` ~ ” ( ) ; < >
Django(Python)で使ってみる
パスワード生成方法はPythonのドキュメントにサンプルがあります。

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

試したところ、コマンドラインで起動するせいか一部の記号が使えませんでした。
回避方法があれば良いのですが調査中です。
(いろいろ省略)
# 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モジュールを使うとパスワードが設定できるようです。

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


Comments