16
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

わかりやすいPythonコードの秘匿化 ─ PyInstaller × PyArmor

16
Posted at

1. はじめに

こんにちは!タランチュラです。

普段は統計や機械学習など、データサイエンスに関する情報発信をX上で行なっています。
もしご興味ある方は、是非フォローお願いします。

データサイエンス業務でコードを書くことは多いと思いますが、「作ったコードをどう安全に提供するか」という点は難しい問題の一つだと思います。

せっかく開発したアルゴリズムをそのままソースで渡してしまうと、数日後には派生プロダクトが社内で勝手に展開されていた、という苦い経験をお持ちの方もいると思います。また、Jupyter Notebook でスムーズに動くのに、クライアント側は環境構築に手こずり、結局「納品しても動かない」という声が上がることもあります。

このジレンマを解決する鍵が「秘匿化」です。
ただ、Python の世界では「EXE 化すれば大丈夫」と思われがちですが、PyInstallerなどでひとつの実行ファイルにまとめても、ツールを使えば中身は意外と簡単にのぞけてしまいます。

本記事では、そもそも秘匿化が必要となる背景を整理し、PyInstallerによる EXE 化の実践的な手順と注意点、さらに PyArmorを使った難読化の効果と限界を検証します。

まだコードを難読化したことがない方はもちろん、既に試したけれど効果に不安を感じている方の道しるべになれば幸いです。

2. なぜ秘匿化が必要か

まず「秘匿化」という言葉自体がピンと来ない方のために、ざっくり定義から入りましょう。ここでいう秘匿化とは「ソースコードをそのまま読めない状態にしつつ、利用者がワンクリックで実行できる形に変換すること」です。
言い換えると、レシピは隠したまま料理を提供するようなイメージで、味わうことはできるが、作り方は簡単には盗めない状態を作ることが目的です。

データサイエンス案件では、実装したアルゴリズムや前処理のパイプライン自体が知的財産となるケースが珍しくありません。もしソースを無防備なまま渡せば、クライアントや第三者がコードを改変・再配布してもこちらは追跡しづらく、「せっかく作った武器がフリー素材化する」という事態になりかねません。
また、分析ロジックの詳細が外部に漏れると、競合に同じ手法を真似されるだけでなく、モデルの前提や弱点まで知られてしまうため、サービス全体の優位性が削がれるリスクもあります。

一方で、クライアント側の立場から見ると「環境構築に失敗して動かない」「依存ライブラリが古くてインストールに半日かかった」といったトラブルは契約満足度を大きく下げます。動くまでが仕事なのに、環境設定で時間を浪費すると「納品物の価値」そのものが疑問視されてしまいます。

ここにこそ秘匿化の意義があります。実行ファイル(EXE)にまとめることで依存関係をパッケージし「ダブルクリックで起動」を実現しつつ、難読化によって中身の逆解析を難しくする。
この二段構えが、配布体験とセキュリティのバランスを取った方法になります。

さらに、秘匿化には顧客との契約に際してもメリットがあります。
委託契約の多くは「成果物を納品=ソースを渡すこと」と解釈されがちですが、秘匿化によって**「使用権は提供するが、ソースの所有権は手放さない」**という柔軟な契約形態を取りやすくなります。

まとめると秘匿化は、

  1. アルゴリズム流出や無断改変の防止
  2. ユーザー体験の向上(環境構築不要で即実行)
  3. 契約の柔軟性向上

の三つを同時に満たすことができるものだと考えています。
次章では、この目的を実際にどう叶えるか、コードを示しつつ解説していきます。

3. EXE化の実践

Python スクリプトを実行ファイル(EXE)に変換する基本ツールがPyInstallerです。ここでは “最短で動く EXE” と “実務で使える EXE” の両方を、失敗しやすいポイントも交えながら解説します。

3-1. 準備:環境をクリーンに保つ

まずはプロジェクト専用の仮想環境を作り、その中にライブラリを閉じ込めることをオススメします。こうしておくとPyInstallerが必要な依存関係だけを検出でき、EXEの肥大化を防ぐことができます。

# プロジェクト直下で
python -m venv venv
# Windowsの場合
.\venv\Scripts\activate
# macOS / Linuxの場合
source venv/bin/activate

pip install --upgrade pip
pip install pyinstaller

依存ライブラリ(pandas・scikit-learn など)がある場合はこのタイミングで pip install pandas scikit-learnのように追加してください。

3-2. 最小構成:たった 1 行で EXE を作る

メインスクリプトをmain.pyとすると、以下 1 行で dist/main.exe が生成されます。

pyinstaller --onefile main.py
  • --onefile:ライブラリをすべて 1 つの実行ファイルにまとめるスイッチ
  • 生成物
    • dist/ … 完成した EXE(=納品物)
    • build/ … 一時ファイル
    • main.spec … ビルド設定を記録したファイル(後述)

ここまでで 「ダブルクリックすると動く」 だけなら達成です。
ただしサイズが50MB超になる、黒いコンソールが開きっぱなしになる等、実務ではもう一歩チューニングが必要です。

3-3. 実務仕様:ビルドオプションと .spec ファイル

多くのオプションが存在するが、その一部を紹介します。

目的 オプション例 解説
コンソールを隠す --noconsole GUI アプリやバッチ処理に便利
アイコンを付ける --icon=logo.ico Windows の見栄え対策
圧縮してサイズ削減 --upx-dir=<パス> UPX を事前インストールしておく
不要ライブラリ除外 --exclude-module=tkinter GUI 不使用なら tkinter などを外す

これらのオプションを組み合わせて、以下のようにpyinstallerを実行できます。

pyinstaller --onefile --noconsole --icon=logo.ico --exclude-module=test --name MyApp main.py

pyinstallerを一度実行すると、main.specができます。
ここを編集すると データファイル同梱、実行時パスの解決、複数エントリポイント など CLI では面倒な設定をコード感覚で手軽に記述できます。

編集後は以下のコマンドでで再ビルドできます。

pyinstaller main.spec

ここまでで 「動く EXE」 は完成しましたが、pyinstxtractor.pyのような抽出ツールを使うとバイトコード(.pyc)はまだ取り出せます。
逆コンパイルすればソースの大部分が読めてしまうため、ここから先は PyArmor を利用した「難読化」でガードを固めるのが定石です。次章ではその手順と限界を検証していきます。

4. PyArmorによる難読化の実践

ここからは PyArmor を使い、先ほど生成したEXEの内部に残っているバイトコード(.pyc)をさらに読みにくくしていきます。
PyArmor は CPython の命令列を暗号化し、実行時にだけ復号する仕組みを採るため、逆コンパイルツールでもほぼ読めない断片しか取り出せなくなる点が最大の特長です。

4-1. インストールとバージョン確認

まずは仮想環境をアクティブにしたままインストールします。
PyArmorはバージョンで使い方が大きく変わるので、バージョンを固定しておくとチーム作業でもトラブルが減ります。

pip install "pyarmor>=8,<9"
pyarmor -V    # 例: PyArmor 8.4.6

4-2. 最小構成でスクリプトを難読化

試しにmain.pyを単体で難読化する場合は次の1行だけです。dist-obf/ に暗号化済みコード一式が生成されます。

pyarmor obfuscate --output dist-obf main.py

出力されたdist-obf/main.pyは4〜5 行のローダーコードに置き換わり、実体は.pyarmor_runtime*以下に暗号化された状態で保存されます。
ここまでのファイルは元のPython環境でもそのまま実行できますが、EXEにまとめるなら次節に進みます。

4-3. PyInstaller と一気通貫でパッケージする

PyArmor には pack サブコマンドがあり、難読化と EXE 化をワンショットでこなせます。さきほどのオプションをそのまま渡せるため、既存の PyInstaller フローをほとんど変えずに導入できます。

pyarmor pack -e " --onefile --noconsole --icon=logo.ico" main.py

コマンドが完了するとdist/main.exeが生成されます。
内部では

  1. ソース全体を難読化
  2. 一時フォルダに展開
  3. 指定オプションでPyInstallerビルド

という 3 段階が連続して走るため、手動でフォルダを行き来する手間もなく便利です。

4-4. よくある落とし穴と限界

難読化したコードは「読みにくい」だけであって、「絶対に読めない」わけではありません。
メモリ上で復号される瞬間を動的解析すれば命令列を抜き取ることは理論上可能だそうです。
また、PyArmorはC拡張モジュールを暗号化できないため、NumPyの内部Cython部分などはそのまま残る点にも注意が必要です。

おわりに

最後まで読んでいただきありがとうございました。
この記事では、まず秘匿化が必要になる背景を整理し、つづいて PyInstaller で「ダブルクリックで動く EXE」を作る方法、さらに PyArmor を組み合わせて逆コンパイルされにくい形へ強化する手順を紹介してきました。

要は “実行の手軽さ” と “ロジックの保護” を両立させる ことが、データサイエンス案件でも大事だというお話でした。

ご自身のプロジェクトでもぜひ試してみてください。
多少の手間はかかりますが、アルゴリズム流出や環境構築トラブルで失う信頼に比べれば、投資効果は十分に大きいはずです。

僕は タランチュラの名で Xでも統計・機械学習の最新トピックや実務で役立つTipsを発信しています。
興味をもっていただけたら、ぜひフォローしていただけると嬉しいです。

この記事が役に立った、もっと深掘り記事を読みたい、と感じていただけたら いいねとストック で応援していただけると励みになるので、ぜひよろしくお願いします。

16
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?