競合状態の脆弱性とは何ですか?

競合状態の脆弱性とは何ですか?
あなたのような読者は、MUO のサポートを支援します。当サイトのリンクを使用して購入すると、アフィリエイト手数料が発生する場合があります。

競合状態は、2 つの操作を特定の順序で実行する必要がある場合に発生しますが、逆の順序で実行される可能性があります。





たとえば、マルチスレッド アプリケーションでは、2 つの個別のスレッドが共通の変数にアクセスする場合があります。その結果、一方のスレッドが変数の値を変更した場合、もう一方のスレッドは最新の値を無視して古いバージョンを引き続き使用する可能性があります。これにより、望ましくない結果が生じます。





今日のメイク動画

このモデルをよりよく理解するには、プロセッサのプロセス切り替えプロセスを詳しく調べるとよいでしょう。





iphone 12 pro max vs s21 ultra

プロセッサがプロセスを切り替える方法

最新のオペレーティング システム マルチタスクと呼ばれる、複数のプロセスを同時に実行できます。このプロセスを観点から見ると、 CPUの実行周期 、マルチタスクは実際には存在しないことに気付くかもしれません。

代わりに、プロセッサはプロセスを常に切り替えて同時に実行するか、少なくともそうしているように動作します。 CPU は、完了する前にプロセスを中断し、別のプロセスを再開することがあります。オペレーティング システムは、これらのプロセスの管理を制御します。



たとえば、最も単純なスイッチング アルゴリズムの 1 つであるラウンド ロビン アルゴリズムは、次のように機能します。

  CPU が現在実行中の 1 つのアクティブなプロセスと、キューに入れられた 3 つのスリープ プロセスを示す図

通常、このアルゴリズムでは、オペレーティング システムが決定するように、各プロセスを非常に短い時間だけ実行できます。たとえば、これは 2 マイクロ秒の期間である可能性があります。





CPU は各プロセスを順番に実行し、2 マイクロ秒間実行されるコマンドを実行します。その後、現在のプロセスが終了したかどうかに関係なく、次のプロセスに進みます。したがって、エンドユーザーの観点からは、複数のプロセスが同時に実行されているように見えます。ただし、舞台裏を見ると、CPU はまだ順番どおりに処理を行っています。

ところで、上の図が示すように、ラウンド ロビン アルゴリズムには最適化や処理優先度の概念がありません。その結果、これは実際のシステムではめったに使用されないかなり初歩的な方法です。





ここで、これらすべてをよりよく理解するために、2 つのスレッドが実行されていると想像してください。スレッドが共通変数にアクセスすると、競合状態が発生する可能性があります。

Web アプリケーションと競合状態の例

以下の単純な Flask アプリをチェックして、これまでに読んだすべての具体的な例を振り返ってください。このアプリケーションの目的は、Web 上で行われる金銭取引を管理することです。という名前のファイルに次を保存します。 money.py :

from flask import Flask 
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)

class Account(db.Model):
id = db.Column(db.Integer, primary_key = True)
amount = db.Column(db.String(80), unique = True)

def __init__(self, count):
self.amount = amount

def __repr__(self):
return '' % self.amount

@app.route("/")
def hi():
account = Account.query.get(1) # There is only one wallet.
return "Total Money = {}".format(account.amount)

@app.route("/send/")
def send(amount):
account = Account.query.get(1)

if int(account.amount) < amount:
return "Insufficient balance. Reset money with /reset!)"

account.amount = int(account.amount) - amount
db.session.commit()
return "Amount sent = {}".format(amount)

@app.route("/reset")
def reset():
account = Account.query.get(1)
account.amount = 5000
db.session.commit()
return "Money reset."

if __name__ == "__main__":
app.secret_key = 'heLLoTHisIsSeCReTKey!'
app.run()

このコードを実行するには、アカウント テーブルにレコードを作成し、このレコードでトランザクションを続行する必要があります。コードからわかるように、これはテスト環境であるため、テーブルの最初のレコードに対してトランザクションを行います。

from money import db 
db.create_all()
from money import Account
account = Account(5000)
db.session.add(account)
db.session.commit()

これで、残高が ,000 のアカウントが作成されました。最後に、Flask および Flask-SQLAlchemy パッケージがインストールされている場合は、次のコマンドを使用して上記のソース コードを実行します。

python money.py 

これで、単純な抽出プロセスを実行する Flask Web アプリケーションが完成しました。このアプリケーションは、GET 要求リンクを使用して次の操作を実行できます。 Flask はデフォルトで 5000 ポートで実行されるため、アクセスするアドレスは 127.0.0.1:5000/ .このアプリは、次のエンドポイントを提供します。

  • 127.0.0.1:5000/ 現在の残高を表示します。
  • 127.0.0.1:5000/send/{金額} 口座から金額を引きます。
  • 127.0.0.1:5000/リセット アカウントを ,000 にリセットします。

さて、この段階で、競合状態の脆弱性がどのように発生するかを調べることができます。

競合状態の脆弱性の確率

上記の Web アプリケーションには、競合状態の可能性のある脆弱性が含まれています。

最初に ,000 を持っていて、 を送信する 2 つの異なる HTTP 要求を作成するとします。このために、2 つの異なる HTTP リクエストをリンクに送信できます。 127.0.0.1:5000/送信/1 .仮定すると、すぐに ウェブサーバー 最初の要求を処理すると、CPU はこのプロセスを停止し、2 番目の要求を処理します。たとえば、次のコード行を実行すると、最初のプロセスが停止する場合があります。

account.amount = int(account.amount) - amount 

このコードは新しい合計を計算しましたが、まだレコードをデータベースに保存していません。 2 番目の要求が開始されると、同じ計算が実行され、データベースの値 (,000) から が減算され、結果が保存されます。最初のプロセスが再開すると、それ自体の値 (,999) が保存されますが、これには最新の口座残高は反映されません。

したがって、2 つのリクエストが完了し、それぞれがアカウントの残高から を差し引いたはずであり、その結果、新しい残高は ,998 になります。ただし、Web サーバーがそれらを処理する順序によっては、最終的な口座残高が ,999 になる場合があります。

ターゲット システムに 1 ドルを送金するためのリクエストを 5 秒間で 128 回送信したとします。この取引の結果、予想される勘定明細書は 5,000 ドル - 128 ドル = 4,875 ドルになります。ただし、競合状態のため、最終的な残高は ,875 ~ ,999 の間で変動する可能性があります。

プログラマーはセキュリティの最も重要な構成要素の 1 つです

ソフトウェア プロジェクトでは、プログラマーとしてかなりの責任があります。上記の例は、単純な送金アプリケーション用です。銀行口座や大規模な電子商取引サイトのバックエンドを管理するソフトウェア プロジェクトに取り組んでいると想像してみてください。

それらを保護するために作成したプログラムに脆弱性がないようにするには、そのような脆弱性に精通している必要があります。これには強い責任が必要です。

競合状態の脆弱性はその 1 つにすぎません。使用するテクノロジに関係なく、作成するコードの脆弱性に注意する必要があります。プログラマーとして習得できる最も重要なスキルの 1 つは、ソフトウェア セキュリティに精通していることです。