分散型計算とは、時間と状態に関するものです。つまり、複数のコンポーネントが通信するためには、状態を共有しなければならず、そのためには時間がかかります。
ほとんどのプログラマーは、自分の仕事を人であるかのように考えています。彼らは、自分で仕事をしなければならない場合、自分がするのと同じように、1 つのスレッドのコントロールでプログラム全体を実行することを考えます。しかし、最近のコンピュータは、タスクの切り替えが非常に速く、マルチコア、マルチ CPU、分散システムなどでは、2 つのイベントが全く同時に発生することもあります。不具合は、プログラマーが考えたプログラムの実行方法のモデルと、現実に起きていることとのギャップを埋めるために押し寄せます。これらの欠陥は、スレッド、プロセス、時間、および情報の間の予期せぬ相互作用に関連しています。これらの相互作用は、セマフォ、変数、ファイル システムなど、基本的には情報を保存できるあらゆるものを含む共有状態を通じて行われます。
HttpSessionState
属性として保存すると、アプリケーションの信頼性を損なう場合があります。HttpSessionState
オブジェクト、その属性、および参照するすべてのオブジェクトがメモリに保存されます。この処理モデルでは、アクティブセッションの状態が 1 台のマシンのシステムメモリに保存できる範囲に制限されます。これらの制限を取り除き、容量を拡張するために、複数のサーバーに対して永続的なセッション状態情報が設定される場合がよくあります。これにより容量が拡張され複数のマシン間における複製が許可されるため、全体的なパフォーマンスが向上します。セッション状態を保持するために、サーバーは HttpSessionState
オブジェクトをシリアライズする必要があり、このオブジェクトをシリアライズするためには、保存されるすべてのオブジェクトがシリアライズ可能である必要があります。[Serializable]
属性を宣言する必要があります。さらに、オブジェクトで独自のシリアライズメソッドが必要となる場合には、ISerializable
インターフェイスを実装する必要があります。
public class DataGlob {
String GlobName;
String GlobValue;
public void AddToSession(HttpSessionState session) {
session["glob"] = this;
}
}
sleep()
をコールすると、パフォーマンスの低下を招いたり、デッドロックが発生したりする可能性があります。sleep()
をコールすると、他のすべてのスレッドがそのリソースが解放されるのを待機するため、パフォーマンスが低下しデッドロックが発生する可能性があります。 sleep()
をコールしています。
ReentrantLock rl = new ReentrantLock();
...
rl.lock();
Thread.sleep(500);
...
rl.unlock();
if (fitz == null) {
synchronized (this) {
if (fitz == null) {
fitz = new Fitzer();
}
}
}
return fitz;
Fitzer()
オブジェクトが 1 つのみ割り当てられていることを保証したいと考えますが、このコードのコールのたびに同期化させるコストをかけたくはありません。この用法は Double-Checked Locking として知られます。Fitzer()
オブジェクトが割り当てられます。詳細は「Double-Checked Locking is Broken」宣言を参照してください [1]。pthread_mutex_unlock()
をコールしてミューテックスをアンロックする必要がありますシグナルを送信しているスレッドがミューテックスをアンロックできない場合は、2 番目のスレッドにある pthread_cond_wait()
コールは返されず、スレッドは実行されません。 pthread_cond_signal()
をコールしてミューテックスを待機している他のスレッドにシグナルを送信していますが、他のスレッドが待機しているミューテックスのアンロックに失敗しています。
...
pthread_mutex_lock(&count_mutex);
// Signal waiting thread
pthread_cond_signal(&count_threshold_cv);
...
...
if (tmpnam_r(filename)){
FILE* tmp = fopen(filename,"wb+");
while((recv(sock,recvbuf,DATA_SIZE, 0) > 0)&&(amt!=0))
amt = fwrite(recvbuf,1,DATA_SIZE,tmp);
}
...
tmpnam()
、tempnam()
、mktemp()
などの C ライブラリ関数と、先頭に _
(アンダースコア) が付けられた、それぞれに対応する C++ 関数、および Windows API に属する GetTempFileName()
関数です。このグループの関数は、選択されたファイル名の Race Condition の脆弱性があります。関数は、ファイル名が選択されたときに一意であることを保証しますが、ファイルの選択後、アプリケーションがファイルを開こうと試みる前に別のプロセスまたは攻撃者が同一の名前を持つファイルを作成するのを阻止するメカニズムがありません。同じ関数への別のコールによって発生する正当な衝突の危険のほかに、攻撃者が悪意ある衝突を作成する可能性も十分あります。これらの関数によって生成されたファイル名が十分にランダムでなく、推測が難しくないためです。O_CREAT
フラグや O_EXCL
フラグを使用した open()
のコールや、CREATE_NEW
属性を使用した CreateFile()
のコールでファイルが開かれるのは、ファイルが既に存在する場合に失敗するため、前述のタイプの攻撃は阻止されます。ただし、攻撃者が一連のテンポラリファイル名を正確に予測できる場合、アプリケーションは必要とするテンポラリストレージを開こうとしても阻止され、Denial of Service (DoS) 攻撃が発生する可能性があります。これらの関数によって生成されたファイル名の選択に使用されているランダム度が小さければ、このタイプの攻撃を準備するのは容易です。tmpfile()
などの C ライブラリ関数と、先頭に _
(アンダースコア) が付けられた、それに対応する C++ 関数、および多少動作が優れている C ライブラリ関数 mkstemp()
です。tmpfile()
スタイル関数は一意のファイル名を作成して、"wb+"
フラグが渡された場合の fopen()
と同じ方法、つまり読み書きモードでバイナリ ファイルとしてそのファイルを開きます。ファイルが既に存在する場合、tmpfile()
により、サイズがゼロに切り詰められます。これは、一意であるはずのファイル名を選択してから、選択されたファイルをその後で開くまでの間に存在する Race Condition に関する、前述したセキュリティ上の懸念を緩和しようとするものです。ただし、この動作は関数のセキュリティ上の問題を明確に解決するわけではありません。まず、攻撃者は事前にアクセス許可を緩くしたファイルを作成でき、その許可が tmpfile()
により開かれるファイルによって変更される可能性はあまりありません。さらに、UNIX ベースのシステムの場合、攻撃者がファイルを別の重要なファイルへのリンクとして事前に作成すると、アプリケーションは昇格した許可を使用してそのファイルを切り詰める可能性があり、結果的に攻撃者の意図した損傷を与えます。最後に、tmpfile()
が実際に新しいファイルを作成する場合、そのファイルに適用されるアクセス許可はオペレーティングシステムにより異なります。つまり、攻撃者が事前にファイル名を予測できない場合も、アプリケーションデータは脆弱な状態に保たれる可能性があります。mkstemp()
は、テンポラリファイルを作成する上で十分に安全性が確保された方法です。ユーザーが提供したファイル名テンプレートに基づき、ランダムに生成した一連の文字を組み合わせて一意のファイルを作成して開こうとします。ファイルを作成できなかった場合は、失敗して -1
を戻します。最近のシステムでは、ファイルはモード 0600
で開かれます。つまり、ユーザーがアクセス許可を明示的に変更しない限り、ファイルは改竄に対して安全です。しかし、mkstemp()
は予測可能なファイル名が使用されているため攻撃されやすく、攻撃者が使用されるファイル名を予測して事前に作成することで mkstemp()
の失敗を引き起こした場合、アプリケーションは Denial of Service 攻撃にさらされる可能性があります。
...
try:
tmp_filename = os.tempnam()
tmp_file = open(tmp_filename, 'w')
data = s.recv(4096)
while True:
more = s.recv(4096)
tmp_file.write(more)
if not more:
break
except socket.timeout:
errMsg = "Connection timed-out while connecting"
self.logger.exception(errMsg)
raise Exception
...
os.O_CREAT
フラグや os.O_EXCL
フラグを使用した open()
のコールでファイルが開かれる最良のケースでは、ファイルがすでに存在する場合は処理が失敗するため、ここで説明したタイプの攻撃は阻止されます。ただし、攻撃者が一連のテンポラリファイル名を正確に予測できる場合、アプリケーションは必要とするテンポラリストレージを開こうとしても阻止され、Denial of Service (DoS) 攻撃が発生する可能性があります。これらの関数によって生成されたファイル名の選択に使用されているランダム度が小さければ、このタイプの攻撃を準備するのは容易です。tmpfile()
のような関数が含まれます。tmpfile()
スタイル関数は一意のファイル名を作成して、"wb+"
フラグが渡された場合の open()
と同じ方法、つまり読み書きモードでバイナリ ファイルとしてそのファイルを開きます。ファイルが既に存在する場合、tmpfile()
により、サイズがゼロに切り詰められます。これは、一意であるはずのファイル名を選択してから、選択されたファイルをその後で開くまでの間に存在する Race Condition に関する、前述したセキュリティ上の懸念を緩和しようとするものです。ただし、この動作は関数のセキュリティ上の問題を明確に解決するわけではありません。まず、攻撃者は事前にアクセス許可を緩くしたファイルを作成でき、その許可が tmpfile()
により開かれるファイルによって変更される可能性はあまりありません。さらに、UNIX ベースのシステムの場合、攻撃者がファイルを別の重要なファイルへのリンクとして事前に作成すると、アプリケーションは昇格した許可を使用してそのファイルを切り詰める可能性があり、結果的に攻撃者の意図した損傷を与えます。最後に、tmpfile()
が実際に新しいファイルを作成する場合、そのファイルに適用されるアクセス許可はオペレーティングシステムにより異なります。つまり、攻撃者が事前にファイル名を予測できない場合も、アプリケーションデータは脆弱な状態に保たれる可能性があります。
...
HttpSession sesssion = request.getSession(true);
sesssion.setMaxInactiveInterval(-1);
...
HttpSession
属性として保存すると、アプリケーションの信頼性を損なう場合があります。HttpSession
オブジェクトを複数の JVM にわたって複製します。こうすることで、1 つの JVM が利用できなくなった場合でも、アプリケーションを中断させることなく別の JVM が取って代わることができます。Serializable
インターフェイスを実装しなければなりません。
public class DataGlob {
String globName;
String globValue;
public void addToSession(HttpSession session) {
session.setAttribute("glob", this);
}
}
...
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);
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つ目のスレッドの出力に表示されています。Windows.Storage.ApplicationData
クラスの RoamingFolder
または RoamingSettings
プロパティを使用します。RoamingFolder
プロパティおよび RoamingSettings
プロパティは、ローミング アプリのデータストアのコンテナを取得します。このコンテナは、複数デバイス間でのデータの共有に使用することができます。ローミング アプリのデータストアに格納されているオブジェクトに対する読み書きにより、危険にさらされるリスクが高まります。その対象は、ローミング アプリのデータストアを使用してこれらのオブジェクトを共有するデータ、アプリケーション、およびシステムの機密性、完全性、および可用性です。free()
を同じ値で 2 回コールすると、Buffer Overflow が発生する可能性があります。プログラムが free()
を同じ引数で 2 回コールすると、プログラムのメモリ管理データ構造が破損します。これにより、プログラムがクラッシュするか、一部の環境では、その後に malloc()
を 2 回コールして同じポインタを戻すことになります。malloc()
が同じ値を 2 回戻し、その後に攻撃者が、この二重に割り当てられたメモリに書き込まれたデータの制御をプログラムから得た場合、プログラムは Buffer Overflow 攻撃に対して脆弱になります。
void sh(int dummy) {
...
free(global2);
free(global1);
...
}
int main(int argc,char* argv[]) {
...
signal(SIGHUP,sh);
signal(SIGTERM,sh);
...
}
public class GuestBook extends HttpServlet {
String name;
protected void doPost (HttpServletRequest req, HttpServletResponse res) {
name = req.getParameter("name");
...
out.println(name + ", thanks for visiting!");
}
}
Dick
" to name
Jane
" to name
Jane, thanks for visiting!
"Jane, thanks for visiting!
"
public class ConnectionManager {
private static Connection conn = initDbConn();
...
}
<http auto-config="true">
...
<session-management session-fixation-protection="none"/>
</http>
Example 1
では、攻撃者は明らかに直接的な方法を使用しており、あまり知られていない Web サイトまでには攻撃の手を広げていません。ただし、安心は禁物です。攻撃者はこの攻撃手段の限界をバイパスするための七つ道具を持っています。攻撃者が最も一般的に使用するテクニックは、ターゲット サイトにある Cross-Site Scripting や HTTP レスポンス スプリッティングといった脆弱性を利用することです [1]。攻撃者は攻撃対象を操作して、JavaScript などのコードを攻撃対象のブラウザーに反映するなど悪意あるリクエストを脆弱なアプリケーションに送信するように仕向けて cookie を作成し、攻撃者の支配下にあるセッション ID を再使用させることができます。bank.example.com
や recipes.example.com
のように、多数のアプリケーションが同一の最上位ドメインに存在する場合、1 つのアプリケーションに脆弱性があると、攻撃者はセッション ID が固定化された cookie を設定して、ドメイン example.com
上のあらゆるアプリケーションとの間のすべての通信で使用されるようにできます [2]。use_strict_mode
属性を無効にしています。
ini_set("session.use_strict_mode", "0");