在 Java 開發中,確保資料加密的安全性至關重要。本文首先分析一個使用硬編碼金鑰的錯誤示範,該方法容易被破解,存在安全風險。接著,我們介紹如何利用 SHA-256 雜湊演算法生成更安全的加密金鑰,並以程式碼範例說明如何正確使用。此外,針對 Android 應用程式安全,我們提供最佳實務,包含避免在主執行緒進行耗時操作、驗證應用程式安裝來源,以及保護敏感資料避免外洩。最後,我們也示範如何移除不必要的功能,藉此縮小攻擊面,提升應用程式的安全性。
不足夠的加密實踐
以下是一個不夠安全的加密與解密範例:
public class EncryptionUtils {
private static final String KEY = "mySecretKey";
public static String encrypt(String data) {
try {
Key key = generateKey();
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedData = cipher.doFinal(data.getBytes());
return Base64.encodeToString(encryptedData, Base64.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String decrypt(String encryptedData) {
try {
Key key = generateKey();
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decodedData = Base64.decode(encryptedData, Base64.DEFAULT);
byte[] decryptedData = cipher.doFinal(decodedData);
return new String(decryptedData);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static Key generateKey() throws Exception {
return new SecretKeySpec(KEY.getBytes(), "AES");
}
}
這個範例使用了一個硬編碼的金鑰 (mySecretKey
) 來進行加密與解密。這種做法是不安全的,因為金鑰可以被輕易地猜測或破解。
安全的加密實踐
為了提高加密的安全性,我們可以使用更強大的金鑰生成方法。以下是一個改進的範例:
public class EncryptionUtils {
private static final String KEY_ALGORITHM = "AES";
private static final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS7Padding";
private SecretKeySpec secretKeySpec;
private IvParameterSpec ivParameterSpec;
public EncryptionUtils(String secretKey) {
try {
byte[] keyBytes = generateKeyBytes(secretKey);
secretKeySpec = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
ivParameterSpec = new IvParameterSpec(keyBytes);
} catch (Exception e) {
e.printStackTrace();
}
}
public String encrypt(String data) {
try {
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encryptedData = cipher.doFinal(data.getBytes());
return Base64.encodeToString(encryptedData, Base64.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String decrypt(String encryptedData) {
try {
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decodedData = Base64.decode(encryptedData, Base64.DEFAULT);
byte[] decryptedData = cipher.doFinal(decodedData);
return new String(decryptedData);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private byte[] generateKeyBytes(String secretKey) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(secretKey.getBytes());
return md.digest();
}
}
在這個改進的範例中,我們使用了一個更強大的金鑰生成方法,利用 SHA-256 雜湊演算法從秘密金鑰 (secretKey
) 中生成一個更安全的金鑰。這種做法可以提高加密的安全性。
安全程式設計實踐:避免不安全的加密和授權
在軟體開發中,安全性是非常重要的。兩個常見的安全問題是不安全的加密和授權。在這篇文章中,我們將探討如何避免這些問題並提供安全的程式設計實踐。
不安全的加密
在以下的非安全程式碼中,使用了一個硬編碼的加密金鑰,這是一種非常不安全的做法:
import java.security.Key;
import javax.crypto.spec.SecretKeySpec;
public class EncryptionUtils {
private static final String KEY = "my_secret_key";
public static Key generateKey() {
return new SecretKeySpec(KEY.getBytes(), "AES");
}
}
這種做法容易受到攻擊,因為金鑰是硬編碼的,如果攻擊者獲得了程式碼,就可以輕易地取得金鑰。
安全的加密
為了避免這個問題,我們可以使用一個安全的金鑰生成方法,例如使用一個安全的隨機數生成器生成金鑰。以下是安全的加密程式碼:
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
public class EncryptionUtils {
public static Key generateKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128, new SecureRandom());
return keyGen.generateKey();
}
}
在這個例子中,我們使用了 KeyGenerator
類別生成了一個安全的金鑰。
不安全的授權
在以下的非安全程式碼中,使用了一個簡單的字串比較來進行授權,這是一種非常不安全的做法:
public class AuthorizationUtils {
public boolean checkAdminAccess(String username, String password) {
if (username.equals("admin") && password.equals("password")) {
return true;
} else {
return false;
}
}
}
這種做法容易受到攻擊,因為攻擊者可以輕易地猜測或暴力破解帳號和密碼。
安全的授權
為了避免這個問題,我們可以使用一個安全的授權機制,例如使用一個安全的密碼儲存和強大的驗證協定。以下是安全的授權程式碼:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class AuthorizationUtils {
private static final String ADMIN_USERNAME = "admin";
private static final String ADMIN_PASSWORD = "password";
public boolean checkAdminAccess(String username, String password) {
// 使用安全的密碼儲存和強大的驗證協定
String storedPassword = getStoredPassword(username);
if (storedPassword!= null) {
String hashedPassword = hashPassword(password);
if (hashedPassword.equals(storedPassword)) {
return true;
}
}
return false;
}
private String getStoredPassword(String username) {
// 從安全的儲存中取得密碼
//...
}
private String hashPassword(String password) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashedBytes = md.digest(password.getBytes());
return bytesToHex(hashedBytes);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
private String bytesToHex(byte[] bytes) {
// 將byte陣列轉換為十六進位制字串
//...
}
}
在這個例子中,我們使用了 MessageDigest
類別來對密碼進行雜湊,並且使用了一個安全的儲存來儲存密碼。
Android 應用程式安全性最佳實踐
1. 避免在主 UI 執行緒中執行長時間運算
在 Android 中,主 UI 執行緒(Main Thread)負責更新使用者介面和處理使用者輸入。若在主 UI 執行緒中執行長時間運算,可能導致應用程式無回應或當機。為避免此問題,應使用背景執行緒(Background Thread)或其他並發機制來執行長時間運算。
非遵守範例
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
// 執行長時間運算
}
}
遵守範例
// 更新 UI 在主執行緒中
runOnUiThread(new Runnable() {
@Override
public void run() {
// 更新 UI
textView.setText("Operation completed");
}
});
2. 檢查應用程式安裝來源
為防止應用程式被未經授權安裝,應在 onCreate
方法中檢查安裝來源。若檢查失敗,應顯示錯誤訊息並離開應用程式。
非遵守範例
// 檢查應用程式安裝來源
boolean isAuthorizedSource = checkInstallationSource();
if (!isAuthorizedSource) {
// 顯示錯誤訊息,但不離開應用程式
textView.setText("Unauthorized app installation");
}
遵守範例
// 檢查應用程式安裝來源
boolean isAuthorizedSource = checkInstallationSource();
if (!isAuthorizedSource) {
// 顯示錯誤訊息並離開應用程式
textView.setText("Unauthorized app installation");
finishAffinity(); // 關閉所有活動並離開應用程式
return; // 防止進一步執行程式碼
}
3. 保護敏感資料
為防止反向工程和敏感資料外洩,應避免直接在螢幕上顯示敏感資料。相反,應顯示一般性訊息以避免暴露敏感資訊。
非遵守範例
// 執行敏感操作
String sensitiveData = performSensitiveOperation();
// 直接顯示敏感資料在螢幕上
textView.setText(sensitiveData);
遵守範例
// 執行敏感操作
String sensitiveData = performSensitiveOperation();
// 顯示一般性訊息以保護敏感資料
textView.setText("Sensitive data is protected");
移除不必要的功能以降低攻擊面
在開發應用程式時,為了確保安全性和效率,移除不必要的功能是非常重要的。這不僅能夠減少攻擊面的大小,也能夠簡化應用程式的維護和更新。
非遵守規範的程式碼
以下是非遵守規範的程式碼範例:
public class MainActivity extends AppCompatActivity {
private Button loginButton;
private Button adminButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loginButton = findViewById(R.id.loginButton);
adminButton = findViewById(R.id.adminButton);
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 執行登入功能
performLogin();
}
});
adminButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 執行管理功能
performAdminAction();
}
});
}
private void performLogin() {
// 登入功能
}
private void performAdminAction() {
// 管理功能
}
}
在這個範例中,adminButton
和其相關的管理功能是沒有必要的。如果應用程式不需要或不打算為一般使用者提供管理功能,這就會引入不必要的風險。它增加了攻擊面的大小,並且如果攻擊者獲得應用程式的控制權,就有可能進行未經授權的存取。
避免硬編碼動作
為了避免硬編碼動作,可以使用以下規則:
rules:
- id: hardcoded-actions
patterns:
- pattern: 'performLogin\(\)'
- pattern: 'performAdminAction\(\)'
message: "Hardcoded actions in onClick methods"
這些規則使用 Semgrep 來檢查是否有硬編碼的動作在 onClick
方法中。
使用 CodeQL 進行檢查
也可以使用 CodeQL 來檢查是否有不必要的功能:
import android
class MainActivity extends AnyFile {
MainActivity() {
exists(
MethodDeclaration m |
m.getEnclosingType().toString() = "MainActivity" and
m.getBody().getAStatement() instanceof MethodInvocation and
(
m.getBody().getAStatement().toString().indexOf("performLogin()") >= 0 or
m.getBody().getAStatement().toString().indexOf("performAdminAction()") >= 0
)
)
}
}
這個 CodeQL 查詢檢查是否有 performLogin()
或 performAdminAction()
方法被呼叫在 MainActivity
中。
合規程式碼
以下是合規的程式碼範例:
public class MainActivity extends AppCompatActivity {
private Button loginButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loginButton = findViewById(R.id.loginButton);
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 執行登入功能
performLogin();
}
});
}
private void performLogin() {
// 登入功能
}
}
在這個範例中,已經移除 adminButton
和其相關的管理功能。應用程式現在只關注登入功能,減少了攻擊面的大小,並且消除了不必要的功能。
從使用者經驗與安全風險的平衡角度來看,提升應用程式安全性是一個持續迭代的過程。本文探討了從加密方法、授權機制到程式碼安全的多個導向,並佐以實際案例說明如何避免常見的程式碼漏洞。分析顯示,硬編碼金鑰、簡單的字串比對授權以及不必要的程式碼功能都大幅增加了系統的脆弱性,並可能導致敏感資料洩露等嚴重後果。對於開發者而言,採用安全的隨機數生成金鑰、雜湊演算法保護密碼以及移除不必要的功能,都是提升應用程式安全性的關鍵步驟。展望未來,隨著攻擊手段日趨複雜,安全防護措施也需要不斷演進。玄貓認為,除了程式碼層面的安全強化外,更需建立一套涵蓋威脅建模、安全審查、滲透測試的完整安全開發生命週期,才能有效降低應用程式安全風險,保障使用者資料安全。