remove
コマンドを使用して、外部の攻撃者はデータセット全体を削除することができます。最近、インターネット上で実行しているオープンな MongoDB の安全ではないインスタンスに対する悪意のある攻撃が報告されています。攻撃者はデータベースを消去し、復元のために身代金を支払うよう要求しました。remove
コマンドを使用して、外部の攻撃者はデータセット全体を削除することができます。 最近、インターネット上で実行しているオープンな MongoDB の安全ではないインスタンスに対する悪意のある攻撃が報告されています。 攻撃者はデータベースを消去し、復元のために身代金を支払うよう要求しました。FLUSHALL
コマンドを使用して、外部の攻撃者はデータセット全体を削除することができます。 最近、インターネット上で実行しているオープンな Redis の安全ではないインスタンスに対する悪意のある攻撃が報告されています。 攻撃者はデータベースを消去し、復元のために身代金を支払うよう要求しました。 Read()
や System.IO
クラスの一部である関連メソッドを誤解することはよくあります.NET では、エラーや異常イベントが発生すると、通常例外が発生します(これは .NET が C のような言語より優れている点の 1 つです。例外は、何が起こり得るのかについてプログラマが考える際に役立ちます。)しかし、少量のデータさえ利用可能であれば、ストリームや Reader クラスでは異常である、あるいは例外的であるとは見なされません。これらのクラスは戻りバッファに少量のデータを追加し、読み込まれたバイト数または文字数を戻り値にセットします。戻されるデータ量がリクエストされるデータ量と等しいという保証はありません。Read()
やその他の IO メソッドからの戻り値を確認して、想定どおりの量のデータを確実に受け取ることが重要な意味を持ちます。Read()
からの戻り値を無視します。攻撃者がこれより小さいファイルを作成できる場合、プログラムは前のユーザーからのデータの残りをリサイクルし、それが攻撃者のデータであるかのように処理します。
char[] byteArray = new char[1024];
for (IEnumerator i=users.GetEnumerator(); i.MoveNext() ;i.Current()) {
string userName = (string) i.Current();
string pFileName = PFILE_ROOT + "/" + userName;
StreamReader sr = new StreamReader(pFileName);
sr.Read(byteArray,0,1024);//the file is always 1k bytes
sr.Close();
processPFile(userName, byteArray);
}
char buf[10], cp_buf[10];
fgets(buf, 10, stdin);
strcpy(cp_buf, buf);
fgets()
が返された時点で、buf
には NULL ターミネートする 9 文字以下の文字列が格納されていると予測します。ところが入出力エラーが発生した場合、fgets()
で処理された buf
は NULL ターミネートしません。さらに、文字が全く読まれないうちにファイルの終端に到達した場合は、buf
には何も書き込まれていない状態で fgets()
が返されます。この 2 つの状況では、fgets()
によって返される NULL
により異常が発生したことがわかりますが、このコードの中では警告されません。buf
が NULL ターミネートされていないと、次の strcpy()
がコールされた場合に Buffer Overflow の原因となります。read()
と多くの java.io
クラスの関連メソッドを誤解してしまうのは珍しいことではありません。Java のほとんどのエラーと異常イベントは例外を発生させます。(これは Java が C などの言語に対して持つメリットです:例外は、何が起こり得るのかについてプログラマが考える際に役立ちます。)しかし、少量のデータしか利用できないようになっている場合、stream クラスと reader クラスは異常または例外とはみなされません。これらのクラスは戻りバッファに少量のデータを追加し、読み込まれたバイト数または文字数を戻り値にセットします。戻されるデータ量がリクエストされるデータ量と等しいという保証はありません。read()
およびその他の IO メソッドからの戻り値を調べることが重要です。read()
からの戻り値を無視します。攻撃者がこれより小さいファイルを作成できる場合、プログラムは前のユーザーからのデータの残りをリサイクルし、それが攻撃者のデータであるかのように処理します。
FileInputStream fis;
byte[] byteArray = new byte[1024];
for (Iterator i=users.iterator(); i.hasNext();) {
String userName = (String) i.next();
String pFileName = PFILE_ROOT + "/" + userName;
FileInputStream fis = new FileInputStream(pFileName);
fis.read(byteArray); // the file is always 1k bytes
fis.close();
processPFile(userName, byteArray);
}
read()
からの戻り値を無視します。攻撃者がこれより小さいファイルを作成できる場合、プログラムは前のユーザーからのデータの残りをリサイクルし、それが攻撃者のデータであるかのように処理します。
var fis: FileInputStream
val byteArray = ByteArray(1023)
val i: Iterator<*> = users.iterator()
while (i.hasNext()) {
val userName = i.next() as String
val pFileName: String = PFILE_ROOT.toString() + "/" + userName
val fis = FileInputStream(pFileName)
fis.read(byteArray) // the file is always 0k bytes
fis.close()
processPFile(userName, byteArray)
}
StreamReader
の Finalize()
メソッドは最終的に Close()
をコールしますが、Finalize()
メソッドが呼び出されるまでの所要時間には何も保証がありません。それどころか、Finalize()
が呼び出されるという保証すらありません。その結果、処理量が多い環境では、利用可能なファイルハンドラを VM が使い尽くしてしまう可能性があります。例 2: 通常の条件下では、次のコードはデータベース クエリを実行し、データベースから戻された結果を処理し、割り当てられた
private void processFile(string fName) {
StreamWriter sw = new StreamWriter(fName);
string line;
while ((line = sr.ReadLine()) != null)
processLine(line);
}
SqlConnection
オブジェクトを閉じます。しかし、SQL の実行中や結果の処理中に例外が発生すると、SqlConnection
オブジェクトは閉じられないままになります。これが頻繁に発生する場合、データベースは利用できるカーソルが不足し、SQL クエリをそれ以上実行できなくなります。
...
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(queryString);
cmd.Connection = conn;
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
HarvestResults(rdr);
conn.Connection.Close();
...
int decodeFile(char* fName)
{
char buf[BUF_SZ];
FILE* f = fopen(fName, "r");
if (!f) {
printf("cannot open %s\n", fName);
return DECODE_FAIL;
} else {
while (fgets(buf, BUF_SZ, f)) {
if (!checkChecksum(buf)) {
return DECODE_FAIL;
} else {
decodeBlock(buf);
}
}
}
fclose(f);
return DECODE_SUCCESS;
}
CALL "CBL_CREATE_FILE"
USING filename
access-mode
deny-mode
device
file-handle
END-CALL
IF return-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
PERFORM write-data
IF ws-status-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
DISPLAY "Success!"
END-IF
END-IF
CALL "CBL_CLOSE_FILE"
USING file-handle
END-CALL
GOBACK
.
New()
関数は、システム ログ デーモンへの新しい接続を確立します。これは log.syslog パッケージの一部です。返されたライターに書き込むたびに、指定された優先度 (syslog 機能と重大度の組み合わせ) とプレフィックス タグを持つログ メッセージが送信されます。ビジー環境では、これによってシステムがソケットをすべて消費する可能性があります。例 2: この例では、
func TestNew() {
s, err := New(syslog.LOG_INFO|syslog.LOG_USER, "the_tag")
if err != nil {
if err.Error() == "Unix syslog delivery error" {
fmt.Println("skipping: syslogd not running")
}
fmt.Println("New() failed: %s", err)
}
}
net/smtp
パッケージの Dial()
メソッドが、localhost の SMTP サーバーに接続された新しいクライアントを返します。接続リソースは割り当てられますが、Close()
関数を呼び出しても解放されません。
func testDial() {
client, _ := smtp.Dial("127.0.0.1")
client.Hello("")
}
Arena.ofConfined()
によって作成されたリソースが閉じられていません。
...
Arena offHeap = Arena.ofConfined()
MemorySegment str = offHeap.allocateUtf8String("data");
...
//offHeap is never closed
BEGIN
...
F1 := UTL_FILE.FOPEN('user_dir','u12345.tmp','R',256);
UTL_FILE.GET_LINE(F1,V1,32767);
...
END;
onPause()
、onStop()
、または onDestroy()
イベント ハンドラで Camera
インスタンスの解放に失敗しています。onPause()
、onStop()
、または onDestroy()
コールバックで解放されていない Camera
インスタンスを割り当てます。 Android OS は、現在のアクティビティをバックグラウンドに送信する必要がある場合や、システムリソースの不足時に現在のアクティビティを一時的に破壊する必要がある場合に、これらのコールバックを呼び出します。 Camera
オブジェクトを適切に解放しないため、アクティビティが原因で他のアプリケーション (または同じアプリケーションの将来のインスタンス) はカメラにアクセスできません。 さらに、アクティビティの一時停止中に Camera
インスタンスを所有し続けた場合、電池が無駄に消費されてしまうため、ユーザー体験の質が低下することがあります。Camera
オブジェクトを解放するために使用されるべきベースの onPause()
メソッドを上書きもせず、シャットダウン処理中に適切に解放もしない Android アクティビティを記述しています。
public class UnreleasedCameraActivity extends Activity {
private Camera cam;
@Override
public void onCreate(Bundle state) {
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
cam.stopPreview();
}
}
onPause()
、onStop()
、または onDestroy()
イベントハンドラで MediaRecorder
、MediaPlayer
、または AudioRecord
オブジェクトの解放に失敗します。onPause()
、onStop()
、または onDestroy()
コールバックで解放されていないメディアオブジェクトを割り当てます。Android OS は、現在のアクティビティをバックグラウンドに送信する必要がある場合や、システムリソースの不足時に現在のアクティビティを一時的に破壊する必要がある場合に、これらのコールバックを呼び出します。アクティビティがメディアオブジェクトを適切に解放できないと、Android のメディアハードウェアに対する (他のアプリケーションまたは同じアプリケーションによる) 後続のアクセスが、ソフトウェア実装に左右される、あるいは完全に失敗することさえあります。多数の解放されていないメディアインスタンスをオープンしたままにしておくと、Android が例外を発生させ、事実上 Denial of Service を引き起す可能性があります。さらに、アクティビティの一時停止中に media インスタンスを所有し続けた場合、電池が無駄に消費されてしまうため、ユーザー体験の質が低下することがあります。onPause()
メソッドを上書きもせず、シャットダウン処理中に適切に解放もしない Android アクティビティを記述しています。
public class UnreleasedMediaActivity extends Activity {
private MediaPlayer mp;
@Override
public void onCreate(Bundle state) {
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
mp.stop();
}
}
onPause()
、onStop()
、または onDestroy()
イベントハンドラで Android データベースハンドラの解放に失敗します。onPause()
、onStop()
、または onDestroy()
コールバックで閉じられていない Android SQLite データベースハンドラを割り当てます。Android OS は、現在のアクティビティをバックグラウンドに送信する必要がある場合や、システムリソースの不足時に現在のアクティビティを一時的に破壊する必要がある場合に、これらのコールバックを呼び出します。データベースを適切に閉じることができないと、アクティビティが常に再起動されている場合、使用可能なカーソルのデバイスが使い果たされてしまう可能性があります。また、実装の内容によっては、Android オペレーティングシステムもまた DatabaseObjectNotClosedException
を発生させる可能性があるため、この例外が認識されない場合はアプリケーションがクラッシュします。onPause()
は上書きされません。また、シャットダウンの処理中にデータベース オブジェクトを適切に解放することもしません。
public class MyDBHelper extends SQLiteOpenHelper {
...
}
public class UnreleasedDBActivity extends Activity {
private myDBHelper dbHelper;
private SQLiteDatabase db;
@Override
public void onCreate(Bundle state) {
...
db = dbHelper.getWritableDatabase();
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
db.insert(cached_data); // flush cached data
}
}
DATA: result TYPE demo_update,
request TYPE REF TO IF_HTTP_REQUEST,
obj TYPE REF TO CL_SQL_CONNECTION.
TRY.
...
obj = cl_sql_connection=>get_connection( `R/3*my_conn`).
FINAL(sql) = NEW cl_sql_prepared_statement(
statement = `INSERT INTO demo_update VALUES( ?, ?, ?, ?, ?, ? )`).
CATCH cx_sql_exception INTO FINAL(exc).
...
ENDTRY.
SqlConnection
オブジェクトを閉じます。しかし、SQL の実行中または結果処理中に例外が発生すると、SqlConnection
オブジェクトは閉じません。これが頻繁に発生する場合、データベースは利用できるカーソルが不足し、SQL クエリをそれ以上実行できなくなります。
...
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(queryString);
cmd.Connection = conn;
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
HarvestResults(rdr);
conn.Connection.Close();
...
- void insertUser:(NSString *)name {
...
sqlite3_stmt *insertStatement = nil;
NSString *insertSQL = [NSString stringWithFormat:@INSERT INTO users (name, age) VALUES (?, ?)];
const char *insert_stmt = [insertSQL UTF8String];
...
if ((result = sqlite3_prepare_v2(database, insert_stmt,-1, &insertStatement, NULL)) != SQLITE_OK) {
MyLog(@"%s: sqlite3_prepare error: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), result);
return;
}
if ((result = sqlite3_step(insertStatement)) != SQLITE_DONE) {
MyLog(@"%s: step error: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), result);
return;
}
...
}
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(CXN_SQL);
harvestResults(rs);
stmt.close();
func insertUser(name:String, age:int) {
let dbPath = URL(fileURLWithPath: Bundle.main.resourcePath ?? "").appendingPathComponent("test.sqlite").absoluteString
var db: OpaquePointer?
var stmt: OpaquePointer?
if sqlite3_open(dbPath, &db) != SQLITE_OK {
print("Error opening articles database.")
return
}
let queryString = "INSERT INTO users (name, age) VALUES (?,?)"
if sqlite3_prepare(db, queryString, -1, &stmt, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("error preparing insert: \(errmsg)")
return
}
if sqlite3_bind_text(stmt, 1, name, -1, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure binding name: \(errmsg)")
return
}
if sqlite3_bind_int(stmt, 2, age) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure binding name: \(errmsg)")
return
}
if sqlite3_step(stmt) != SQLITE_DONE {
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure inserting user: \(errmsg)")
return
}
}
ZipFile
の finalize()
メソッドは最終的に close()
をコールしますが、finalize()
メソッドが呼び出されるまでにどれくらいの時間がかかるかは保証されません。つまり、使用中の環境では JVM ですべてのファイルハンドルを使い切ってしまう場合もあります。例 2: 通常の状況下では、次の修正プログラムは、すべての zip ファイルエントリを指定した後にファイルハンドルを適切に閉じます。しかし、エントリ間で同じ処理を繰り返すときに例外が発生すると、zip ファイルハンドルが閉じられなくなります。この状況が頻繁に発生すると、JVM は、利用可能なファイルハンドルをすべて使い尽くすことがあります。
public void printZipContents(String fName) throws ZipException, IOException, SecurityException, IllegalStateException, NoSuchElementException {
ZipFile zf = new ZipFile(fName);
Enumeration<ZipEntry> e = zf.entries();
while (e.hasMoreElements()) {
printFileInfo(e.nextElement());
}
}
public void printZipContents(String fName) throws ZipException, IOException, SecurityException, IllegalStateException, NoSuchElementException {
ZipFile zf = new ZipFile(fName);
Enumeration<ZipEntry> e = zf.entries();
while (e.hasMoreElements()) {
printFileInfo(e.nextElement());
}
zf.close();
}
...
lo_client = cl_apc_tcp_client_manager=>create( i_host = host
i_port = port
i_frame = lv_frame
i_protocol = protocol
i_ssl_id = ssl_id
i_event_handler = lo_event_handler ).
" initiate the connection setup, successful connect leads to execution of ON_OPEN
lo_client->connect( ).
...
例 2: 通常の状況下では、次の修正プログラムは、ソケットと関連するストリームを適切に閉じています。しかし、入力を読み取るときや画面にデータを書き込むときに例外が発生すると、ソケットオブジェクトが閉じられない場合があります。この状況が頻繁に発生すると、システムはソケットを使い果たし、それ以降の接続を処理できなくなります。
private void echoSocket(String host, int port) throws UnknownHostException, SocketException, IOException
{
Socket sock = new Socket(host, port);
BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream()));
while ((String socketData = reader.readLine()) != null) {
System.out.println(socketData);
}
}
private void echoSocket(String host, int port) throws UnknownHostException, SocketException, IOException
{
Socket sock = new Socket(host, port);
BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream()));
while ((String socketData = reader.readLine()) != null) {
System.out.println(socketData);
}
sock.close();
}
StreamReader
の Finalize()
メソッドは最終的に Close()
をコールしますが、Finalize()
メソッドが呼び出されるまでにどれくらいの時間がかかるかは保証されません。それどころか、Finalize()
が呼び出されるという保証すらありません。その結果、処理量が多い環境では、利用可能なファイル ハンドラを VM が使い尽くしてしまう可能性があります。
private void processFile(string fName) {
StreamWriter sw = new StreamWriter(fName);
string line;
while ((line = sr.ReadLine()) != null)
processLine(line);
}
FileInputStream
の finalize()
メソッドは最終的に close()
をコールしますが、finalize()
メソッドが呼び出されるまでにどれくらいの時間がかかるかは保証されません。つまり、使用中の環境では JVM ですべてのファイル ハンドルを使い切ってしまう場合もあります。
private void processFile(String fName) throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream(fName);
int sz;
byte[] byteArray = new byte[BLOCK_SIZE];
while ((sz = fis.read(byteArray)) != -1) {
processBytes(byteArray, sz);
}
}
...
CFIndex numBytes;
do {
UInt8 buf[bufferSize];
numBytes = CFReadStreamRead(readStream, buf, sizeof(buf));
if( numBytes > 0 ) {
handleBytes(buf, numBytes);
} else if( numBytes < 0 ) {
CFStreamError error = CFReadStreamGetError(readStream);
reportError(error);
}
} while( numBytes > 0 );
...
def readFile(filename: String): Unit = {
val data = Source.fromFile(fileName).getLines.mkString
// Use the data
}
...
func leak(reading input: InputStream) {
input.open()
let bufferSize = 1024
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
while input.hasBytesAvailable {
let read = input.read(buffer, maxLength: bufferSize)
}
buffer.deallocate(capacity: bufferSize)
}
...
performOperationInCriticalSection()
の前にロックを確立しますが、このメソッドで例外が発生する場合にはロックを解放しません。
Object synchronizationObject = new Object ();
System.Threading.Monitor.Enter(synchronizationObject);
performOperationInCriticalSection();
System.Threading.Monitor.Exit(synchronizationObject);
int helper(char* fName)
{
int status;
...
pthread_cond_init (&count_threshold_cv, NULL);
pthread_mutex_init(&count_mutex, NULL);
status = perform_operation();
if (status) {
printf("%s", "cannot perform operation");
return OPERATION_FAIL;
}
pthread_mutex_destroy(&count_mutex);
pthread_cond_destroy(&count_threshold_cv);
return OPERATION_SUCCESS;
}
CALL "CBL_GET_RECORD_LOCK"
USING file-handle
record-offset
record-length
reserved
END-CALL
IF return-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
PERFORM write-data
IF ws-status-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
DISPLAY "Success!"
END-IF
END-IF
CALL "CBL_FREE_RECORD_LOCK"
USING file-handle
record-offset
record-length
reserved
END-CALL
GOBACK
.
performOperationInCriticalSection()
の前にロックを確立しますが、このメソッドで例外が発生する場合にはロックを解放しません。
ReentrantLock myLock = new ReentrantLock ();
myLock.lock();
performOperationInCriticalSection();
myLock.unlock();
performOperationInCriticalSection()
の前にロックを確立しますが、決してそのロックを解放しません。
os_unfair_lock lock1 = OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(&lock1);
performOperationInCriticalSection();
performOperationInCriticalSection()
の前にロックを確立しますが、決してそのロックを解放しません。
let lock1 = OSAllocatedUnfairLock()
lock1.lock()
performOperationInCriticalSection();
Echo
という名前のクラスを定義します。このクラスは、コンソールで C 言語を使って入力されたコマンドをユーザーにそのまま伝える、ひとつのネイティブメソッドを宣言します。
class Echo {
public native void runEcho();
static {
System.loadLibrary("echo");
}
public static void main(String[] args) {
new Echo().runEcho();
}
}
Echo
クラスで実装されるネイティブメソッドを定義します。
#include <jni.h>
#include "Echo.h" //javah でコンパイルされたExample 1
の java クラス
#include <stdio.h>
JNIEXPORT void JNICALL
Java_Echo_runEcho(JNIEnv *env, jobject obj)
{
char buf[64];
gets(buf);
printf(buf);
}
gets()
を利用するため、Buffer Overflow に対して脆弱です。 Example 1
の脆弱性は、ネイティブメソッド実装のソースコード監査で簡単に検出することができます。これは C ソースコードを利用できるかどうかや、プロジェクトのビルド方法によっては実用的でなかったり可能でないことがありますが、多くの場合は十分です。しかし、Java とネイティブメソッドでオブジェクトを共有できる能力は、潜在的なリスクを油断できないほどに拡げます。Java の誤ったデータ取り扱いがネイティブコードの予測不能な脆弱性につながったり、ネイティブコードでの安全でない使用により Java のデータ構造が破壊されることがあります。Redirect
という名前のクラスを定義します。このクラスは 1 つのネイティブ JavaScript メソッドを宣言し、JavaScript を使用してドキュメントの場所を変更します。
import com.google.gwt.user.client.ui.UIObject;
class MyDiv {
...
public static void changeName(final UIObject object, final String name) {
changeName(object.getElement(), url);
}
public static native void changeName(final Element e, final String name) /*-{
$wnd.jQuery(e).html(name);
}-*/;
...
}
public
アクセスメソッドから private
配列変数を返しているため、モバイルコードに関するセキュアコーディングの原則に違反しています。 private
配列変数を public
アクセスメソッドから出力すると、コードのコールは配列のコンテンツを変更でき、実質的に配列に public
アクセスを許可し、private
としたプログラマの意図に反します。 public
から private
配列変数を返しています。
public final class urlTool extends Applet {
private URL[] urls;
public URL[] getURLs() {
return urls;
}
...
}
public class CustomerServiceApplet extends JApplet
{
public void paint(Graphics g)
{
...
conn = DriverManager.getConnection ("jdbc:mysql://db.example.com/customerDB", "csr", "p4ssw0rd");
...
package
レベルで元の外部クラスにアクセスできるピアクラスに変換する必要があります。知らない間に、Inner Class は括弧付きクラスで private
フィールドにアクセスできるため、Inner Class がバイトコードのピアクラスになると、コンパイラは Inner Class がアクセスできる private
フィールドを protected
フィールドに変換します。
public final class urlTool extends Applet {
private final class urlHelper {
...
}
...
}
finalize()
メソッドを public
と宣言しているため、モバイルコードに関するセキュアコーディングの原則に違反しています。finalize()
の実装内で super.finalize()
のコール以外に finalize を明示的にコールしません。finalize メソッドが public
アクセスで宣言されているため、モバイルコードの状況では、攻撃者が悪意のある方法でいずれかの finalize()
メソッドを呼び出す場合、エラーを発生させやすい手動のガーベジコレクションはセキュリティ上の脅威となります。finalize()
を本来の目的どおりに使用しているのであれば、protected
アクセス以外で finalize()
を宣言する理由はありません。public finalize()
を宣言しています。
public final class urlTool extends Applet {
public void finalize() {
...
}
...
}
public
、final
、および static
を宣言しているため、モバイルコードに関するセキュアコーディングの原則に違反しています。public
、final
、および static
を宣言する配列はバグです。配列は可変オブジェクトであるため、final
制約は配列オブジェクトが一度だけ割り当てられることを必要としますが、配列要素の値は保証しません。配列が公開されているので、悪意のあるプログラムは配列に保存された値を変更することができます。ほとんどの場合、配列は private
で作成されます。 public
、final
および static
を宣言しています。
public final class urlTool extends Applet {
public final static URL[] urls;
...
}
final
ではなく public
を宣言しているため、モバイルコードに関するセキュアコーディングの原則に違反しています。 public
メンバー変数は、攻撃者によるアプレットの操作または内部状態への不正アクセスを防止するために final
を宣言します。final
ではなく public
を宣言しています。
public final class urlTool extends Applet {
public URL url;
...
}