2009/11/26

小論文格式

一.摘要
二.Abstract
三.前言
1.研究動機
四.使用者需求分析
五.研究與方法(文獻探討)
1.RFID技術
2.PKI技術
六.系統架構
七.系統評估
八.結論
九.參考文獻
-------------------------
大概這樣吧!要改再討論吧

2009/10/11

資通PKI應用競賽 時程表

活動網站:
http://csim.tca.org.tw/
http://www.ares.com.tw/pki_pk2009/a.php

1. 報名日期:98年10月1日(四)至11月19日(四) 下午六點截止
2. 初賽日期:98年11月23日(一)至11月27日(五)
3. 決賽日期:98年12月5日(六)
4. 競賽地點:台灣大學綜合體育館(台北市羅斯福路四段一號)

別忘了還有這個活動啊XD
---
報名還要錄製VCD,且邊操作邊說明,所以這系統一定要在期限內完成啊。

2009/10/04

One-Time Password

沒想像中的那麼容易,為了簡化文章,我直接將運作流程列出來:

  1. 先在「安全環境」(如你家電腦)設定產生OTP的各項參數: 密碼, Seed, 演算法, Count/Seq.,產生OTP密碼表格
  2. 到「非安全環境」(如網咖、學校)登入時,系統會要求你查找密碼表格的某一個密碼(例如第49個)
  3. 以查表法查出正確密碼,輸入後登入。如果不想查表,可以用 OTPGen/Mobile-OTP 等 Java程式,給予當初初始化的參數,算出表格再查表輸入

這是正統的 RFC2289 一次性密碼系統的流程,很明顯在我們的系統用這種東西根本不對。

因為教授說用預先定義好的內容作數位簽章,很容易被中間人攻擊,所以希望這個內容能夠隨機產生,最好是一次性的,這樣中間人攔截內容要重送時已經失效了。

不過正規的OTP系統,是寫死的流程,跟我們單純要求的隨機內容有點差距。所以又只能自己動手了。

因為我們要求的隨機密碼不會有讓使用者手動輸入的機會,得到後就立刻數位簽章送給伺服器認證,不必擔心太過複雜難以輸入的問題。我的想法是這樣的:

登入系統向伺服器要求(https)一次性隨機密碼

伺服器回傳(https),同時紀錄於 Session 內

登入系統收到密碼,簽章傳送給伺服器(https)

伺服器拿出使用者憑證跟 Session 紀錄的一次性隨機密碼,進行驗證。不論成功與否皆清除 Session 密碼

原先教授的疑慮是這樣的:當我用固定的內容(如: 「超爽的,撿到一百塊咧」或者是當天日期),系統送出給伺服器的過程中假如被攔截(假設 https 還會被攔截解密的話啦...),中間人可以進行重送攻擊 (因為簽章驗證會通過,驗證是驗證資料不是送的人)。如果是固定文字,那此攻擊可以不限時間成功;當天日期的話,當天重送攻擊都有效。所以教授才會說要將內容加到分鐘,甚至是秒,以避免這種攻擊。

改成隨機內容的話,由於使用者要登入都得先向伺服器要求產生一次性隨機密碼,這個密碼萬一真被中間人竊取成功,中間人並沒有私鑰可以進行簽章,所以攻擊失敗。要是在送出簽章的時候攔截實施重送攻擊,一來中間人並沒有要求伺服器發給一次性密碼,伺服器也就沒有用Session儲存,在進行檢查的時候就沒有東西可以用,而紀錄警告;二來這個一次性隨機密碼已經在剛才的正規使用者登入時,就已失效,所以還是沒有用。

如果中間人夠厲害,一次性隨機密碼、數位簽章都取得,再加上入侵伺服器將 Session 改成一次性隨機密碼,那就有機會。但是這需要攻擊 HTTPS, 伺服器,自有難度。Session 變造內容的攻擊法還沒有聽說過,因為 Session 存在伺服器除非入侵不然改不到。只有 Session ID 攻擊 (參見: Session Hijacking 攻擊與Cookie 欺騙(4/5) - 網路攻防戰):利用 Session ID 綁 Cookie 的設計,竊取正規使用者 Cookie,把自己的 Cookie 改的跟正規者一樣,Session ID自然一樣,對應的 Session 正是合法使用者的。

解決方式是每次要求就改變 Session ID,PHP 有函式 session_regenerate_id() 可以避免這種問題。

結論:根基於 HTTPS、一次性隨機密碼、數位簽章、Session 跟每次改變 Session ID的方式,讓中間人的破解難度增加,我想可以達到安全。

2009/09/29

09/29 開會結論

- C# <-> MySQL 加密連線傳輸使用法

pMA 開啟 MySQL SSL 連線
http://wiki.phpmyadmin.net/pma/Config/Servers#ssl

MySQL SSL 支援
http://www.option-c.com/xwiki/MySQL_Replication_with_SSL

好吧,C#部分不明,很多人在問但沒有一個好回答
http://dev.mysql.com/doc/refman/5.1/en/connector-net-programming-connection-options.html
官方是說連線字串加上 useSSL=true 就成,實際上沒那麼容易,一試就error。


- 數位簽章本文使用 OTP 方式建立,避免數位簽章被中間人攔截登入
- 闡明技術部份 (CA如何建立,如何管憑證、安全傳輸使用 ,etc)

還有,三本文件的催生。

最後右邊有個增加緊張感的自製 Widget,希望可以給大家一點推力XD

2009/09/12

設定 iptables - 防火牆規則設定

Ubuntu 預設的 iptables 設定是全部通行,防火牆如果全部通行那就等於沒設定,所以我們要對它設些規則。

目標:
  • 對內: 僅打開 SSH(22), HTTP(80), HTTPS(443), MYSQL(3306) PORT,其他全部封鎖
  • 對外: 不設限

這樣就可以防止有心人入侵其他 Port。

本來是要下一大堆指令的,不過 iptables 可以用 iptables-save 來輸出規則,所以我直接貼出規則檔:

# Generated by iptables-save v1.4.1.1 on Tue Aug 25 12:57:50 2009
*filter
:INPUT DROP [2858:374901]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [186:47949]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 3306 -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
COMMIT
# Completed on Tue Aug 25 12:57:50 2009


將此檔存到 /etc/iptables.up.rule。

因為每次開機後 iptables 的規則就會重置,要修改開機時能夠讀取剛才建立的規則檔,這樣我們建立的防火牆規則才有效。

用 vi 打開 /etc/network/interfaces,應該可以看到網路卡設定,加一行指令即可:


pre-up iptables-restore < /etc/iptables.up.rule


並存檔。這樣可以確保開機後自動回覆規則。

但是沒重新開機前,現在的 iptables 狀態還是空的,所以我們要手動載入規則檔,執行:

# iptables-restore < /etc/iptables.up.rule

就可以了,要查看是否成功的話可以下指令:

# iptables -L

應該會出現很多規則,這樣就成功了。

2009/08/31

resin-4.0 session的設定

我們
這在做管理介面時遇到鬼打牆
看session是啟用的
存活時間是30分鐘
但只要一換頁面session就會變空的
翻來翻去翻resin的說明

有這玩意兒可以來調session的設定
請參考這裡
前面講了一大推沒在用的我還去試他orz
最後試一試是只要設這個

<!-- enable persistent sessions -->

<session-config>
<save-mode>after-request</save-mode>
<use-persistent-store/>
</session-config>

<!--

增加save-mode這個child
並設定其質為after-request
一有request就存進去
解決這問題

(題外 管理介面有做一些小變更 包含登入驗證等頁面改變 可以去看看?)

2009/08/30

PKI Research #4.5 - Digital Signature 再臨

最近要實作檔案傳輸的部分,由於一開始作使用者登入時,驗證的部分是在本機端 (Client) 作的,伺服器端 (Server) 根本無法得知真實性。所以當有人聲稱某甲要求檔案或傳輸檔案過來時,我們也不能肯定他就是本人。因此,有必要在伺服器端再作一遍。

數位簽章的實作,其實在研究 #4 中已經有了,但是主要是驗證簽章的部分,要在伺服器中執行。搭配接收 POST 上傳檔案的設計,我採用 PHP 來實作。查了一下 PHP 也有 OpenSSL 套件支援 (API),所以我只要使用它提供的函式就可以驗證簽章,關鍵函式 openssl_verify。

openssl_verify 能接受一些參數 (原訊息、數位簽章、公鑰) 來驗證簽章正確性,其中簽章演算法只支援到 SHA1,沒有先前研究的 SHA256,只好把簽章的部分也改用 SHA1 以配合。公鑰的部分,得先從憑證裡抽出來,這有 openssl_get_publickey 可以使用。

大概像這樣:
<?php
$message = 'test'; // Original Message

try {
    $dbh = new PDO('mysql:dbname=FAST;host=127.0.0.1', 'user', 'pass');
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}

$sth = $dbh->prepare('SELECT `Public_Key` FROM XXX WHERE ID = ?');
$sth->bindParam(1, $tagID, PDO::PARAM_STR, 8);
$sth->execute();

$row = $sth->fetch();
$pubkeyid = openssl_get_publickey($row['Public_Key']);

// state whether signature is okay or not
$ok = openssl_verify($message, base64_decode($_POST['sigData']), $pubkeyid);
if ($ok == 1) {
    echo "good";
} elseif ($ok == 0) {
    echo "bad";
} else {
    echo "ugly, error checking signature";
}

// free the key from memory
openssl_free_key($pubkeyid);
?>

sigData 是數位簽章內容,為了傳輸方便已先行以 BASE64 編碼,所以這邊再解碼還原。

實際測試時,發現結果一直回傳 bad 即簽章不符,因為 C# 實作驗證都一切正常,所以我懷疑是不是 PHP 的問題,改換使用 JSP 來實作簽章驗證的部分,結果一樣失敗,不同的是 Java 跳出簽章結構錯誤的訊息,我開始懷疑起實作簽章的部分了。在 PHP 上使用 key 私鑰檔實作簽名,跑出來的東西跟 C# 的完全不一樣。C# 簽章驗證的結果,兩種結果都能通過驗證,不過 C# 的結果丟給 PHP 是失敗的,百思不得其解。

回頭想想,會不是會當初實作簽章的時候,想法錯了呢?還記得 .NET Framework 不許我們使用公鑰解密,取而代之的提供 Verify 方法,找看看 Bouncy Castle 有沒有提供類似的 Sign 方法,結果真的有。
public static byte[] signature(byte[] data){
try{
AsymmetricCipherKeyPair keyPair;
using(var reader = File.OpenText(@"D:\Programs\OpenSSL\user1.key")) { // Direct read pem format RSA private key file
keyPair = (AsymmetricCipherKeyPair) new PemReader(reader).ReadObject();
}

RsaDigestSigner rds = new RsaDigestSigner(new Sha1Digest());
rds.Init(true, keyPair.Private);
rds.BlockUpdate(data, 0, data.Length);
return rds.GenerateSignature();
}catch(Exception e){
throw new IOException("problem creating private key: " + e.ToString());
}
}

public static bool verify(byte[] message, byte[] sig){
var crt = new X509Certificate2(@"D:\Programs\OpenSSL\user1.crt"); // Read X509 Certificate file
var rsa = (RSACryptoServiceProvider) crt.PublicKey.Key; // Get public key

return rsa.VerifyData(message, "SHA1", sig);
}

Bouncy Castle 提供了 Org.BouncyCastle.Crypto.Signer 類別來處理數位簽章,RsaDigestSigner 就是給 RSA 公開金鑰演算法使用的數位簽章處理類別,由於 PHP 方的 Hash 演算法只支援到 SHA1,所以我簽章的部分也僅使用 SHA1。使用這個 Signer 類別不必自己手動 Hash 訊息,交給類別去作。

驗證簽章的部分跟上次基本上沒很大的差別,只是我也捨棄 VerifyHash 方法改使用 VerifyData 方法,直接把訊息 Hash 的動作交給了方法去處理,我完全不必實作相關 Hash 方法。

這次修改後,產生簽章的結果跟 PHP 的一模一樣,這樣就可以由 C#產生簽章,由 PHP 接收來驗證,進而確認使用者身份,伺服器端也可以利用 SESSION 暫時紀錄結果,就不必每次都要驗證。預設 30 分鐘 SESSION 會過期,到時候再驗證一次即可。