ハッキングに強いように設計されたプログラミング言語はありますか?
つまり、設計は完璧ですが、実装が壊れているためにアプリケーションがハッキングされる可能性があります。開発者が仕様を誤って実装するリスクを軽減したいと考えています。
例えば
使用されている言語が Buffer Over-Read を防ぐことができれば、ハートブリードは起こりませんでした。
マネージコードと比較すると、ポインターの問題/オーバーフローはCでより頻繁に発生します
数値の丸めエラー 開発者が間違ったデータに対して間違ったデータ型を使用すると発生する可能性がある
アプリが multi-threaded である場合、サービス拒否攻撃は軽減される可能性があります
質問
編集:多くの人がバッファオーバーフローの問題に対処しました。または、プログラマーがセキュリティを担当すると言います。私は、セキュリティを可能な限り合理的に利用することを主な目的とする言語が存在するかどうか考えているだけです。つまり、一部の言語には、他のほとんどの言語より明らかに安全性が高い(または低い)機能がありますか?
Ada言語 は、一般的なプログラミングエラーをできるだけ防ぐように設計されており、システムバグが壊滅的な結果をもたらす可能性がある重要なシステムで使用されます。
Adaが他の現代言語によって提供される典型的な組み込みのセキュリティを超えるいくつかの例:
整数の範囲タイプでは、整数の許容範囲を指定できます。この範囲外の値は例外をスローします(範囲タイプをサポートしていない言語では、手動でチェックする必要があります)。
:=
は割り当て用=
は等価性チェック用。これにより、代入に=
を使用し、同等性チェックが意図されていた場合に偶然に代入することで同等に==
を使用する言語の一般的な落とし穴を回避できます(Adaでは、偶発的な代入はコンパイルされません)。
in
およびout
パラメーターは、メソッドパラメーターの読み取りまたは書き込みが可能かどうかを指定します
end
キーワードの使用によるステートメントグループのインデントレベルの問題(最近の Apple SSLバグ など)を回避します
契約(Ada 2012以降、以前はSPARKサブセット)に含まれていた)により、メソッドは、満たす必要がある前提条件と事後条件を指定できます
安全で安全な小冊子 (PDF)で提供されるセキュリティのためにAdaがどのように設計されたかの例がさらにあります。
もちろん、これらの問題の多くは、適切なコーディングスタイル、コードレビュー、ユニットテストなどによって軽減できますが、言語レベルでそれらを実行することは、無料で入手できることを意味します。
Adaなどのセキュリティ用に設計された言語は多くのクラスのバグを削除するという事実にもかかわらず、その言語が何も知らないビジネスロジックバグの導入を妨げるものは何もないことも付け加える価値があります。
実際、ほとんどの言語はバッファオーバーフローに関して「安全」です。その点で言語が「安全」であるために必要なのは、厳密な型、体系的な配列の境界チェック、および自動メモリ管理(「ガベージコレクター」)の組み合わせです。詳細は this answer を参照してください。
いくつかの古い言語は、その意味で「安全」ではありません。特にC(およびC++)、Forth、Fortran ...、そしてもちろん、Assemblyです。 技術的に、「安全」でありながら正式にC標準に準拠するCの実装を作成することは可能ですが、価格が高くなります(たとえば、 free()
を操作なしにするため、割り当てられたメモリは「永久に」割り当てられます)。誰もそれをしません。
「バッファオーバーフローに関する」「安全な」言語には、Java、C#、OCaml、Python、Perl、Go、さらにはPHPが含まれます。これらの言語の一部は、SSL/TLSを実装するのに十分効率的です(組み込みシステムであっても-私は経験から話します)。安全なCコードを書くことは可能ですが、(多くの)集中力とスキルが必要であり、経験から、それが困難であり、最高の開発者でさえも偽ることができないことが繰り返し示されていますそれらが常に必要なレベルの集中力と能力を適用すること。これは謙虚な体験です。 「Cを使用しないでください、危険です」という主張は不人気です。それは間違っているからではありませんが、それとは逆に、真実であるため、開発者は自分たちの半神ではないという考えに直面する必要があります。彼らの魂のプライバシーの奥深くにあると彼らが信じているプログラミング。
ただし、これらの「安全な」言語はバグを防止しないことに注意してください。バッファオーバーフローは依然として望ましくない動作です。しかし、それらには損傷が含まれています。バッファを超えたメモリは、実際には読み書きされません。代わりに、問題のあるスレッドが例外をトリガーし、(通常は)終了します。ハートブリードの場合、これはバグが脆弱性になることを回避し、過去数日間に観察されたフルパニックを防ぐのに役立つ可能性があります(誰もランダムな脆弱性が韓国の目に見えない馬をフィーチャーしたYoutubeビデオのように口コミで広まる原因を本当に理解していますが、「論理的に」、脆弱性がまったくなかった場合、これはこの悲劇をすべて回避するはずです)。
編集:コメントで豊富に議論されたので、Cの安全なメモリ管理の問題について考えました、そして一種の解決策がありますそれでもfree()
が機能することを許可しますが、チートがあります。
「ファットポインタ」を生成するCコンパイラを想像できます。たとえば、32ビットのマシンでは、ポインタを96ビットの値にします。割り当てられた各ブロックには、一意の64ビット識別子(たとえば、カウンター)が付与され、IDによってすべてのブロックを参照する内部メモリ構造(ハッシュテーブル、バランスツリーなど)が維持されます。各ブロックについて、その長さも構造に記録されます。ポインタ値は、ブロックIDとそのブロック内のオフセットを連結したものです。ポインターが追跡されると、ブロックはIDによって配置され、オフセットはブロック長と比較され、その後にのみアクセスが実行されます。このセットアップは、二重解放と解放後使用を解決します。また、mostバッファオーバーランを検出します(すべてではありません:バッファはより大きな構造の一部であり、malloc()
/free()
管理は外部ブロックのみを参照します)。
「チート」は「ユニークな64ビットカウンター」です。これは、64ビット整数が不足しない限り当てはまります。それを超えると、古い値を再利用する必要があります。 64ビットは実際にはその問題を回避する必要があります(「ラップアラウンド」には何年もかかります)が、小さいカウンター(たとえば32ビット)が問題になる可能性があります。
また、もちろん、メモリアクセスのオーバーヘッドは無視できません(アクセスごとに数回の物理読み取りですが、最適化される場合もあります)。また、ポインタサイズを2倍にすると、ポインタが豊富な構造のメモリ使用量も増加します。 。私はそのような戦略を適用する既存のCコンパイラについては知りません。それは現在、純粋に理論的なものです。
Heartbleedのようなプログラミングエラーに関しては、Cよりも高いレベルのほとんどのプログラミング言語ははるかに安全です。主にマシンコードにコンパイルする例には、D、RustおよびAdaが含まれます。メモリの安全性だけについて話すのは興味深いと思います。
以下は、安全でないコードを書くことをはるかに難しくする(私は思う)追加のプログラミング言語機能のリストです。最初の5つの機能は、コードを推論する際のコンパイラの機能を拡張するため、エラーを起こしやすい人間youである必要はありません*。さらに、これらの機能により、他の人間である監査人がコードを推論しやすくなります。 OpenSSLのソースコードは、混乱しているとしばしば説明されており、Cよりも厳密な言語を使用することで、推論が容易になります。最後の2つの機能は、セキュリティにも影響するコンテキストの問題に関するものです。
私の言語の限られた知識から、これらのすべてを行う言語はありません。 Rust は、多くの言語をカバーする言語の例です。厳密で、デフォルトでは不変であり、安全性が制限されており、ガベージコレクションを必要とせず、非常に高性能で移植性があります。コンパイル時の汚染チェックと依存型は現在、残念ながら追加の静的コード分析ツールまたは新しい言語のいずれかを必要とするエキゾチックな機能のようです。
*参照: 正式な検証
あなたが求めていることの一般的な精神では、私は E言語 (「安全な分散純粋オブジェクトプラットフォームとp2pスクリプト言語」)は、安全に提供しようとしているという点で、かなり興味深いと思います一般に利用できない機能/計算モデル。
すべての現在の(まだ更新されている)プログラミング言語は、固有のセキュリティ欠陥をできる限り少なくするように設計されていますが、結局のところほとんどの場合)セキュリティ欠陥を担当するのはプログラマーであり、自分の言語ではありません使用しています。
編集:@DCKingが指摘したように、すべての言語が同等であるとは限りません。ランダムに1つを選んで動作させるのは良い考えだとは言っていません。私は(非常に)才能のあるCプログラマーが、より高いレベルの言語で書かれた意味的に同一のプログラムと同じくらい安全なプログラムを作成できると言っています。私の要点は、一部の言語は間違いを犯しやすくすることを認識する必要があることですが、結局のところ、それは言語の誤りではなく、プログラマーの誤りであることを知っている必要があります(例外はいくつかあります)
安全な言語というものはありません。言語が問題に対して十分なセキュリティを提供するかどうかは、解決しようとしている問題に大きく依存します。 Webアプリケーションを作成している場合と同様に、このコンテキストで使用されるほとんどの言語(Java、PHP、JavaScript ...のお気に入りを追加)のセキュリティは、バッファオーバーフローなどを防ぐのに十分ですが、より強く型付けされた言語でも十分ではありませんWeb固有のものに対する固有のサポートを提供します。クロスサイトスクリプティングのバグなどを導入することを不可能または少なくとも困難にするようなものです。また、DNSサーバー(DNS再バインドなど)の信頼、現在のPKIモデル、またはサードパーティのスクリプト(例:制御不能)をWebアプリケーション(通常は広告またはGoogleアナリティクス)に含めるなど、悪意のある信頼モデルから言語を保護することはできません。 。
ですから、適切な言語を選択することは少し役立つかもしれませんが、魔法の安全剣はありません。
ほとんどのプログラミング言語では、2言語のセキュリティについて心配する必要があることに注意してください。実際に使用している言語と、コンパイラまたはインタープリタが記述されている言語がありますが、それは多くの場合異なります。 (技術的には、CPU自体のマイクロコードである3番目があります。)これらの言語のどちらかのセキュリティ問題により、プログラムが安全でなくなる可能性があります。
安全な言語はたくさんあります。メモリ管理とスレッドセーフティを備えた言語は、言語が取得できるのと同じくらい安全です。
ただし、これらのほとんどは非効率的です。ガベージコレクションは高価であり、インタプリタ言語はより高価です。そして、それが今日の大規模なアプリケーションがメモリ安全でないC/C++で書かれている理由です。
私は最近 Rust で遊んでいますが、私にとっては、これのために部分的に設計されたという意味で「安全な」言語であるように思われます。
これはC++のようなコンパイル言語であり、ポインタと同時実行性も提供します。 (ガベージコレクターは不要)
ただし、ポインタのメモリの安全性とそれとの同時実行性は保証されません。 Rustはプログラマを信頼しない言語であり、コンパイル時にポインタの不審な使用をチェックします。複数の種類がありますポインタ/参照(借用、所有など)の一部であり、それらのいくつかには厳密なルールがあります。たとえば、次のことはできません。
スレッドの安全性を保証する同様のルールがあります。必要に応じて、安全でないマークが付いたボックス(「信頼してください、私が何をしているか知っています」)またはガベージコレクションの遅いポインターを使用して、これらのチェックの多くをバイパスできます。クローンと参照の組み合わせを使用してこれを行うより素朴な(そして効率的な)方法もあり、使用方法の変化に応じて異なります。
まず、実際にプログラミング言語でプログラムを作成するわけではありません。 instructionsをコンパイラに記述しますdescribeどのような種類のプログラムが必要か、コンパイラは独自の方法でプログラムを生成します。適切に設計されている)ソースコードと同じことを行います。すべてのプログラムは、実行中は「機械語」で表されます。これらは一連の数値であり、RAMにロードされてCPUに供給されると、特定の方法で解釈されます。機械語はハッキングに対する堅牢性を考慮して設計されていないため、実際のプログラムはいずれにせよ機械語であるため、コンパイルされた言語がハッキングに対して本当に「耐性」をもつことはありません。解釈された言語またはVM言語は、最終的には機械語にコンパイルされたネイティブフレームワークで実行されるため、問題は解決しません。
第二に、ほとんどの実際の言語はチューリング完全です。つまり、そのうちの1つで実行できるタスクはすべて実行できます。したがって、「ハッキング」を不可能にすることはできません(ハッキングが悪意のあるプログラムの作成を意味する場合)。それはチューリングの完全性を壊すでしょう。
この時点で、ハッキングの意味を明確にしておく価値があります。ハートブリードについて言及しているので、ストールマンの意味でそれを意味しているのではないと思います(「遊び心のあるいじくり回し」)。
メモリに直接アクセスしてデータを盗むプログラムを書く人、または他のプログラム(ウイルスやキーロガーなど)を変更する人を意味する場合、これは言語が実際に対処できる問題ではありません。コンパイラは、コンパイル時に難読化されたマシンコードを生成する追加の関数を提供することで役立ちますが、最終的には、熟練したメモリハッカーが自分の道を見つけることがまだ可能です。この問題の解決策はOSの設計です。オペレーティングシステムはプログラムをサンドボックス化する必要があり、あるプログラムが別のプログラムに属するメモリを混乱させることを許可しません。これはWindowsのUACの機能の一部です(Sandboxieがより良い例ですが)。
ここに注意があります:C#やJavaのような一部の言語には機能があります(より正確には、コンパイラーとVMプログラムが内部で実行することは機能を持っています)プログラムが別のプログラムのメモリを悪用しようとしているかどうかを確認し、これが発生するとIllegalAccessException
などのエラーをスローします(たとえば、_keylogger.exe
_は_Credit_card_number
_の値を読み取れないはずです) _internet banking application.exe
_から)もちろん、これには、どのメモリがどのプログラムに属しているかを追跡する必要があります。これには、重要なパフォーマンスと労力コストがあります。Cのような一部の「より単純な」言語にはありません。これが理由です。ウイルスのようなハックの多くはCで書かれています。現在、UACを回避することについて巧妙にする必要がありますが、Windows 98の時代には、コンピュータにOSに対してあらゆる種類のクレイジーなことを行ったり、メモリに書き込んだりすることができました。想定されていません。C#でも、通常のCのようなポインター(言語がunsafe
を呼び出し、次のようにマークする必要がある)を使用するオプションがあることに注意してください。必要に応じて-CLRにはおそらくハッキングが含まれますが、CLRにセキュリティホールがあり、メモリの残りの部分にトンネルできる場合を除きます。
2番目の種類のハッキングは、既存のプログラムのバグを悪用することです。これは、ハートブリードが属するカテゴリです。これで、問題はプログラマーが間違いをするかどうかです。もちろん、あなたの言語がBrainfuckやPerlのように非常に読みにくいものである場合、間違いを犯す可能性があります。 C++のように多くの「問題」がある言語(「クラシック」if (i=1)
とif (i==1)
またはC難読化コンテストを参照)の場合、間違いを見つけるのが難しい場合があります。この意味で、セキュリティのための設計は、プログラマーのエラーを最小限に抑えるための設計のほんのささいな特別なケースです。
ハートブリードバグは、故意の妨害行為か正直な間違いかに関係なく、使用されたalgorithmの問題であったことに注意してください(作者がサイズをチェックするのを忘れた)-AIに匹敵するほどコンパイラは不足しています非常に賢い人間なら、それを検出することを期待できます。結果として生じるアクセス違反は、おそらくいくつかの巧妙なメモリ管理で捕らえられたかもしれませんが。
ハッキングに関して2種類の懸念事項があります。
プログラムが誤ってプログラムされており、本来すべきでないことができるようになっています。例えば。 Gmailサーバーでは、サーバーソフトウェアの開発中に誰かがエラーを犯したため、最初に正しいユーザー名とパスワードを入力するように要求する代わりに、誰でもあなたのメールを見ることができます。バグ、脆弱性などが含まれます。
プログラムは、ハッカーの悪意のあるプログラムによって操作されます。ウイルス、キーロガー、その他のマルウェアが含まれます。
(1)言語をより厳密かつ明示的にすることで修正できるため、エラーの検出はより簡単になりますが、最終的には自動ツールで検出できるのは非常に単純なエラーのみであり、Adaの範囲チェックのような「トリップワイヤー」については、議論の余地があります。エラーの可能性を認識することは、最初にチェックを追加することを考えるために必要であり、可能性を認識することはすでに最も難しい部分です。
(2)言語を変更しても修正できません。悪意のあるアプリケーションを書くことが非常に困難な言語を作成すると、ハッカーは単に別の言語を使用し、最終的にはとにかくマシンコードとして実行されるため、自分の言語で書かれたプログラムを操作するのに困難はありません。これは、実行中のプログラムを非常に慎重に監視するOSを作成することで修正できますが、(1)タイプの問題OSのソースコード内の問題になります。
マネージタイプセーフ言語は、タイプの検証を自動的に提供し、コードの実行をCPU自体から遠ざけることで、この種のことを防ぐために多くのことを行いますが、言語が使用するシステムの実装にバグがある可能性を排除するものではありませんCPUにマップします(たとえば、.NetのCLRまたはJavaのJVM)。また、アプリケーション自体の操作やデータ漏洩に対して脆弱になる可能性のあるアプリケーションのバグの可能性を排除していません。
それらはシステムのセキュリティをかなり改善しますが、その機能を提供するために実行しなければならない実行エンジンのオーバーヘッドにより、機能がより大きく、遅く、より制限されます。
安全のために設計された言語は確かにありますが、完璧な言語はありません。たとえば、Adaでは整数変数の許容範囲を指定でき、それらがその範囲外になると例外がスローされます。いいですね、手動でチェックする必要がなくなりました。問題は、手動でチェックする必要がない場合に、このメカニズムを設定して、その結果、つまり整数が範囲外の例外であることを考慮するのを簡単にすることです。サービス拒否攻撃のベクトルを作成しました。
セキュリティはプロセスです。言語は役立ちますが、せいぜい、エラーが発生する可能性を減らすことしかできず、プロセス内で通常、新しく、多くの場合さらに微妙なエラーが作成されます。おそらく、Cは完全に理解しやすく、完全に確定的な操作(たとえば、バックグラウンドガベージコレクションなし)の性質上、安全なコードを書くのに理想的です。そして確かに、多くのセキュリティクリティカルなコードを見ると、それはCで書かれています。Adaに公平を期すために、それはセキュリティよりも信頼性の問題です。