110 公務人員升官等薦任 資訊處理 程式語言 考古題詳解
第 一 題35 分
第 5 代行動通訊標準(5G)的網路架構包含終端設備、接取網路、邊緣運算、核心網路、外部服務等,其系統程式的開發需要許多軟體安全開發機制。
(一) 為強化 5G 系統開發之安全性,可使用 SSDLC 和 DevSecOps 開發流程;請說明 DevSecOps 開發流程的要點。(15 分)
(二) 軟體安全開發機制,包含建立威脅模型與分析、設計威脅模型的緩解措施。請說明若威脅模型中有會話劫持(Session hijacking)攻擊、機敏資料洩漏、Cookie 操控(Manipulation),其緩解措施分別如何設計。(10 分)
二元樹(Binary tree)可使用陣列(Array)和鏈結串列(Linked list)設計。
(一) 以二元樹表達算術運算式 ,並寫出此運算式的後置表示法(Postfix Expression)。(10 分)
(二) 以一維陣列設計二元樹,節點循序編號,從陣列 1 開始表達根節點,節點編號規則為:左子樹是父節點編號乘以 2,右子樹是父節點編號乘以 2 加 1, 表示沒有子節點。請填寫以下陣列表達題目(一)算術運算式之二元樹。
| 索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 值 |
🖼️【此處有附圖,請對照原卷】
(三) 請根據下圖完成下面 C 程式實作(I)~(V)之鏈結串列的表示,其中 btree 為指向一個二元樹的指標變數。
____ node {
____________;
____________;
____________;
} treeNode_t;
typedef ____ tree_t;
tree_t btree;
登入後即可作答並保存紀錄。
(一)DevSecOps 開發流程要點
核心觀念: DevSecOps 是將資安工作融入 DevOps 的持續開發與交付流程,讓開發、維運與資安人員共同負責安全性。資安檢查從需求與設計階段開始,並在程式建置、測試、部署及營運期間持續執行。
解題方法: 可依軟體交付生命週期說明各階段的資安作法:
- 規劃與需求: 辨識系統資產、資料敏感度、使用者與可能的攻擊者,訂定安全需求及驗收標準。
- 設計: 執行威脅建模與架構檢視,確認信任邊界、權限控管、加密及錯誤處理等設計。
- 開發: 落實安全程式設計規範、程式碼審查與版本管理;保護密碼、金鑰等機敏資訊,避免寫入原始碼。
- 建置與測試: 在自動化流程中加入靜態程式碼分析、開源元件弱點檢查、動態測試及組態檢查;對高風險問題設定修正門檻。
- 發布與部署: 使用受控且可追溯的建置流程,檢查部署設定與映像檔,採最小權限原則,並確保發布內容可驗證。
- 營運與監控: 持續監控系統與安全事件,管理弱點及修補更新;將事件與測試結果回饋到下一輪開發。
DevSecOps 的重點是把安全檢查自動化並納入日常工作,及早發現問題、縮短修正時間,同時維持開發與部署流程的持續性。
解題技巧: 作答時依「規劃、設計、開發、測試、部署、監控」順序列出資安措施,再補充跨團隊合作、自動化與回饋機制,即能涵蓋流程要點。
(二)三種威脅的緩解措施
核心觀念: 威脅模型須將風險對應到可執行的控制措施;會話劫持關注身分驗證後的會話安全,機敏資料洩漏關注資料保護,Cookie 操控則關注用戶端資料的完整性與可信度。
- 會話劫持(Session hijacking):
- 全程使用 TLS,避免會話識別資訊在傳輸中被竊取。
- 設定 Cookie 的
Secure、HttpOnly、SameSite屬性,降低遭竊取或跨站濫用的風險。 - 使用不可預測的會話識別碼,並在登入、權限變更後重新產生識別碼。
- 設定閒置與絕對逾時;登出時使伺服器端的會話失效。
- 防止跨站指令碼等漏洞,並監控異常的會話使用情形。
- 機敏資料洩漏:
- 依資料敏感度分類,只收集及保存業務所需資料。
- 傳輸中使用加密,儲存時採加密或適當的雜湊方式;金鑰應妥善管理並與資料分離。
- 依最小權限原則授權,限制資料存取並保留稽核紀錄。
- 避免在日誌、錯誤訊息或除錯輸出中記錄密碼、金鑰及個人資料;顯示資料時採遮蔽處理。
- 設定保存期限,期滿後安全刪除不再需要的資料。
- Cookie 操控(Cookie Manipulation):
- 不信任用戶端 Cookie 中的角色、權限、價格等資料;每次重要操作均由伺服器端重新驗證。
- 必須使用 Cookie 儲存狀態時,採伺服器端保存狀態,或對 Cookie 內容加上伺服器端簽章或訊息驗證碼,並驗證其完整性。
- 對 Cookie 設定
Secure、HttpOnly、SameSite,並設定適當的有效期限。 - 對收到的欄位進行格式與範圍驗證,拒絕未授權的欄位變更。
解題技巧: 會話劫持的核心是保護會話識別碼及其生命週期;Cookie 操控的核心是不可直接信任用戶端資料;機敏資料保護則從減少資料、加密、控管存取與避免日誌外洩著手。
第二題
(一)運算式二元樹與後置表示法
第 三 題25 分
給定正整數二維陣列,起點為其中數值最大的點。從起點開始移動,求經過點的數值之加總。移動規則:
- 從相鄰的點(上、下、左、右)選擇一個最大的值移動。
- 走過的點不能重複。
請完成遞迴程式與非遞迴式(I)~(XI)空格,使以下 C 程式均能執行出下列結果。(22 分)
(一) 請比較遞迴與非遞迴程式記憶體空間使用狀況。(3 分)
#include <stdio.h>
#define maxN 30
const int NV = -1;
int max(int x, int y){
int r = x > y ? ____ : y;
return r;
}
int findR(int m[maxN][maxN], int x, int y, int sum){
int mi = max(max(m[x+1][y],m[x-1][y]),max(m[x][y+1],m[x][y-1]));
sum += m[x][y];
printf("%d, ", m[x][y]);
m[x][y] = NV;
if (mi == NV) return ____;
if (m[x+1][y] == mi) return findR(m, ____, sum);
if (m[x-1][y] == mi) return findR(m, ____, sum);
if (m[x][y+1] == mi) return findR(m, ____, sum);
if (m[x][y-1] == mi) return findR(m, ____, sum);
}
int findI(int m[maxN][maxN], int x, int y, int sum){
while(1){
int mi = max(max(m[x+1][y],m[x-1][y]),max(m[x][y+1],m[x][y-1]));
sum += m[x][y];
printf("%d, ", m[x][y]);
m[x][y] = NV;
if (mi == NV) ____;
else if (m[x+1][y] == mi) ____;
else if (m[x-1][y] == mi) ____;
else if (m[x][y+1] == mi) ____;
else ____;
}
return sum;
}
int main(){
int x=0, y=0, mi=NV, n=5, m=6;
int map[maxN][maxN] = {
{NV, NV, NV, NV, NV, NV, NV},
{NV, 11, 15, 23, 18, 31, NV},
{NV, 31, 68, 21, 88, 42, NV},
{NV, 19, 36, 52, 33, 28, NV},
{NV, 12, 55, 77, 66, 63, NV},
{NV, NV, NV, NV, NV, NV, NV}
};
// 邊界都是 NV
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
if (mi < map[i][j]){
mi = map[i][j];
x = i;
y = j;
}
}
}
printf("path= ");
printf("sum=%d\n", findR(map, x, y, 0));
return 0;
}
登入後即可作答並保存紀錄。
核心觀念
本題考查遞迴與迴圈的控制流程、二維陣列的相鄰位置存取,以及使用特殊值標記已走過的點。
程式以 NV = -1 表示邊界或已走過的點。每一步先比較目前位置的上、下、左、右四個值,選出最大值,再把目前位置標記為 NV,避免重複經過。當四個相鄰位置皆為 NV 時,表示無路可走,路徑結束。
max(x, y) 回傳兩個整數中較大的值:
int r = x > y ? x : y;
解題方法
遞迴程式空格
每次呼叫 findR,依序完成以下動作:
- 找出四個相鄰點中的最大值
mi。 - 將目前點的值加入
sum,並輸出目前點。 - 把目前點設為
NV,避免之後再次選到。 - 若
mi == NV,代表無路可走,回傳累計總和。 - 否則移動到值等於
mi的相鄰位置。
依題目程式的判斷順序,若有相同最大值,會優先選擇 x+1、x-1、y+1、y-1。
非遞迴程式空格
findI 以 while(1) 重複執行相同的選點與標記流程。找不到可走的位置時用 break 離開迴圈;其餘情況則更新目前座標。
完整填答
第 四 題25 分
敘述統計學上中位數和平均數均為數據資料的集中趨勢。中位數是將一組數值資料由小到大排列,最中間的數值為中位數。若有奇數個資料,則取最中間的數值為中位數,例如 的中位數是 ;若有偶數個資料,則取最中間兩個數值的平均為中位數,例如 的中位數是 。算術平均數是將一組數值加總,除以這組數值的個數,例如 的算術平均數 。
以下 C++ 程式輸出:。
(一) 請完成程式碼(I~V)使程式正常運作。(15 分)
01 #include <iostream>
02 #include <algorithm>
03 using namespace std;
04 class Compute{
05 public:
06 void setData(int *, int);
07 ____ = 0;
08 protected:
09 int *data, d_size;
10 };
11 class ComputeMedian: public Compute{
12 public:
13 double getNum();
14 };
15 class ComputeMean: public Compute{
16 public:
17 double getNum();
18 };
19 void Compute::setData(int *d, int s){
20 data = d;
21 d_size = s;
22 sort(data, data+d_size);
23 }
24 double ComputeMedian::getNum(){
25 if (____ == 1)
26 return data[d_size/2];
27 else
28 return (data[____] + data[1+d_size/2])/2.0;
29 }
30 double ComputeMean::getNum(){
31 double sum=0, avg=0;
32 for (int i=0; i<d_size; i++)
33 sum = sum + data[i];
34 return (____);
35 }
36 class Report{
37 public:
38 Report(int *d, int s){
39 data = d;
40 d_size = s;
41 }
42 void setCompute(Compute *c){ cp = c; }
43 void report(){
44 cp->____;
45 cout << cp->getNum() << ", ";
46 }
47 private:
48 int *data, d_size;
49 Compute *cp;
50 };
51 int main(){
52 int data[10] = {6, 7, 1, 21, 2, 3, 4, 3, 7};
53 Report r(data, 9);
54 Compute *cp1 = new ComputeMedian();
55 Compute *cp2 = new ComputeMean();
56 r.setCompute(cp1);
57 r.report();
58 r.setCompute(cp2);
59 r.report();
60 return 0;
61 }
(二) 請根據程式碼完成下面 UML 類別圖的關係連線,並說明此設計對模組耦合性(Coupling)的影響。
🖼️【此處有附圖,請對照原卷】
登入後即可作答並保存紀錄。
核心觀念
本題結合 C++ 抽象類別與多型、類別繼承,以及中位數和算術平均數的計算。
Compute 是共同介面:兩個子類別都要實作 getNum,分別計算中位數與平均數。Report 透過 Compute* 呼叫計算方法,因此執行時會依指標實際指向的物件,呼叫對應子類別的 getNum。
解題方法
(一)填入程式碼
| 空格 | 填入內容 | 說明 |
|---|---|---|
| I | virtual double getNum | 宣告純虛擬函式,令 Compute 成為抽象類別,並提供多型介面。 |
| II | d_size % 2 | 資料筆數為奇數時,餘數為 。 |
| III | d_size/2-1 | 偶數筆資料中,較中間的左側索引。 |
| IV | sum/d_size | 算術平均數為總和除以資料筆數。 |
| V | setData(data, d_size) | 將 Report 持有的資料及筆數交給計算物件。 |
| VI | getNum | 透過基底類別指標呼叫虛擬函式,執行對應子類別的計算。 |
完成後,相關程式如下:
class Compute{
public:
void setData(int *, int);
virtual double getNum = 0;
protected:
int *data, d_size;
};
double ComputeMedian::getNum{
if (d_size % 2 == 1)
return data[d_size/2];
else
return (data[d_size/2-1] + data[1+d_size/2])/2.0;
}
double ComputeMean::getNum{
double sum=0, avg=0;
for (int i=0; i<d_size; i++)
sum = sum + data[i];
return (sum/d_size);
}
void Report::report{
cp->setData(data, d_size);
cout << cp->getNum << ", ";
}