cython でpythonコードを秘匿化する
配布用にビルドした資材のソースコードを、秘匿化して二次利用や改変などを防ぎたい。
そこでcython を使ってコンパイルし、バイナリファイルを配布するようにした。
cythonの公式ドキュメントなどを読みつつ秘匿化を進めていたが、
単純にcython導入してコンパイルするだけでは上手くいかないところがあったため
回避方法をここに残す。
環境や構成
python 3.6(flask)
AWS API Gateway + AWS Lambda
秘匿化するファイルは、Lambdaにアップロードするバックエンドの処理を記したコード群
cython のセットアップ
cythonの利用を始めるのに必要なコマンドは以下。
python利用している人ならばpip 入っているだろうと思うので割愛。
ちなみにunix 用です。mac、windowsはgccに変わる物を用意する必要あり。
$ pip install cython
$ sudo yum install gcc
$ sudo yum install python36-devel
cython でシンプルなpythonコードは秘匿化しやすい
公式ドキュメントにも書いてある通りのコンパイルは簡単にでき、
importも問題なくできる。
# file構成
example
┣hello.pyx
┣setup.py
:
hello.pyxにはprint('Hello World')
しか書いていない。
# setup.py
from distutils.core import setup
from Cython.Build import cythonize
setup(
name = "My hello app",
ext_modules = cythonize('hello.pyx')
)
この記述でexample ディレクトリ内で以下のコマンドによりビルドする
$ python setup.py build_ext --inplace
ディレクトリ内に.c
.so
ファイルが作成される。
soがバイナリ化されたファイルで、秘匿化が目的のためsoファイルを配布用のパッケージに含める。
試しに別のディレクトリbuild
を作成し、そこに作成された.so
ファイルを移動させ、
同じディレクトリ内にmyapp.py
を作成し、import hello
と記述しておく。
# 試し用
myapp
┣hello.xxxxxxx.so
┣myapp.py
:
※xxx…はコンパイルしたOSにより名称が変わる
myapp ディレクトリで$ python myapp.py
とコマンドを入力すると「Hello World」と出力される。
目的がC/C++のライブラリとpythonを絡める、実行速度を向上する場合は.c
が必要。
そっちの方はあまり学習していないため解説はできません。
flask.app のrootと組み合わせたときに引っかかった
Lambda にsoファイルをアップロードし、リクエストを送信すると以下のエラーが起きた。
TypeError: example_method() takes no keyword arguments
単純なお試し用のコンパイルしたファイルとは違い、flaskを起動させてリクエストや引数なども
メソッドに渡しているので、流石にcythonで”良しなに”とはいかない様子。
キーワード引数の渡し方
メソッドの受け取り方が良くないらしく、routeメソッドに関しては以下のような修正が必要でした。
:
:
@app.route('/myapp/menu-list/reg/<username>', methods=['POST'])
def register_menu(username):
result = 'this code is TypeError'
@app.route('/myapp/menu-list/reg/<username>', methods=['POST'])
def register_menu(*args, username):
result = 'this code is No Error'
flaskが引数キーワード(username=username のような)でURL内の変数を渡すため、
cython 化したファイルでpythonとして使う場合 *args、**kwargs
を使う必要がある。
実行メソッド名は取得できない
これもピンポイントな用途ですが、実行中のメソッド名を取得したいがために、
メソッド内でsys._getframe().f_code.co_name
を使用してました。
これcythonで秘匿化する前は上記例だと「register_menu」として取れていたのですが、
秘匿化して.soファイルにするとメソッド名が「dispatch_request」というメソッド名に変わってしまいます。
inspectを使用してinspect.getframeinfo(inspect.currentframe())[2]
でも同様でした。
詳細に読み解いてませんが、おそらくcythonコンパイルするとapp.route系のメソッドはC言語で扱うために
一元化されている気がします。
同じようにcython で秘匿化しメソッド名を取得するような処理を書いている場合は、app.route内の
名称取得を他に考える必要があります。
自分はrouteのURLをrequest.pathで取得し加工することで回避しました。
以上、cython化でハマった事の対処と回避策でした。