🎃

CythonでPythonのソースコードを秘匿化する

に公開

要点

Cythonを利用してPythonのソースコード(src)を秘匿化することは可能です。その方法を説明します。

具体的には、.py ファイルをCythonでコンパイルし、バイナリファイル(.pydや.so)に変換することで、元のPythonコードを直接読むことができない状態にします。


1. なぜCythonでコードを秘匿できるのか?

  • Cythonは、PythonコードをC言語コードに変換し、コンパイルします。
  • コンパイル後の成果物(.pyd.so)はバイナリ形式であり、元のPythonコードは含まれていません。
  • これにより、簡単にはソースコードを読まれないように保護できます。

注意点
完全な秘匿(リバースエンジニアリング不可能)は難しいですが、十分に強力な保護となります。


2. Cythonによるコード秘匿化の基本手順

一般的な流れは以下です。

① Cythonをインストール

pip install cython setuptools wheel

② フォルダ構造の例(推奨)

project-name/
│
├── setup.py
├── README.md
├── requirements.txt
├── LICENSE
│
├── src/
│   └── your_package/
│       ├── __init__.py
│       ├── detection.py
│       ├── utils.py
│       ├── backbones.py
│       └── visualize.py
│
└── build/  # コンパイル後に生成されるフォルダ
  • setup.py を使ってCythonビルドをします。

③ setup.py の内容例

from setuptools import setup, Extension
from Cython.Build import cythonize
import numpy

extensions = [
    Extension("your_package.*", ["src/your_package/*.py"]),
]

setup(
    name='your_package',
    ext_modules=cythonize(
        extensions,
        compiler_directives={'language_level': "3"},
    ),
    packages=['your_package'],
    package_dir={'your_package': 'src/your_package'},
    include_dirs=[numpy.get_include()],
)
  • 上記はフォルダ全体の .py をまとめてCython化します。
  • 個別に指定する場合は、明示的にファイル名を指定可能です。

④ コンパイルの実行方法

python setup.py build_ext --inplace
  • これで、元の .py ファイルがコンパイルされ、.pyd(Windows)または.so(Linux/Mac)が生成されます。

  • コンパイル後の構造(例):

your_package/
├── __init__.py
├── detection.cp311-win_amd64.pyd
├── utils.cp311-win_amd64.pyd
├── backbones.cp311-win_amd64.pyd
└── visualize.cp311-win_amd64.pyd
  • 元の .py は削除しても実行可能です。

⑤ 不要なファイルの削除(秘匿化)

find src/your_package/ -name "*.py" -delete
find src/your_package/ -name "*.c" -delete

(重要なバックアップは必ず取ってから実行してください。)


3. フォルダ構造の推奨内容の変化

Cythonを導入することで、推奨フォルダ構成が少し変わります:

  • ソースコード(src/)フォルダの構造は同じですが、ビルド成果物(.pyd/.so)のみを配布対象にします。
  • 配布時は元のPythonファイルを削除または別途保管します。

推奨の最終構造(秘匿化後)は次のようになります。

project-name/
│
├── README.md
├── LICENSE
├── requirements.txt
│
├── your_package/                 # コンパイル後のバイナリ
│   ├── __init__.py               # 必須 (空でOK)
│   ├── detection.cp311-win_amd64.pyd
│   ├── utils.cp311-win_amd64.pyd
│   ├── backbones.cp311-win_amd64.pyd
│   └── visualize.cp311-win_amd64.pyd
│
└── setup.py                      # 再コンパイル用
  • ユーザーにはバイナリのみを配布し、元のPythonコードは配布しません。
  • パッケージインストールの際は、上記フォルダをそのまま利用します。

4. 制限事項と注意点

  • .pyd.soはPythonのバージョンごとに互換性があります。複数バージョンのPythonで利用する場合は、それぞれのバージョン用にコンパイルする必要があります。(python 3.10, python3.11, python3.12の各バージョアンを用意する。
  • 完全にコードを読めなくするわけではなく、高度なリバースエンジニアリングは理論上可能です。機密性の高いコードには、追加で難読化技術(obfuscation)も検討してください。

5. まとめ(推奨フロー)

  • コードを src に用意
  • Cythonでコンパイルし、.pyd.soにする
  • 元の .py を削除し、バイナリのみ配布
  • 配布する際は必ずPythonバージョンを明記する

これでソースコードの秘匿性が大幅に向上します。ぜひ試してみてください。

6. 補足資料(pydとsoファイルについて)

CythonでPythonファイルをコンパイルすると、プラットフォームに応じて拡張モジュールとしてバイナリ形式のファイルが生成されます。主に生成されるのは、以下の二種類です。

  • .pyd ファイル(主にWindows環境)
  • .so ファイル(主にLinux、macOSなどUnix系環境)

1. 拡張モジュールの概要

Pythonで拡張モジュールを作る際、CやC++で記述したコードをPythonで呼び出せるように、動的ライブラリとしてコンパイルします。この動的ライブラリがプラットフォームごとに異なる形式を持ちます。

OS 拡張子 説明
Windows .pyd Python用のDLL
Linux/macOS .so Shared Object(共有ライブラリ)

2. .pyd ファイル(Windowsの場合)

基本的な特徴

  • .pyd は、WindowsにおいてPythonの拡張モジュールとして使われる特殊なDLLファイルです。
  • PythonのC APIを使ってPythonインタプリタとのインタフェースを持ち、通常のDLLとは少し異なり、Pythonインタプリタで直接 import 可能な形式となっています。

通常のDLLとの違い

  • 通常のDLLと同じくダイナミックリンクライブラリであることには変わりありませんが、.pyd ファイルはPythonから直接インポートできる特別な形式をしています。
  • 通常のDLLは ctypes を利用してロードしますが、.pyd は通常のPythonモジュールのように直接インポート可能です。

使用方法の例

import example  # example.pydが存在する場合
example.say_hello()

命名規則

  • Pythonのバージョンによって名前が決まることがあります(例:example.cp310-win_amd64.pyd など)。
  • cp310 はCPython 3.10を意味し、win_amd64 は64bit Windows用という意味です。

3. pyd と so の比較まとめ

項目 .pyd ファイル (Windows) .so ファイル (Linux/macOS)
ファイル形式 DLL(ダイナミックリンクライブラリ) 共有ライブラリ (Shared Object)
インポート方法 Pythonで直接 import 可能 Pythonで直接 import 可能
プラットフォーム Windows Linux, macOS
命名規則 .cp310-win_amd64.pyd のような命名規則 .cpython-310-x86_64-linux-gnu.so のような命名規則

4. クロスプラットフォーム対応について

  • .pyd.soは、プラットフォーム固有のバイナリのため、互換性はありません。
  • マルチプラットフォーム対応をする場合、それぞれのOS向けにビルドして提供する必要があります。

参考資料

https://zenn.dev/kotai/articles/49bd8b550fefca

Discussion