Pythonは初心者にも経験豊富な開発者にも適した、高速でプラットフォームに依存しない、習得しやすいプログラミング言語です。Pythonは、1991年の最初のリリース以来、コンピュータの世界で常に存在感を示し、わかりやすいコードと汎用性によって定番の言語となりました。今日では、幅広いライブラリやフレームワークを備え、素早く簡単なPythonプログラミング、いわゆるPythonic(パイソニック)な開発方法の基礎となっています。
しかし、プログラミング言語である以上、Pythonもセキュリティの脅威からは免れられません。攻撃者によるリスクを回避するには、セキュアコーディングのベストプラクティスを採用する必要があります。この投稿では、セキュアなアプリケーションを構築する場合に採用する必要があるPythonのセキュリティのベストプラクティスについて説明します。
常に最新のコードを使用して、ソフトウェアが問題なく動作し、攻撃者に隙を与えないようにする必要があります。Pythonも例外ではありません。Pythonバージョン3では、バージョン2と比較して、ソフトウェアのセキュリティを確保するための機能が大きく進歩しています。Pythonのもう一つの利点は、報告されたセキュリティの欠陥に迅速に対処する一大コミュニティの存在です。Pythonの脆弱性の現状について質問がある場合は、こちらのページから回答をご覧いただけます。
Pythonはバージョン間の完全互換性がなく、違いが存在するために、Pythonバージョン2.xで記述したコードをPythonバージョン3.xで実行できない場合があることをお伝えしておく必要があります。これによって新しいバージョンのPythonに移行するためには多くのコード変更が必要になるため、開発チームにさまざまな問題をもたらします。
それでも、Python 3.xではセキュリティ面が改善されているので、更新は価値がある重要なことです。Python 3.xで行われた変更は次のとおりです。
古いバージョンのコード(Pythonでのサポートが終了したコード)を使用する場合は、入力を検証するか、シェルやプロセスの実行などの危険な関数によって実装された関数の呼び出しを避ける必要があります。Python 3.xでの言語の構文とセマンティクスの変更は下位互換性がないため、大規模なコードベースの単純な移行は容易ではなく、大きな困難を伴いますが、それでもお勧めしたい方法です。
前述のように、大規模なコミュニティがPythonとPythonライブラリをサポートし、その機能を拡張していますが、Python Package Index(PyPI)から取得するパッケージがプロジェクトにとって安全であることを確認することは困難な場合があります。PyPIを用いることで、パッケージのメンテナンス担当者が提出書類に署名して、導入者がダウンロードの整合性検証と作成者の本人確認をできるようにすることが可能ですが、PyPIのパッケージはセキュリティレビューを受けないことに注意してください。
まずライセンスのコンプライアンスから説明しましょう。プロジェクトはさまざまなライセンスで発行され、それぞれに固有のライセンス義務がありますが、ライセンスの種類を問わず、法的な問題を回避するために、すべてのライセンス義務を遵守する必要があります。知的財産を守るために、特定のライセンスを完全に回避した方がいい場合もあります。ライセンスコンプライアンスの詳細については、過去のブログ投稿をご覧ください。
Pythonのセキュリティのベストプラクティスに従うことで、ユーザーやお客様は、コード内の脆弱性やバグを気にすることなく安全なコードを使用できます。対象として考慮するコードには2種類あります。1つは、独自開発のコード(内製したコード)です。独自開発のコードの検査には、セキュリティの問題を引き起こす可能性があるコード内の誤りを開発中に発見できるCoverity®などの静的アプリケーション・セキュリティ・テスト(SAST)ツールが最適です。
もう1つは、外部から取得したコードです。直接的コードおよび推移的コード内の依存関係を含むオープンソース・コードまたはサードパーティ・コードの処理には、Black Duck®などのソフトウェア・コンポジション解析(SCA)ツールが最適です。この種のツールは、使用しているパッケージに関する情報(ライセンス、セキュリティ、運用リスク状態など)を明らかにします。SASTとSCAの両方のツールを使用すると、ソフトウェア開発ライフサイクル(SDLC)の早期段階で誤りを発見できるため、コストと時間がかかる後工程での対処を軽減できます。
対話型のソフトウェアでは、ユーザー入力とそれに対するソフトウェアの反応が重要ですが、どれほど有用な入力でもインジェクション攻撃を招く可能性があるため、高リスクを伴います。SQLインジェクションは、一般的で単純なインジェクション攻撃です。
SQLインジェクションは、脆弱性を狙って、ソフトウェアがデータベースに対して実行するクエリを操作する攻撃方法です。比較的単純なコマンドを挿入することで、認可チェックをWebポータルへの管理者アクセス権に変更するなどの操作を行うことが可能です。一部のWebプラットフォームでは、ユーザー名とパスワードに使用できる特殊文字を制限し、SQLインジェクション攻撃でそれらの文字が使用されないようにしていますが、この場合は強力なパスワードの作成がさらに難しくなります。そのため、入力をサニタイズする(つまり、すべての入力をチェックし、有効な入力、許容可能な文字シーケンス、許可する組み合わせを定義するルールを作成する)ことをお勧めします。これにより、インジェクション攻撃を防ぐとともに、強力なパスワードの生成が可能になります。
セキュリティをさらに強化するには、データベースがプリペアド(準備済み)ステートメントをサポートしていることを確認してください。MySQL、MS SQL Server、PostgreSQLなどのデータベースでは、SQLインジェクションなどの脆弱性から保護するためにこの機能がサポートされています。場合によっては、特にSQLステートメントを反復実行する際などに、アプリケーションのパフォーマンスが向上することもあります。Pythonでは、データベースでサポートされていない場合でも、プリペアドステートメントを使用できます。Pythonは標準ライブラリでこの機能をサポートしており、必要に応じてクライアント側でエミュレートします。プリペアドステートメントを使用するセキュリティ上の最大の利点は、SQLステートメントとユーザー指定データの分離です。これにより、ユーザーが指定したデータを悪用してSQLステートメントを変更することはできなくなり、ロジックが変更されることのないコンパイル済みのステートメントが使用されます。
悪意のあるアクターは、多くの場合、URLのスペルを間違えたユーザーを捕捉するためにスペルミスのあるドメインを作成します。PyPIからライブラリをフェッチする場合も同様です。そして正当な名前に似た名前が付けられた悪意のあるパッケージをリポジトリに配置し、誰かが誤ってそのパッケージを取得するように仕掛ける可能性があります。
絶対インポートでは、使用するパッケージの完全パスを指定します。相対インポートでは、importステートメントを作成したプロジェクトの場所を基準にした相対パスで指定されたパッケージをインポートします。相対インポートには2つのタイプがあります。
暗黙的な相対インポートには、有害なパッケージがプロジェクトの別の部分に(別のライブラリのインポートを介して)進路を見つけ、意図したライブラリの代わりに誤って使用される可能性があるというリスクがあります。パスが未指定で混同が生じる可能性があるため、暗黙的な相対インポートはPython 3.xから削除されました。まだ古いバージョンのPythonを使用している場合は、暗黙的な相対インポートを削除し、絶対インポートまたは明示的な相対インポートを使用してください。
仮想環境の使用は、セキュリティ上の理由からも、開発環境を整理された状態に維持するという点でもPythonのセキュリティのベストプラクティスです。フォルダー構造のないオペレーティングシステムを想像してみてください。すべてのファイル(構成ファイル、使用しているすべてのライブラリ、テキストドキュメント、画像、音楽ファイル、ビデオ)を1つのフォルダーに集め、ファイル名のみで区別するとします。目的の適切なファイルを見つけるのが困難であるばかりか、ファイル名の重複の問題も発生します。このような混乱状態は、過誤やセキュリティ上の問題の温床になるでしょう。すべてのファイルが全く整理されず、システム全体に散在する状態でもこれと同様の混乱が生じます。どのライブラリがどこで使用されているか、削除した場合にどのプロジェクトに影響するかがわかりません。
プロジェクトの仮想環境を設定する場合は、プロジェクトに必要なすべてのパッケージが使用可能で、システム上の他のプロジェクトから分離されていることを確認する必要があります。これにより、ライブラリ間の衝突や競合を回避できます。Python 2.7を使用していて、Python 3.xでプロジェクトを開始する最適な方法を探している場合、Python 2.7を使用するプロジェクトに影響を与えずにPython 3.xを使用する仮想環境を構築できるため、やはり仮想環境がお勧めです。仮想環境では、誤って取得した可能性がある悪意のあるパッケージを封じ込めることができるため、システム全体に影響が及ばないようにすることが可能です。
「インターネットは決して忘れない」という言葉がありますが、これは画像やメディアばかりでなく、コードと共に配布される可能性のある機密情報にも当てはまります。
開発中には、テストを容易にするためにパスワード、認証情報を含むURL、APIキーなどの情報をハードコーディングする場合がありますが、ハードコーディングされたシークレット(機密情報)は忘れられてGitHubなどのコードリポジトリに送られる可能性があるため、この方法はお勧めできません。そうなった場合、シークレットはデータベースまたはログに記録され、誰でも見られるようになります。アップロードするファイル(コード、Readme、構成ファイル、特にプレーン・テキストファイル)には、一切の機密情報を含めないようにしてください。
私たちは皆、開発中の試行錯誤の範例を知っています。コーディングとテストを行い、結果に基づいてコードを修正し、再度テストします。これは適切なデバッグ情報を継続的に提供する終わりのないプロセスです。そのため、多くの場合、開発環境ですべてのデバッグ出力を表示します。
開発環境と運用環境を切り離す必要がある理由もここにあります。運用システムのデバッグ情報はセキュリティ上のリスクになります。環境が分離されていない場合、すべてのバグが一般ユーザーに通知されるため、悪意のあるアクターはシステムの侵害方法に関する情報を入手できます。公開される可能性がある運用システムのデバッグ情報をオフにし、問題に関する通知を例外処理コードに置き換えて、内部バグ追跡システムにその情報を保存します。ユーザーには、必要に応じてエラーに関する一般的な説明のみが表示されるようにする必要があります。
デバッグ情報は表示しないに越したことはありませんが、問題を修正するためにやむを得ない場合があります。システムの運用を開始する前に、すべてのデバッグ情報に注意してください。それにはSASTおよびSCAツールがお勧めです。Coverity SASTツールは、独自開発コードの脆弱性につながる開発上のミスを発見し、Black Duck SCAはオープンソース・コンポーネントとその直接的または推移的な依存関係をチェックしてコードにもたらされる可能性があるリスクを検出します。これらのテストは、コードのセキュリティリスクが運用環境に入り込む前に確実に排除するために有効です。
このPythonのセキュリティのベストプラクティスに関する概要が、Pythonでの開発のための便利なヒントになったことを願っています。Pythonは、機械学習、人工知能、データサイエンス、Web開発など、さまざまな分野で利用できます。Web開発ではPHPに匹敵する言語として機能し、Django Webフレームワークと連携することで結果を迅速に得ることができます。Pythonを使い始めたばかりでも、既に開発に使用している場合でも、セキュアなアプリケーションを構築・保守するには、脅威とその回避方法を熟知する必要があります。