113 司法特考三等 檢察事務官類科電子資訊組 程式語言 考古題詳解
第 一 題30 分
請回答下列問題:
(一) 請簡要說明何謂「SQL 資料隱碼攻擊(SQL injection)」。(10 分)
(二) 系統使用下列 SQL 陳述(SQL statement)查詢資料庫,以判斷是否為合法使用者。當該 SQL 陳述被執行時,會檢查輸入的 username 與 password 是否存在於資料庫中。如果執行該 SQL 陳述有回傳任何結果,則表示認證成功,反之認證失敗。
SELECT * FROM db_user WHERE username='<USERNAME>' AND password='<PASSWORD>'
假設惡意攻擊者可以輸入任意 <USERNAME> 或是 <PASSWORD> 的資料,請以一個例子說明該 SQL 陳述會產生 SQL injection 的問題。(10 分)
(三) 請舉出至少兩種常見預防「SQL injection 攻擊」的方法,並簡要說明。(10 分)
登入後即可作答並保存紀錄。
核心觀念
SQL 資料隱碼攻擊(SQL injection)是指系統把使用者輸入直接串接進 SQL 陳述,卻未妥善限制或處理輸入,使輸入內容被資料庫解讀成 SQL 語法的一部分。攻擊者因此可能改變原查詢的條件或結構,造成未授權登入、資料外洩、資料遭竄改或刪除等結果。
本題的登入查詢把輸入值放在字串引號內:
SELECT * FROM db_user
WHERE username='<USERNAME>' AND password='<PASSWORD>'
若輸入含有引號或邏輯運算子,直接串接就可能改變 WHERE 條件的意義。核心問題是把「資料」當成「指令」解讀。
解題方法
判斷是否有 SQL injection,可將攻擊者輸入代入原查詢,再檢視組合後的 SQL 是否改變了原本的邏輯。登入查詢原本要求帳號與密碼必須同時符合資料庫紀錄,也就是:
username = 輸入的帳號 AND password = 輸入的密碼
若輸入能使條件恆為真,或註解掉後續的密碼判斷,便可能在不知道正確密碼時仍取得查詢結果。
(一)SQL 資料隱碼攻擊的說明
SQL injection 是利用應用程式未安全處理使用者輸入的弱點,將惡意輸入注入 SQL 陳述,讓資料庫執行攻擊者預期的查詢或操作。它可能影響資料的機密性、完整性與可用性,例如讀取未授權資料、變更資料或刪除資料。
(二)攻擊例子
假設攻擊者輸入:
USERNAME:' OR '1'='1' --PASSWORD:任意內容
代入後,查詢約為:
SELECT * FROM db_user
WHERE username='' OR '1'='1' -- ' AND password='任意內容'
在常見支援 -- 單行註解的 SQL 語法中,-- 之後的文字會被當成註解,因此密碼判斷被忽略。前半段條件為:
username='' OR '1'='1'
第 二 題50 分
遞迴函式(Recursion)是程式語言常用的解題技巧之一,請回答下列問題:
(一) 請說明何謂程式語言的遞迴函式,撰寫遞迴函式需要哪些條件?(10 分)
(二) 請用迴圈(Iteration)以及遞迴分別撰寫計算第 個費氏數列的值。費氏數列由 和 開始,之後費氏數列的值就是由之前的兩數相加而得出。程式語言可使用 C/C++、Python 或是 Java 撰寫。(10 分)
(三) 請說明為何用遞迴函式撰寫第 個費氏數列的值,其執行的時間會比用迴圈來得多。(15 分)
(四) 請修改上述的費氏數列遞迴函式版本,使其可以加快執行的時間(接近迴圈的版本)。(15 分)
登入後即可作答並保存紀錄。
核心觀念
遞迴函式(Recursion)是指函式在執行過程中直接或間接呼叫自己,以較小的子問題逐步求解,再將子問題的結果合併成原問題的答案。費氏數列的定義為:
撰寫遞迴函式須具備兩項條件:
- 終止條件(Base case):明確處理不再呼叫自身的基本情況。
- 遞迴步驟(Recursive case):將問題化為規模更小的子問題,且每次呼叫都朝終止條件前進,否則會無限遞迴,直到發生堆疊溢位。
解題方法
(二)以迴圈及遞迴計算費氏數列
以下以 Python 撰寫,並約定 為非負整數。
**迴圈版本:**依序保存前兩個數,每次計算下一項。
def fib_iter(n):
if n < 0:
raise ValueError("n 必須是非負整數")
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
迴圈開始時,a、b 分別代表目前的 、。每輪更新後移至下一對數值;執行 輪後,a 即為 。例如 時,依序得到 ,回傳 。
**遞迴版本:**直接依費氏數列的遞迴定義拆解問題。
def fib_rec(n):
if n < 0:
raise ValueError("n 必須是非負整數")
if n <= 1:
return n
return fib_rec(n - 1) + fib_rec(n - 2)
當 或 時直接回傳;其餘情況以 計算。
執行時間比較
(三)直接遞迴為何比迴圈慢
直接遞迴會重複計算相同的子問題。例如求 時:
而計算 時又會計算 ;同一個 因而被重複求值。輸入愈大,這類重複呼叫愈多。
以 表示直接遞迴的執行時間,其遞迴關係約為:
第 三 題20 分
圖 1 是一個 Java 程式碼,其功能是計算標準輸入 個整數的平均值,輸出結果為小數點四捨五入到整數,其中 是一個命令列參數。其預期執行的結果如圖 2 所示。
圖 1:Java 程式碼
圖 2:預期執行方式與結果
(圖 1、圖 2 的原卷文字擷取不全,請參考原卷。)
(一) 這個程式依據題目說明以及預期呈現結果,會有三種錯誤,分別是語法錯誤(Syntax error)、執行錯誤(Run-time error)以及語意錯誤(Semantic error)。請分別指出是哪些行數指令造成上述三種錯誤、錯誤的原因以及修正之方法。(15 分)
(二) 請說明這個程式功能是否可以改為不使用陣列而節省記憶體空間的方法。若是,請說明如何修改程式;若否,請說明原因。(5 分)
登入後即可作答並保存紀錄。
核心觀念
本題考查 Java 中:
- 命令列參數
args的資料型態。 String與int的轉換。- 陣列索引與執行時錯誤。
- 整數除法與平均值四捨五入。
- 以累加方式取代陣列,降低記憶體使用量。
在 Java 中,args 的型態是 String[],因此 args[i] 都是字串,必須使用:
Integer.parseInt(args[i])
轉換為整數。
(一)三種錯誤、原因與修正
1. 語法/編譯錯誤:第 4 行
原程式:
int N = args[0];
args[0] 是 String,不能直接指定給 int 型態的變數,因此會產生編譯錯誤。
修正為:
int N = Integer.parseInt(args[0]);
2. 語法/編譯錯誤:第 7 行
原程式:
a[i] = args[i];
左側 a[i] 是 int,右側 args[i] 是 String,兩者型態不相容,會產生編譯錯誤。
此外,命令列參數中:
args[0]是- 真正的整數資料從
args[1]開始
因此修正為:
a[i] = Integer.parseInt(args[i + 1]);
完整寫法為:
for (int i = 0; i < N; i++)
a[i] = Integer.parseInt(args[i + 1]);
嚴格而言,這兩行屬於 Java 的編譯期型態錯誤,題目將其歸類為 Syntax error。
3. 執行錯誤:第 7 行或第 9 行
情況一:命令列參數不足
若輸入的整數個數少於 ,則執行:
args[i + 1]
時會超出 args 的有效索引範圍,產生:
ArrayIndexOutOfBoundsException
例如 ,卻只輸入三個整數。
修正方式是確保命令列至少提供 個參數,並可加入檢查:
if (args.length < N + 1)
return;
情況二:
原程式:
sum = a[0];
當 時,陣列沒有任何元素,存取 a[0] 會產生陣列索引越界錯誤。
修正方式是限制:
if (N <= 0)
return;
因此,合理的輸入條件為:
且命令列中必須確實提供 個整數。
4. 語意錯誤:第 13 行
原程式:
System.out.println("Mean: " + sum / N);
sum 與 N 都是 int,因此 sum / N 會進行整數除法,小數部分會直接被捨去,並非題目要求的四捨五入。
例如:
整數除法會得到 ,但四捨五入後應為 。
應先轉換為浮點數,再使用 Math.round: