@鄭昀匯總
關鍵詞: cookie poisoning
概述:
除了 session 外,一般不會在客戶端的 cookies 里保存過於重要的憑據,但電商應用有時候不可避免地存儲了一些敏感數據到客戶端,當然不希望被篡改。
目的:
讓服務器端能識別cookie值被篡改了。
手法:
set-cookie時加上防篡改驗證碼。
如:
user_name=alex|bj95ef23cc6daecc475de
防篡改驗證碼的生成規則可以很簡單:md5(cookieValue+key)或sha1(cookieValue+key),key可以是服務器端掌握的一個固定字符串,也可以很復雜(如后面的LTPA示例)。
服務器端得到客戶端送上來的cookie后,重新計算一下驗證碼,如一致,則未篡改。
示例2:IBM LTPA 的 cookie 簽名
Lightweight Third-Party Authentication (LTPA)是IBM Websphere和Domino產品中使用的單點登錄技術。
當服務器配置好LTPA認證方式,用戶通過瀏覽器成功登錄后,服務器會自動發送一個 session cookie 給瀏覽器;此 Cookie 中包含一個 LTPA Token。
一個有效的 LTPA Cookie 能夠在同一個認證域中被所有服務器自動認證。此 Cookie 中包含認證信息和時間戳。這些信息通過共享的 3DES Key 進行了 bis 加密。使用公共密鑰/私有密鑰進行簽名。
1)大致介紹:
LTPA Cookie 原始值 通過
3DES密鑰
使用 DESede/ECB/PKCS5P 進行加密。
此
3DES密鑰 也是采用 DESede/ECB/PKCS5P 進行加密,加密后再使用事先提供的
密鑰密碼 進行SHA-1 Hash,生成24個字節的密鑰,再進行Base64編碼。
如 Dmonio 里, LTPA Cookie 值為以下公式組成:
SHA-1=LTPA版本號+創建時間+過期時間+用戶名+Domino LTPA 密鑰
LTPA Cookie= Base64(LTPA版本號+創建時間+過期時間+用戶名+SHA-1)
如要解析 LTPA Token,先得使用
密鑰密碼,生成
3DES密鑰;再使用
3DES密鑰
解密 Token Cookie。也可以使用公共/私有密鑰來簽名或驗證LTPA Cookie。
2)WebSphere LTPA 生成原理
首先,這個 cookie 由以下部分組成,以%進行分隔:
- 用戶信息,格式為u:user\:<RealmName>/<UserDN>,如:u:user\:VGOLiveRealm/CN=squallzhong,O=VGOLive Technology
- 過期時間
- 簽名信息,如:
u: user\:VGOLiveRealm/CN=squallzhong,O=VGOLive Technology% 1301558320666% Cy2CAeru5kEElGj0hrvYsKW2ZVsvvcu6Un573aeX55OO4G3EMYWc0e/ZbqDp1z7MS+dLzniuUH4sYWCMpnKdm7ZGabwmV+WcraBl+y+yzwcl722gHVMOnDZAW7U3jEay9Tk2yG4yXkMWU+617xndpVxke2jtS5wIyVVM3q7UDPw=
3)WebSphere LTPA Cookie 的解析
以下代碼為解析從 WebSphere 或 Domino 發送過來的 LTPAToken Cookie。以Java為例:
01
…
02
// LTPA 3DES 密鑰
03
String ltpa3DESKey =
"7dH4i81YepbVe+gF9XVUzE4C1Ca5g6A4Q69OFobJV9g="
;
04
// LTPA 密鑰密碼
05
String ltpaPassword =
"Passw0rd"
;
06
try
{
07
// 第一步,獲得加密key
08
byte
[] secretKey = getSecretKey(ltpa3DESKey, ltpaPassword);
09
// 第二步,使用加密key解密ltpa Cookie
10
String ltpaPlaintext =
new
String(decryptLtpaToken(tokenCipher,
11
secretKey));
12
displayTokenData(ltpaPlaintext);
13
}
catch
(Exception e) {
14
System.out.println(
"Caught inner: "
+ e);
15
}
16
…
17
//獲得安全Key
18
private
static
byte
[] getSecretKey(String ltpa3DESKey, String password)
19
throws
Exception {
20
// 使用SHA獲得key密碼的hash值
21
MessageDigest md = MessageDigest.getInstance(
"SHA"
);
22
md.update(password.getBytes());
23
byte
[] hash3DES =
new
byte
[
24
];
24
System.arraycopy(md.digest(),
0
, hash3DES,
0
,
20
);
25
// 使用0替換后4個字節
26
Arrays.fill(hash3DES,
20
,
24
, (
byte
)
0
);
27
// BASE64解碼 ltpa3DESKey
28
byte
[] decode3DES = Base64.decodeBase64(ltpa3DESKey.getBytes());
29
// 使用key密碼hash值解密已Base64解碼的ltpa3DESKey
30
return
decrypt(decode3DES, hash3DES);
31
}
32
//解密LtpaToken
33
public
static
byte
[] decryptLtpaToken(String encryptedLtpaToken,
byte
[] key)
34
throws
Exception {
35
// Base64解碼LTPAToken
36
final
byte
[] ltpaByteArray = Base64.decodeBase64(encryptedLtpaToken
37
.getBytes());
38
// 使用key解密已Base64解碼的LTPAToken
39
return
decrypt(ltpaByteArray, key);
40
}
41
// DESede/ECB/PKC5Padding解方法
42
public
static
byte
[] decrypt(
byte
[] ciphertext,
byte
[] key)
43
throws
Exception {
44
final
Cipher cipher = Cipher.getInstance(
"DESede/ECB/PKCS5Padding"
);
45
final
KeySpec keySpec =
new
DESedeKeySpec(key);
46
final
Key secretKey = SecretKeyFactory.getInstance(
"TripleDES"
)
47
.generateSecret(keySpec);
48
cipher.init(Cipher.DECRYPT_MODE, secretKey);
49
return
cipher.doFinal(ciphertext);
50
}
51
…
解析出來的LTPAToken信息以%分隔。
參考資源:
1)hannover,
LTPA Cookie原理