Python 2と3の比較:セキュリティ上の違い
2017年10月10日
0 分で読めますPython 3とPython 2には、機能面でさまざまな違いがあります。それ自体が必ずしも優劣を決めるものではありません(Python 3のほうが改善されていると言えるかもしれません)が、変更はどのようなものであれリスクをもたらす可能性があります。この記事では、セキュリティに影響する両バージョンの違いをいくつか取り上げ、解説します。Pythonアプリケーションをバージョン間で移行する際は、こうした点を念頭に置いてください。
Python 2と3の現在の状況
Python 3は2008年に初めてリリースされました。普及を妨げた問題のひとつは、Python 3に後方互換性がなかったことです。多くの開発者は、アプリケーションをリファクタリングする手間を避けるため、以前のバージョン、主にバージョン2を使い続けました。そのため、バージョン3.0のリリース後もPython 2のサポートは続き、積極的な開発が維持されてきました。
バージョン2.7は、現在も多くの開発者が利用しているPython 2の最終リリースです。2.7は2010年に公開され、Python Foundationは2020年にサポート終了を迎えると発表しています。
Python 3.6は現在のPythonのバージョンで、2016年に初めて公開されました。Python 3は、Pythonプログラミング言語の未来と見なされています。
Python 2と3:プログラミング言語の主な違いとセキュリティへの影響
Python 2とPython 3の主な言語上の違いを以下に示します。どの変更もセキュリティを目的としたものではありませんが、それぞれにセキュリティ上の影響があるため、注意が必要です。
例外の連鎖
Python 3では、デフォルトで例外の連鎖が導入されました。Python 2では、メインのPythonプログラムが参照するライブラリで例外が発生しても、デフォルトではトレースバックにその詳細が表示されませんでした。Python 3では、メインプログラムで発生した例外も、参照先のライブラリで発生した例外も、詳細がトレースバックに表示されます。
コードの内部情報やランタイムの動作がユーザーに公開されると、セキュリティリスクが生じます。例外の連鎖はデバッグには便利ですが、潜在的な攻撃者に多くの情報を与えてしまいます。攻撃者はトレースバックを実行し、ソフトウェアがエラーにどう反応するかを確認できるためです。
リスクがメリットを上回ると考える場合は、Python 3で例外の連鎖を無効にできます。
入力メソッド
Python 2にはeval()関数があります。これは、引数として渡されたテキストを評価するため、安全でないことが知られています。また、評価時に使用するグローバル値を2つ目の引数として任意で指定できます。Python 2のinput()関数もeval()と同様に動作するため、同じく安全ではありません。
攻撃者は、入力に変数名を含めることでこれらの関数を悪用し、その変数の値を取得できます。変数には機密情報が含まれている場合があります。
そのため、Python 2ではセキュリティのベストプラクティスとして、eval()やinput()を使わず、入力テキスト内の変数を評価しないraw_input()を使うことが推奨されます。
Python 3では何が変わったのでしょうか。raw_input()は削除され、その機能はinput()という新しい関数に引き継がれました。新しいinput()関数は、Python 2のraw_input()と同じように動作するため、安全に使えるようになりました。
ただし、Python 3のプログラムをPython 2で実行すると、すべての入力が悪用される可能性があります。
まとめ:
関数 | Python 2.x | Python 3.x |
|---|---|---|
| 変数を評価する — 安全ではない | 変数を評価する — 安全ではない |
| 変数を評価する — 安全ではない | Python 2の |
| 変数を評価しない | 削除 |
整数除算
Python 2では、Python 3とは異なる、直感的とは言いにくい方法で整数除算が行われます。Python 2では、整数同士を割ると常に整数値が返されます。これは、計算結果以下の最大の整数です。この除算方法は床関数による除算(floor division)と呼ばれます。
Python 3では、整数同士の除算で自動的に床関数による除算が適用されないため、整数除算がより直感的になっています。
この直感的な整数除算の方法には、Python 2との後方互換性がありません。そのため、Python 3のコードをPython 2で実行するのは特に危険です。整数除算の動作の違いがあっても、構文エラーは発生しないためです。
これもセキュリティ上の問題につながる可能性があります。たとえば、数値のPINをユーザーに入力させ、システムに保存されたPINを入力値で割って認証の成否を判定するアプリを考えてみましょう。Python 3では正常に動作しますが、同じアプリをPython 2で実行すると、多数の不正な試行が通過してしまいます。
この例は現実的ではありませんが、(Python 2のように)整数除算で数値が丸められるか、(Python 3のように)丸められないかに依存すると、動作が変わる可能性が高いことを示しています。攻撃者はこれを悪用し、防御機構を回避できるかもしれません。
Unicodeのサポート
Pythonの両バージョンでは、文字列(文字の並び)の扱い方が異なります。
Python 2では、デフォルトでASCIIエンコーディングが使われます。ASCIIで表現できるのは256文字に限られます。そのため、特に標準以外の文字をエンコードする際の柔軟性が制限されます。
Python 2でUnicodeを使うには、追加の構文が必要です。たとえば、printを使う場合、特殊文字を扱うために入力テキストをunicode()関数で囲む必要があります。
Unicode規格ははるかに柔軟で、128,000を超える文字に対応しています。Python 3ではUnicodeがデフォルトです。Unicode値を定義するために追加の構文は必要なく、自動的にutf-8文字列として出力されます。
この変更は強力な機能である一方、重大なフィッシングリスクにつながる可能性があります。たとえば、ドメイン名の文字を英語以外のアルファベットに置き換えたURLへユーザーを誘導できます。Xudong Zhengの投稿では、ユーザーにどのように見えるかが紹介されています。

一見するとURLに問題はなさそうですが、実際はそうではありません。Xudongが例に挙げたURLは「apple.com」に見えますが、実際には「xn-pple-43d.com」です。たとえば、ASCIIの「a」(U+0061)ではなくキリル文字の「а」(U+0430)を使うと、一部のブラウザーで実際のドメインが意図せず隠されることがあります。サイトが安全に見えても、ユーザーはセキュリティリスクを含む可能性がある別のドメインに誘導されます。これはホモグラフ攻撃と呼ばれます。
この攻撃は、Pythonアプリが解析するディレクトリパスやクエリパラメーターを標的にした場合、Pythonでも関係する可能性があります。たとえば、アプリケーションがhttp://domain.com/Acmeというパスを公開している場合、攻撃者は「Acme」の文字を別のアルファベットの文字に置き換え、ユーザーを別のアカウントに誘導する可能性があります。
アプリをPython 2から3に移行する際は、すべての入力がUnicodeとして扱われることを考慮する必要があります。これにより、攻撃者がユーザーをだます経路が生まれる可能性があります。
Python 3.6と2.7の脆弱性およびセキュリティ修正
Python 2と3の過去のバージョンには、深刻なセキュリティ脆弱性が存在していたため、使用は避けるべきです(Pythonの各バージョンについてはCVE Detailsを参照してください)。
ただし、最新バージョンの2.7.xと3.6.xでは、ほとんどの脆弱性が修正されています。Python 2と3はいずれも現在も積極的に保守されており、最新バージョンに更新していれば、重大な脆弱性からは安全と言えるでしょう。とはいえ、Python Software FoundationがPython 2に注力する度合いは低くなり、Python 3よりセキュリティ更新の頻度も下がる可能性があります。
最も重要なのは、Python 2が2020年にサポート終了を迎えることです。それ以降、この言語は保守されず、セキュリティ修正も提供されなくなります。Python 2を使い続ける場合は、2020年以降に安全ではなくなることを踏まえ、Python 3への移行計画を立て始める必要があります。
依存関係グラフが異なれば、脆弱なライブラリも異なる
Python 3に後方互換性がなかったため、Pythonの主要なパッケージマネージャーであるpipも、2.xと3.xの系列を用意し、各パッケージがどのバージョンをサポートするか記録する必要がありました。そのため、アプリケーションの移植では、多くの場合、使用するライブラリやそのバージョンを変更する必要があります。
他のソフトウェアと同様、こうしたライブラリも完璧ではなく、脆弱性が発見されることがあります。脆弱性が公表されると、攻撃者に悪用される前にアプリケーションの所有者は急いで修正する必要があります。たとえば、Equifaxはこの競争に大きく敗れました。
脆弱性の修正が、あらゆるバージョンに移植されるとは限りません。そのため、移植プロセスの一環として、既知の脆弱性をテストすることが重要です。何らかの理由でPython 2を使い続ける場合、脆弱性のリスクはさらに高まります。脆弱性の修正も含め、ライブラリが古いバージョンのサポートを終了しつつあるためです。
使用するPythonのバージョンにかかわらず、GitHub integration、command line interface、またはその他のintegrated platformsを利用して、Snykでアプリケーションの既知の脆弱性をテストできます。