分散型計算とは、時間と状態に関するものです。つまり、複数のコンポーネントが通信するためには、状態を共有しなければならず、そのためには時間がかかります。
ほとんどのプログラマーは、自分の仕事を人であるかのように考えています。彼らは、自分で仕事をしなければならない場合、自分がするのと同じように、1 つのスレッドのコントロールでプログラム全体を実行することを考えます。しかし、最近のコンピュータは、タスクの切り替えが非常に速く、マルチコア、マルチ CPU、分散システムなどでは、2 つのイベントが全く同時に発生することもあります。不具合は、プログラマーが考えたプログラムの実行方法のモデルと、現実に起きていることとのギャップを埋めるために押し寄せます。これらの欠陥は、スレッド、プロセス、時間、および情報の間の予期せぬ相互作用に関連しています。これらの相互作用は、セマフォ、変数、ファイル システムなど、基本的には情報を保存できるあらゆるものを含む共有状態を通じて行われます。
HttpSession
属性として保存すると、アプリケーションの信頼性を損なう場合があります。HttpSession
オブジェクトを複数の JVM にわたって複製します。こうすることで、1 つの JVM が利用できなくなった場合でも、アプリケーションを中断させることなく別の JVM が取って代わることができます。Serializable
インターフェイスを実装しなければなりません。
public class DataGlob {
String globName;
String globValue;
public void addToSession(HttpSession session) {
session.setAttribute("glob", this);
}
}
block.timestamp
または block.number
を時間のプロキシとして使用しています。block.timestamp
または block.number
に関連付けられた値は通常、時間依存のイベントをトリガーするために開発者が使用しますが、これらの値から得られる時間感覚の多くは、一般的には使用しない方がよいものです。block.timestamp
の使用は良く言っても信頼性が低く、最悪の場合、メリットに気づいた悪意のあるマイナーがブロックのタイムスタンプを変更する可能性があります。block.number
については、ブロック間の時間 (約 14 秒) を予測することは可能ですが、ブロック時間は一定ではなく、ネットワーク アクティビティに応じて変動する可能性があります。これにより、時間関連の計算において block.number
の信頼性が低くなります。block.number
を使用して、一定期間後に資金のロックを解除します。
function withdraw() public {
require(users[msg.sender].amount > 0, 'no amount locked');
require(block.number >= users[msg.sender].unlockBlock, 'lock period not over');
uint amount = users[msg.sender].amount;
users[msg.sender].amount = 0;
(bool success, ) = msg.sender.call.value(amount)("");
require(success, 'transfer failed');
}
...
var authenticated = true;
...
database_connect.query('SELECT * FROM users WHERE name == ? AND password = ? LIMIT 1', userNameFromUser, passwordFromUser, function(err, results){
if (!err && results.length > 0){
authenticated = true;
}else{
authenticated = false;
}
});
if (authenticated){
//do something privileged stuff
authenticatedActions();
}else{
sendUnathenticatedMessage();
}
true
に、そうでない場合は false
に設定しています。しかし、コールバックは IO によってブロックされるため、処理が非同期に実行され、if (authenticated)
の確認の後に実行される可能性があります。また、デフォルトが true なので、ユーザーが実際に認証されるかどうかにかかわらず if 文が実行されます。
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/download/" + "app.apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
setuid root
をインストールしたプログラムのものです。このプログラムでは、権限が付与されていないユーザーに代わって特定のファイル操作を行い、アクセス チェックを使用して、現在のユーザーが利用できないはずの操作を、root 権限で行わないようにします。このプログラムでは、access()
システム コールを使用して、プログラムを実行している人が指定されたファイルにアクセスする権限を持っているかどうかを確認してから、ファイルを開き、必要な処理を行います。
if (!access(file,W_OK)) {
f = fopen(file,"w+");
operate(f);
...
}
else {
fprintf(stderr,"Unable to open file %s.\n",file);
}
access()
のコールは想定どおりに機能し、プログラムの実行者がファイルに書き込むために必要な権限を持っている場合は 0
を戻し、そうでない場合は -1 を戻します。しかし、access()
と fopen()
はどちらもファイルハンドルではなくファイル名に対して実行されるため、file
変数が fopen()
に渡される際にも、access()
に渡されたときと同じディスク上のファイルを参照している保証はありません。攻撃者が access()
のコール後に file
を別のファイルへのシンボリックリンクに置き換えた場合、そのファイルが本来ならば攻撃者が変更できないファイルであっても、プログラムはファイルを root 権限で操作します。プログラムを操作して本来実行できない操作を実行させることにより、攻撃者は昇格した権限を獲得します。root
権限を持つプログラムに限定されません。本来は攻撃者に許可されていない操作を実行できるアプリケーションはすべて、ターゲットになる可能性があります。
fd = creat(FILE, 0644); /* Create file */
if (fd == -1)
return;
if (chown(FILE, UID, -1) < 0) { /* Change file owner */
...
}
chown()
のコールにより操作されるファイルが creat()
のコールにより作成されたファイルと同じであると仮定していますが、必ずしもそうならない場合があります。chown()
はファイル名に対して操作され、ファイル ハンドルに対しては操作されないため、攻撃者により、攻撃者が所有しないファイルへのリンクにそのファイルが置き換えられる可能性があります。そのような場合、リンクされたファイルの所有権が chown()
のコールによって攻撃者に付与されます。CBL_CHECK_FILE_EXIST
ルーチンを呼び出して、ファイルを作成して必要な操作を実行する前に、ファイルが存在するかどうかを確認します。
CALL "CBL_CHECK_FILE_EXIST" USING
filename
file-details
RETURNING status-code
END-CALL
IF status-code NOT = 0
MOVE 3 to access-mode
MOVE 0 to deny-mode
MOVE 0 to device
CALL "CBL_CREATE_FILE" USING
filename
access-mode
deny-mode
device
file-handle
RETURNING status-code
END-CALL
END-IF
CBL_CHECK_FILE_EXIST
の呼び出しは想定通りに動作し、0 以外の値を返して、ファイルが存在しないことを示します。ただし、CBL_CHECK_FILE_EXIST
と CBL_CREATE_FILE
の両方がファイルのハンドルではなくファイル名に対して操作を行うため、CBL_CHECK_FILE_EXIST
に渡されたときに行ったのと同様に、CBL_CREATE_FILE
に渡されるときに filename
変数が引き続きディスク上の同じファイルを参照するという保証はありません。CBL_CHECK_FILE_EXIST
を呼び出した後に攻撃者が filename
を作成した場合、CBL_CREATE_FILE
の呼び出しは失敗し、プログラムは、実際には攻撃者によって制御されているデータが含まれるにもかかわらず、ファイルが空であると認識してしまいます。root
権限を使用するプログラムに適用される場合があります。また、アクセスチェックを用いて、現在のユーザーが利用できないはずの操作を、root 権限で行わないようにします。プログラムを操作して許可されていない操作を実行させることにより、攻撃者は高い権限を手に入れる可能性があります。java.text.Format
にあるメソッド parse()
および format()
には、あるユーザーが別のユーザーのデータを表示できるという設計上の不備が含まれています。java.text.Format
にあるメソッド parse()
および format()
には、あるユーザーが別のユーザーのデータを表示できるという Race Condition が含まれています。
public class Common {
private static SimpleDateFormat dateFormat;
...
public String format(Date date) {
return dateFormat.format(date);
}
...
final OtherClass dateFormatAccess=new OtherClass();
...
public void function_running_in_thread1(){
System.out.println("Time in thread 1 should be 12/31/69 4:00 PM, found: "+ dateFormatAccess.format(new Date(0)));
}
public void function_running_in_thread2(){
System.out.println("Time in thread 2 should be around 12/29/09 6:26 AM, found: "+ dateFormatAccess.format(new Date(System.currentTimeMillis())));
}
}
format()
の実装にある Race Condition のため、最初のスレッドの日付が2つ目のスレッドの出力に表示されています。