特定のスコープ(関数スコープなど)で変数を作成し、グローバルスコープを避けてモジュール化して整理することがプログラミングのベストプラクティスであるという印象を受けます。ただし、セキュリティ上の問題があるかどうかはわかりません。
1年以上うまく機能したBashのグローバル変数の例を次に示します。
cat <<-EOF >> "$HOME"/.profile
set -x
complete -r
export war="/var/www/html" # Web Application Root;
export dmp="phpminiadmin" # Database Management Program;
export -f war
war() {
cd $war/
}
EOF
source "$HOME"/.profile 2>/dev/null
BashやJavaScriptのグローバル変数に問題があったことは一度もありません。おそらく、最小限の環境で個人的に使用する小さなスクリプトしか記述していないためです。
なぜ多くのプログラマーがグローバル変数の使用を避け、グローバル変数の使用によって引き起こされるセキュリティ違反の例があるのですか?
私は Steffen Ullrichのコメント を盗んでいますが、グローバル変数の主な問題は、コードを適切に整理し、保守しやすくすることが難しいことです。 彼のリンク は問題ありませんが、グローバル変数の問題に関する無数の記事をオンラインで見つけるのに問題はありません。
グローバル変数を使用すると、特に単純な線形フローがない場合は、プログラムのどこで変数が変更されるのかを見失いやすくなります。その結果、グローバル変数は小さなスクリプトでは完全に正常に機能しますが、アプリケーションのスケーリングが始まると、大きな問題が発生する可能性があります。
私がグローバルを回避する主な理由は、自動化されたユニット/統合テストを悪夢にするためです。小さなアプリケーションはテストなしで生き残ることができますが、適切なテストなしで大きなアプリケーションを管理しようとするのは悪夢です(私を信じてください、私は若くて愚かな日々で試しました)。
これは、非常に小さなアプリケーションではグローバルが問題ないという印象を与えるかもしれませんが、アプリケーションは通常、時間の経過とともに成長し、一時的に始まるものは永続的なものになるため、実際にそれらを使用することはまったく悪い考えです。適切にスコープされた変数を使用するのがとても簡単なのに、なぜ間違った足で始めるのでしょうか?
グローバル変数を使用しても、セキュリティにdirectの影響はありませんが、セキュリティの問題が発生しやすくなります。
プログラマーがファイルA
の関数を変更したが、その関数の特定の変数の値がファイルB
のユーザー入力に由来するものであることを知らなかったり忘れたりした場合、結果として安全ではなくなる可能性があります。適切な安全対策なしのユーザー入力に関すること。
特にグローバル変数が原因で発生した違反は知りませんが、- グローバル変数の使用は文字通り人を殺した であると主張するのは簡単なので、それらを使用しないことは理にかなっていると思います。
一部のプログラミング環境では、他のソースが read /混乱する可能性があるため、グローバルスコープを信頼できません。
LastPassブラウザー拡張機能でのリモートコード実行 など、これはすでにセキュリティの影響を受けやすい製品に重大な脆弱性をもたらしています。
PHPには_$GLOBALS
_スーパーグローバルがあります; Pythonにはglobals()
関数があり、Javascriptにはwindow
(またはノードではprocess
)オブジェクトこれらすべてにより、すべてのグローバル変数を列挙することは簡単になります。
これを行うと、それらに含まれる情報を吸い出すことも、悪意を持って変更することもできますたとえば、機密データがグローバルに保存されている場合、攻撃者はそれを簡単に抽出できます。たとえば、データベース接続URLがグローバルに格納されている場合、攻撃者はURLをポイントして攻撃者のサーバーを参照させ、被害者はそのサーバーに接続して資格情報を送信する可能性があります。
また、グローバルオブジェクトがたくさんある場合は、それらのメソッドを簡単に呼び出すことができます。
このセキュリティエンジニアリングの原則は、基本的に「仕事をするために必要な以上のアクセス権を誰かに与えないこと」です。すべての関数とメソッドは、その仕事にまったく関係がない場合でも、グローバル変数の読み取りと書き込みを行うことができます。つまり、そのコードの一部が限られた方法でハイジャックされた場合、より大きな攻撃面が開かれます。
(もちろん、ほとんどのスクリプト言語にはPOLPの違反でもある弱いカプセル化ルールがありますが、オブジェクトがスコープ外にある場合、オブジェクトに対して何かを行うのは非常に困難です。)
以下の理由により:
null
に設定し、後で別の関数がクラッシュした場合)どこにでも紙が散らばっているビジネスについて考えると、物事はきちんと整理されて整理されておらず、ただ横になっていて、何かが足りなくなったときに誰が気づくでしょうか?従業員が詐欺を犯したとしても、矛盾はどこにでもあるので、誰もその矛盾を見つけることはできません。キーのペアがなくなった場合、誰かがそれらが何かの下に埋まっているか、誰かがそれらを借りなければならなかったと思います。
これは誇張と言ってもいいかもしれませんが、bigソフトウェアプロジェクトで作業したことはないでしょう。あなたのウェブサイトが数日で構築するのに十分に小さい(そしてあなたがあなたが何をしているのか知っている)ならば、グローバルは問題ありません。彼らは何かを始める時間を節約できます。
しかし、それらはスケーリングしません。
次のコードを比較してください。
1)
/file.php:
$newconn = new PDO('mysql:Host=localhost;charset=utf8mb4;','username','password');
2)
../secrets.php:
$mysqlusername = 'username';
$mysqlpassword = 'password';
/file.php:
require_once('../secrets.php');
$newconn = new PDO('mysql:Host=localhost;charset=utf8mb4;',$mysqlusername,$mysqlpassword);
3
../secrets.php:
function pdoconn(i) {
$mysqlusername = ['username1','username2'];
$mysqlpassword = ['password1','password2'];
$conn = new PDO('mysql:Host=localhost;charset=utf8mb4;',$mysqlusername[i],$mysqlpassword[i]);
return $conn;
}
/file.php:
require_once('../secrets.php');
$newconn = pdoconn(0);
例1は問題外です。本番サーバーの設定が正しくないと、意図しない関係者に機密パラメータが表示される可能性があります。
例2の方が優れていますが、これらの変数はアプリケーション全体で使用でき、変更可能であるため、エラーが発生する可能性があります。
例3は、物事を非常に整理し、転送可能に保ちます。../secrets.php
が次の場合は、グローバルを使用するように変更できます。
../secrets.php:
$mysqlusername = 'username';
$mysqlpassword = 'password';
function pdoconn() {
$conn = new
PDO('mysql:Host=localhost;charset=utf8mb4;',$GLOBALS['mysqlusername'],$GLOBALS['mysqlpassword']);
return $conn;
}
そして、それはグローバルがほとんどの場合非常に簡潔な方法で意味をなさない理由を示していると思います。
グローバル変数を使用したセキュリティ違反(およびなぜ私がこれらの例をPHPで作成したのか)に関しては、(当時)かなりありました PHP 4.2. ここで、register_globalsがオンからオフになっています。この変更は2002年に行われたため、現在、記事は見つかりませんが、いくつかの違反の原因であったことを覚えているようです。マニュアルから直接コピーすると、脆弱なコードの非常に明確な例:
<?php
// define $authorized = true only if user is authenticated
if (authenticated_user()) {
$authorized = true;
}
// Because we didn't first initialize $authorized as false, this might be
// defined through register_globals, like from GET auth.php?authorized=1
// So, anyone can be seen as authenticated!
if ($authorized) {
include "/highly/sensitive/data.php";
}
?>
グローバル変数を使用すると、多くの問題が発生し、実際には何の利点もないためです。
セキュリティに直接関係はありませんが、ユニットテストは開発に不可欠です。そして、何かを適切に単体テストするには、コードが実行される正確なコンテキストを制御できる必要があります。 2つのコードを見てください。
With With Globals
public Money CalculateExpenses(int departmentId)
{
Department department = (from d in global_db.Departments
where d.Id = departmentId
select d).Single();
return (from ex in department.Expenses
select ex).Sum();
}
グローバルなし
public Money CalculateExpenses(int departmentId, Database database)
{
Department department = (from d in database.Departments
where d.Id = departmentId
select d).Single();
return (from ex in department.Expenses
select ex).Sum();
}
データが実際にどこから来ているかを除いて、コードはほとんど同じに見えると言うかもしれません。毎回データベースを渡す必要がないため、Globalsを使用したコードは「よりクリーン」であると考えるかもしれません。
ユニットテストを書く必要があるまで。そのため、運用データベースに接続するのではなく、テスト固有のローカルデータベースに接続するためのロジックを挿入する必要があります。使用したいデータベースをコードに渡すだけでよいので、非グローバルコードははるかに良く見えます。
要約:グローバルのないコードはテストが簡単です。
コードに約200行のコードが含まれている場合、グローバルの使用はそれほど悪くないようです。実際、同じコードの断片をあちこちで再利用する場合(たとえば、ランダムなデータソース、ロギングなど)は、完全に有効なことのように見えるかもしれません。
ただし、コードベースが十分に大きくなると、グローバルの導入は完全に致命的になる可能性があります。あなたは単にあなたのデータがどこから来たのか、そして誰がそれにアクセスできるのかを制御できなくなります。
みんなの好きな言語の例を使ってみましょう:PHP 5. Eevee による例。
@fopen('http://example.com/not-existing-file', 'r');
このコードは何をしますか?それはグローバルな振る舞いに依存するので、あなたは知りません。 PHPが--disable-url-fopen-wrapper
でコンパイルされている場合、機能しません。グローバル構成allow_url_fopen
の場合も同様です。何が行われるかはわかりません。
最初に@
intを指定すると、グローバルPHP configでscream.enabled
が設定されていない限り、エラーメッセージが無効になります。グローバルerror_reporting
レベルが設定されていない場合も、このメッセージは出力されません。正確にどこに出力されるか印刷する場合、グローバルdisplay_errors
に依存します。
このように、1つの無害な行の動作は5つの異なるグローバル変数に依存します。 100万行のコードベースでこのシナリオを想像してみてください。異なるチーム全体で100人の異なるプログラマーがいます。グローバルの使用が忌まわしく、あらゆる種類の問題を引き起こす理由がすぐにわかります。
いつか仕事に行って、まだ触れていなくても、コードが突然壊れたと想像してください。それがグローバルの魔法です。
グローバルの使用が悪い理由の例は他にもあります。これは他の回答で見つけることができます。
かなり言語固有(C/C++が頭に浮かぶ)で、引き離すのは難しいですが、攻撃の可能性があります。
Computerphileは 素晴らしいビデオ についてそれを心からお勧めしますが、この攻撃のメカニズムの簡単なバージョンと、それがグローバル変数とどのように相互作用するかを次に示します。
バッファオーバーフローは、境界チェックが不十分なために、バッファに書き込まれたデータが宛先バッファに隣接するメモリアドレスのデータ値も破損した場合に発生します。これは、最初にデータが宛先バッファー内に収まることを確認せずに、あるバッファーから別のバッファーにデータをコピーするときに発生する可能性があります。 ( ウィキペディア )
グローバル変数の場合、(ほとんどの言語では)プログラムの開始時にメモリセグメントが割り当てられるため、プログラムのメモリレイアウトはかなり静的です。これにより、攻撃者に一貫性が与えられます。常にショット間を移動しているときよりも、常に同じ場所から(グローバルの場合)撮影しているときは、別の領域のメモリゾーンをターゲットにする方がはるかに簡単です。
これはグローバル変数に固有のものではありませんが、このような構造で意味のある情報を保持しますfacilitates(有効化と間違えないように!)これらの種類の攻撃。
グローバル変数はわずかなセキュリティリスクをもたらす可能性がありますが、バッファオーバーフロー攻撃を恐れる場合は、防御策として入力サイズをチェックする必要がありますリファクタリングコードグローバル変数を取り除くために。
BashやJavaScriptのグローバル変数に問題があったことは一度もありません。おそらく、最小限の環境で個人的に使用する小さなスクリプトしか記述していないためです。
なぜ多くのプログラマーがグローバル変数の使用を避け、グローバル変数の使用によって引き起こされるセキュリティ違反の例があるのですか?
小さなプロジェクトでは、グローバル変数で十分です。彼らの問題のほとんどは現れない。そして、上記のBash環境のカスタマイズは確かにTinyです。
プログラムとしてグローバル変数が問題を引き起こし始める scales ;これはさまざまな方法で発生する可能性がありますが、問題の核となる部分は、各コードを理解するために必要なstateの量です。
1000万行のコードベースを想像してください。その中には、100万個の変数があります。それらはすべてグローバルです。
特定の100行のコードで、20の変数を使用する場合があります。
関数を呼び出すときはいつでも、関数がそれらの20個の変数のどれを変更するかわかりません。関数が開始したとき、変数の状態がどこから来たかはわかりません。
つまり、100行のコードが何を意味するかを理解するには、1000万行のコードをすべて理解して頭に留めるか、データの取得元、データの種類について何らかの規則が必要です。ヘルパー関数などを呼び出す場合は変更できません。
対照的に、コードが関数の引数、ローカル変数、戻り値の型によってほぼ完全に支えられている場合、関数を理解するだけで、その機能を理解できます 。さらに、その関数が別の関数を呼び出す場合、その関数に渡す情報と、その関数が返す情報がわかります。
あなたはその情報でそれが何をしているか知らないかもしれませんが、あなたはそれが何をしているか知っていますt do。
たとえば、integer x
とx
を関数に渡さなかったため、また戻り値から関数に割り当てなかったため、確信があります。
locallyについてコードを推論しやすくすることは、非常に重要な目標です。あなたは関数を読み、それが何をしているのかを知り、バグを特定できるようにしたいと考えています。
コードの記述は、明確で推論しやすいコードを作成するよりもはるかに簡単で、はるかに重要性が低くなります。
大規模で永続的なプロジェクトのほとんどすべてのコードは、変更されるよりも何百倍も頻繁に読み込まれ、一度作成されて設計されます。
このルールから-ローカルについて推論しやすくする-「グローバル状態を回避する」というルールに到達します。
読み書きされるグローバル変数は、グローバル状態の例です。しかし、プロジェクト内のすべてのものが最初の引数として渡されるポインタを持っているというスーパーオブジェクトもあり得ます。
スーパーオブジェクトは依然としてグローバル変数よりも優れています。グローバル変数は、多くの場合、初期化とクリーンアップの方法について混乱するメカニズムを持っています(私はC/C++を見ています)。すべての「グローバル」状態のスーパーオブジェクトがある場合、インスタンス化できます2つのバージョンのスーパーオブジェクトを同じプロセスで同時に実行します(これは機能しない傾向がありますが、これは通常、暗黙的なグローバル状態が原因です) OSがあなたを襲います)。
パラメータではなく、読み取るすべてのグローバル変数は、コードの任意のビット、anywhereはその動作を変更している可能性があります。書き込むグローバル変数はすべて、一部のコードの動作を任意に遠くまで変更していることを意味します。
そして、世界の国々が苦痛を感じるのは人間だけではありません。状態がローカルの場合、グローバルな状態または環境を "偽造"するテストハーネスを書くのがはるかに簡単になります。 (たとえば)グローバルな「マウス」オブジェクトがある場合、特定の動きを装う偽のマウスを作成する単体テストを書くのが難しくなり、記録されたマウスの動きをコードに再生するマクロを書くことがさらに難しくなる場合があります。特に、UIがマウスを使用している実際の人間に反応している間にそれを実行する場合。
セキュリティは、コードの機能を理解する機能です。プログラム内のセキュリティとは、「コードが実行を許可されていることのみを目的としている」ことを意味します。グローバル変数を使用すると、コードの機能を理解する能力が低下するため、コードの機能を制御する能力が低下します。
セキュリティの観点から見ると、グローバル変数の問題は、予期しない動作を引き起こし、ローカリゼーションを破壊する可能性があることです。この背後にあるより一般的なアイデアは "Action at a distance" と呼ばれ、プログラムのある部分が別の部分に予期しない影響を与える可能性があります。
ローカライゼーションが壊れているため、プログラムの一部だけを理解する必要はなく、プログラムを操作するすべての人が、グローバル変数に関係するプログラムの一部を理解する必要があります。これは設計の複雑さを大幅に増大させ、一般に、複雑な設計はバグが多い可能性が高いため、安全ではありません。コンピューター科学者のEdsger Dijkstraがかつて「有能なプログラマーは自分の頭蓋骨の厳密に制限されたサイズを完全に認識している」と述べました。
プログラムは、規模と範囲の時間とともに成長する傾向があります。単独で作成された単純なスクリプトは、モジュール化されていない1つのことだけを実行しますが、グローバル変数の厄介な影響を忘れた可能性がある将来どこかで再利用される可能性があります。グローバル変数が含まれていることさえ誰も知りません。グローバル変数は、地面に穴を残したり、踏みつける準備ができている間違った方法に直面している熊手、または私道の釘のようなものです。
それらは、多くの点で差し迫った危険にさらされており、時には必要に応じてそのように扱われるべきです。
なぜ多くのプログラマーがグローバル変数の使用を避け、グローバル変数の使用によって引き起こされるセキュリティ違反の例があるのですか?
グローバル変数を使用しないコードを変更して、意図しないアプリケーションで実行するほうが簡単なので、元の開発者が予測していなかったアプリケーション。実際、グローバルな状態に依存している場合、集中的なリファクタリング(グローバルな状態をリファクタリングすること)を行わなくても、アプリケーションでそのような状態を最大1つ持つことができます。
このような意図しないアプリケーションには、マルチスレッド環境でコードを実行することが含まれる場合があります。
しかし、多くの人々が気づいていないこと: malloc()
はグローバルです!
グローバル変数の使用に反対する人々は、常にグローバルアロケーターステートを使用しています。
これは、コードの一部が大量のメモリを割り当て、malloc()
がオペレーティングシステムからより多くのメモリを要求する場合、メモリブロックはコードの他の部分から割り当てられたメモリと混同され、同じ部分がコードのコードは、使用していたメモリをすべて解放します。free()
呼び出しは、断片化のためにメモリをオペレーティングシステムに戻しません。その結果、プログラムは必要以上のメモリを使用することになります。
したがって、グローバル変数の使用に反対する人々は、実際にはグローバル状態でアロケーターを使用しています。
主要な標準化組織による標準(ISOによるC89/C99/C11/C18、IEEEによるPOSIX)が、複数のアロケーター状態、つまり独立したヒープを持つことを可能にするアロケーターを定義していないのはなぜでしょうか。
ほぼすべてのプログラミング言語が変数をグローバルに宣言でき、その実装コードのスコープ内でのみアクセスできる場合、これは問題ではありません。グローバル変数を使用する理由はそれほど多くありません。
しかし、これは当てはまりません。そのため、自分のプロジェクトをC++ではなくCで記述し、クラス定義の最も外側の節で静的変数として自然にサポートされていることに気付いたとき、グローバル変数で間に合わせるために。