August 2009 - 文章

瀏覽器XSS防身術比武大會

在噗浪上看到有人抓到了總統府網站的XSS漏洞,在新聞稿網頁嵌入了惡搞的奇笨呆兒脫衣舞男影片。

無意發現,這個攻擊手法在IE8上會被擋下來! 之前微軟一再吹噓強調IE8相較於其他瀏覽器來得安全,索性就用這個案例讓五大瀏覽器比劃一下,看看IE8, Firefox, Chrome, Safari, Opera的XSS防身術功力高低:

Chrome腹部挨了一拳!

Firefox頭上被敲了一記悶棍!

Safari背上中了一掌!

Opera一個箭步,巧妙閃過!

 

IE8秀了一記後空翻,躲開後還三聲狂笑: "哈哈哈,打不到! 打不到!" (Internet Explorer has modified this page to help prevent cross-site scripting…)

實驗證明,Opera與IE8都在此案例中,均具有XSS防護效果,但不同的是Opera攔截後悶不吭聲,而IE8還多了一道資安警示。(我個人欣賞後者,但一般使用者未必領情)

在此宣佈,本次比武,IE8與Opera獲勝!

【題外碎碎唸】
總統府的網站用的是Linux+Apache+PHP平台,繼上回出現治國週記檔名被猜到而讓預錄檔案曝光後,這次的XSS漏洞算是第二記了! 常聽到有人說Windows平台比較容易被攻擊的論調,但我個人以為,看似相同的網站,開發者的經驗與素質左右了網站的安全防護強度,與用什麼平台、語言關係並不大。然而,網站防護力高低並不容易被明確衡量,往往只有因網站樹大招風被駭客高人盯上,一輪猛攻下問題才會爆發。

同一個網站專案,你不難找到號稱五萬包生子的兼職學生、也會有開口要價100萬的SI廠商,但客戶多半只願意花錢在看得到的地方,無法理解兩種網頁都是能選能點能翻頁,為什麼要為了"穩定性、系統效能、可維護性、安全性"多付數倍的錢? 系統品質與價值難以量化,導致專案市場價格混亂,最後苦了資深開發人員,說破了嘴也很難突顯自己的額外價值(說白一點,就是程式寫到老,想仗著經驗足牌子老多掙著錢都很難)。

程式老鳥們,大家自求多福吧!

【延伸閱讀】ASP.NET防駭指南你的網站在裸奔嗎?游擊式的SQL Injection攻擊

在Windows 2003上安裝Windows LIve Messenger 2009

七夕情人節的早上,工作平台是Windows 2003的同事紛紛發出哀嚎: MSN Messenger 8.1版在登入時出現已有新版軟體的警示,不裝新版不給登入;選擇安裝新版又會因不支援Windows 2003版本,不給安裝... 這,這不是要逼人走上絕路嗎?

之前我PO過文章介紹如何在Windows 2003安裝Windows Live Messenger 2008,本想如法炮製解救同胞,卻發現Windows Live Messenger 2009的安裝檔結構改了,找不到WindowLiveInstaller\MsiSources目錄。

在一台Windows 7 VM上裝妥Live Messenger 2009,但對於檔案改放到哪裡毫無頭緒。摸索了一下,無意中發現寶藏: C:\ProgramData\Microsoft\WLSetup\Logs下有個Log檔,從安裝歷程中挖出了msi檔的儲放位置--C:\Program Files\Common Files\Windows Live\.cache

另外,由Log發現Live Messenger 2009不像先前只要一個msi就搞定,而是拆出多個安裝模組,依序安裝:

    1. dw20shared.msi
    2. crt.msi
    3. contacts.msi
    4. wlc.msi
    5. richupload.msi
    6. wllogin.msi
    7. wlxsuite.msi
    8. choiceguard.msi
    9. messenger.msi

由.cache的各子目錄取出這些檔案,在一台Windows 2003上按照順序逐一裝好,Live Messenger 2009與Windows 2003就能"有情人終成眷屬"囉!

PS: 在噗浪上提到此事,噗友給了另一個簡便的解法: Pure MSN,不執著於官方版本程式的朋友也可以考慮看看。

【2009-08-27更新】網友小虫提供了用Resource Hacker修改安裝檔的做法,使安裝檔不會排斥Windows 2003/2008 Server,也是種不錯的做法。參考

漸層的魅力

上週五起,接連幾天都有很美的夕陽,雖然沒有絢爛的火燒雲大景,但由橘到藍的天空漸層,總能扣人心絃,讓我望到出了神。

無奈,"人、機、景永遠三缺一"的魔咒仍在。週六接獲線報,說天上大紅橘子高掛,興沖沖地拖著兒子衝上政大後山,但見橘子早被裝進了灰色棉布袋,滿頭大汗的父子二人組,只能看著天邊映出的幾抹淺淺的髒髒的暗橙自行遙想。

週日下午,空氣忽然變得乾淨、天空也頓時清晰起來。趁著家族聚餐前的丁點空檔,再一次,滿身大汗、氣喘吁吁地狂奔衝上政大後山...

這回,終於捕捉到我鍾愛的漸層藍~~~

很美吧!

Posted 26 August 2009 03:25 AMJeffrey | 4 comment(s)
Filed under:
Excel Tips - 找出重覆的資料儲存格

想將使用者給的Excel資料轉進DB,遇到Primary Key重覆的問題。

上千筆資料,到底是哪幾筆衝在一起? DB沒有指出來,只好自己找~~~

好奇別人遇到這類問題都怎麼解決,問了一下,原來很多人的做法都是先排序再用眼睛比對。但想想平日懶惰成性,我的火眼金睛早已退化,可能看到脫窗也找不出來。想要偷懶,避免摧殘我"不再青春的肉體",有兩條路可以走: 1) 寫VBA巨集處理 2) 用Excel內建函數輔助比對

平時我都選1),今天心血來潮,心想冰雪聰明的Excel,應該有其他簡便的做法。Google一下,我找到了用COUNTIF()函數協助標明重覆項目的做法。

方法是利用COUNTIF(比對起始Cell:目前Cell,目前Cell) > 1的原理,由目前Cell向前找,看該Cell的值在之前是否出現過? 若出現次數>1,就視為重覆。利用$A$2絕對座標決定比對範圍起點,A2為相對座標當成比對終點,同時也當作比對對象,一口氣在B欄遞增複製拉出各列的比對公式,重覆資料馬上現形! 就醬!

Posted 25 August 2009 05:22 PMJeffrey | 4 comment(s)
Filed under:
jQuery Plugin - 將欄位轉為JSON及還原

Abstract: This is a small plugin to covert all input(text, checkbox, radio), textarea, select values into a JSON string, and the values in JSON string can be restored to fields back latter.

專案裡有個小需求,要將<DIV>中的input(text, checkbox, radio), textarea, select轉成JSON字串傳回後端,稍後顯示時要能再還原回去。

jQuery本身提供可序列化整個Form為Query String的.serialize()函數,但我較想轉成JSON(因為在.NET端可以被還原),一時找不到現成的Plugin,心想不太難,就沒投資花時間去搜索,決定自己寫一個:

/* 
==== jQuery Plugin - Fields To Json Ver 0.5 ====
** by Jeffrey Lee, http://blog.darkthread.net
Revision:
Ver 0.5 Beta 2009-08-23 
 - Convert all input, select and textarea to a JSON string
 - Restore all input, select and textarea from a JSON string
*/
 
(function($) {
    $.fn.fieldsToJson = function(prefix) {
        if (!JSON || !JSON.stringify) {
            alert("JSON is required!");
            return "";
        }
        var obj = {};
        this.find("input,select,textarea")
        .each(function() {
            if (!this.id) return; //Skip no id attribute
            var fn = this.id;
            //Filtered by prefix
            if (prefix && fn.indexOf(prefix) == -1) return;
            //Remove prefix
            if (prefix) fn = fn.substr(prefix.length, fn.length - prefix.length);
            var val = this.value + ""; //Avoid IE8 JSON bug
            if (this.type == "checkbox" || this.type == "radio")
                val = this.checked;
            else if (this.type == "select-one")
                val = this.selectedIndex;
            else if (this.type == "select-multiple") {
                var selected = [];
                $(this).children().each(function(i) {
                    if (this.selected) selected.push(i);
                });
                val = selected.join(",");
            }
            obj[fn] = val;
        });
        alert(JSON.stringify(obj));
        return JSON.stringify(obj);
    }
    $.fn.jsonToFields = function(jsonString, prefix) {
        if (!JSON || !JSON.parse) {
            alert("JSON is required!");
            return "";
        }
        var obj = JSON.parse(jsonString);
        this.find("input,select,textarea")
        .each(function() {
            if (!this.id) return; //Skip no id attribute
            var fn = this.id;
            //Filtered by prefix
            if (prefix && fn.indexOf(prefix) == -1) return;
            //Remove prefix
            if (prefix) fn = fn.substr(prefix.length, fn.length - prefix.length);
            if (obj[fn] == undefined) return;
            var val = obj[fn];
            if (this.type == "checkbox" || this.type == "radio")
                this.checked = val;
            else if (this.type == "select-one")
                this.selectedIndex = val;
            else if (this.type == "select-multiple") {
                var selected = val.split(",");
                var options = $(this).children();
                options.filter(":selected").removeAttr("selected");
                for (var i = 0; i < selected.length; i++) {
                    var opt = options[selected[i]];
                    if (opt) opt.selected = true;
                }
            }
            else this.value = val;
        });
    }
})(jQuery);

應用時,要被轉換的欄位必須要有id屬性,指定或不指定id字首都可以。簡單測試程式如下:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script src="../js/json2.js" type="text/javascript"></script>
    <script src="../js/jquery-1.3.2.min.js" type="text/javascript"></script>
    <script src="../js/jquery.filedsToJson.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function() {
            $("#btnSave").click(function() {
                $("#taJson").val($("#dvBlock").fieldsToJson("f_"));
            });
            $("#btnRestore").click(function() {
                $("#dvBlock").jsonToFields($("#taJson").val(), "f_");
            });
        });
    </script>
</head>
<body>
<div id="dvBlock">
    <input type="text" id="f_Text" />
    <input type="radio" id="f_Radio" />
    <input type="checkbox" id="f_Checkbox" />
    <select id="f_SelectOne">
        <option>A</option><option>B</option><option>C</option>
    </select>
    <select id="f_SelectMultiple" size="3" multiple >
        <option>A</option><option>B</option><option>C</option>
    </select>
    <textarea id="f_TextArea" cols="20" rows="2"></textarea>
</div>
<input type="button" id="btnSave" value="SAVE" />
<input type="button" id="btnRestore" value="RESTORE" />
<div id="dvDisp">
<textarea id="taJson" cols="60" rows="4"></textarea>
</div>
</body>
</html>

original[1]

有興趣的朋友可以Copy回去玩,如果發現有蟲蟲,記得再回饋給我,感謝!

不厭其詳的IIS7安裝項目

挑燈夜戰安裝Windows 7之際,一心要把VS2008 + IIS的開發環境先搞起來。

基於資安意識的深化,"用不到的服務就不要安裝"的策略漸漸被融入到軟體安裝哲學裡,這樣做的好處多多,在網路上曝露面積愈小,被流彈打中的機率就愈低;另一方面,既然用不到,就沒必要浪費CPU、記憶體、HD去執行多餘的程式。

以Windows Vista/7為例,IIS預設是不安裝的,而且還細分成40餘項模組。這對一般使用者來說,可避免多安裝完全用不著的Web Server徒增風除。但對身為Web Developer的使用者來說,每裝好一次新機,"該有的"的網路服務必須手動選一下才會有。

睡意襲來,眼神矇矓神志不清地打開IIS安裝選項亂勾一通,卻跑出一個我幾乎不認得的IIS:

  • Javascript全壞了,仔細查,所有*.js請求都傳回空白內容。
  • 圖檔出不來,看了一下,傳回資料長度為零。
  • ASP.NET出錯時未顯示任何錯誤資料,也是一片空白。

研究了一下,才發現IIS安裝選項鉅細靡遺,其中有所謂的Static Content,沒勾就不能用來放js, jpg, html等靜態檔案、HTTP Errors未選在錯誤時就只會傳回空白... 項目真是細得可以。而正確的選取法應先勾群組(如: Common HTTP Features),它會帶出預設的常用細項,之後再額外增減調整,比較省事。

【延伸閱讀】

Posted 23 August 2009 05:56 AMJeffrey | 1 comment(s)
Filed under: ,
來自Yahoo Javascript大師的程式效能提示

James Padolsey的Blog看到這個Nicholad Zakas,Yahoo首頁的主要前端工程師、YUI作者之一、也是多本Javascript書籍的作者,前些時候在Google演講: "Speed Up Your JavaScript"(YouTube上有全程錄影)

身為Yahoo網站前端程式的苦主,Zakas的經驗分享肯定不是嘴砲,絕對是拳拳到肉的紮實功夫。James整理的五則筆記挺扼要的:

  1. 為Global Object建立Local Variable分身
  2. 為深層Property建立Local Variable分身
  3. 用Reverse Loop: while (i--) 取代 while (i < length)
  4. 直接指定完整HTML比逐層建立DOM物件快
  5. 指定CSS,不要個別改變Style內容

第4,5點還算較符合一般程式架構的原理,但1,2,3點若要落實,免不了會讓程式碼變得囉嗦與不直覺,理論上這些是Compiler或Runtime要代勞的骯髒活兒,但依目前Javascript語言與瀏覽器的進展來看,避免效能不彰的重任仍在Javascript Developer身上,而提升效能的手法通常不怎麼直覺,也多少會損及程式碼的簡潔性。換句話說,寫出高效能的Javascript需要花費不少功夫鑽研與實踐。總體而論,在維護規模較大、複雜度較高的網站系統時,全程使用Javascript會比採取.NET等語言耗費更多的心力與時間。

記得先前在噗浪看到幾則關於Javascript Framework的討論,有人問道,難道jQuery這類的解決方案都沒有黑暗面?

在我的看法裡,對jQuery的推崇源於它改變了Javascript Coding的哲學,節省了過去許多囉嗦、沒效率的程式寫法,但這些改良源於它與傳統寫法的對比,jQuery並沒有改變Javascript語言與執行平台的本質。而在做過幾個"純JS"的複雜專案後,我也漸漸體驗到Javascript的黑暗面多不勝數: Javascript不像.NET有完整的Class/Interface架構可資利用、缺乏Strong-Typed的Syntax檢查讓常打錯字的人痛不欲生(像我)、當程式龐大時缺乏VS這類華麗IDE對養尊處優慣的人十分殘忍(像我)、直譯式語言執行效能遠遠不及編譯式語言、未必能找到各式應用所需的Library(例如: 壓縮、編碼、數學運算、統計、圖形)...

但是,Javascript在絕大部分瀏覽器上不需任何安裝就可以順利執行的特點無可取代,在沒有外掛插件的情況下要達到順暢而直覺的網頁互動操作捨它其誰? 當你不得不考量使用Javascript解決問題時,jQuery是最佳的良伴。但若系統複雜到用Javascript寫會血流成河時,別執迷不悟地抱著它同歸於盡。

在IT職場闖蕩十餘年,漸漸有個體悟: 搞程式的人最好能像牆頭草,不同情境下誰有利就投靠誰,有奶便是娘(當然轉換語言平台也要成本,這也是"有利"與否的考量因素之一);不過寫個程式搞成宗教狂熱份子以身殉道,除了受人膜拜空有一身哀榮,對養家活口實在沒什麼幫助。(唉! 我真現實~~~)

關於水災的回憶

老家整修時找出了這件泛黃的蝴蝶標本手工藝品,今天老弟交到我手上。

那是我還是國中生時參加自然科學夏令營的粗糙創作,然而,這張標註73.6.2的蝴蝶標本護貝,在我的記憶裡,卻是與【淹水】連結在一起的!

記得整個活動有兩天,6月2日星期六先上了蝴蝶生態介紹課程,還發了一袋大小品種不一的十來隻蝴蝶標本,讓我們貼在紙上加護貝做成書籤,第二天則是令人興奮的活動-去大屯山捉蝴蝶!

6月3日星期天,一早看到大雨滂沱,心想活動泡湯了,失望之餘,附近鄰居奔相走告: "上水了~~~"

在幼時回憶裡,木柵極易淹水,未必要等到颱風來襲,往往雨勢大一點就有淹水的危險。腦海中不乏急搬家俱、涉水前進、水退清泥的畫面。而每逢淹水,也總會喚起母親兒時身歷八七水災的深刻回憶,年少的我只能似懂非懂地聽她訴說當年的驚恐…

老家地勢算高,幾次政大一帶淹水,有些地方淹到半層樓,水卻只到老家巷口上坡處,但六三水災來得又急又猛,連老家都淹了快半人高,低注漥處應該淹掉一整層樓了吧。

對自己模糊的記憶有些存疑,很幸運地在網路上找到實證:

(五)民國73年6月3日木柵地區突降豪雨,山洪爆發,洪水自四方湧進校園,校區一片汪洋,頓成澤國,校內低窪地區積水達3公尺。由於西南兩邊有堤防阻擋而抽水站抽水機又因淹水失效,宣洩困難,校區淹水長達10餘小時。水退後,到處淤泥厚積盈尺,教室、宿舍、辦公室底樓均無法使用,道路行走維艱。山上校區山坡地、環山道路邊坡坍塌10餘處,土方流失約5萬餘方,道路路基部份被沖毀。此次水災,本校損失至為慘重,行政院俞院長及教育部李部長皆親臨巡視。

很巧合地,今天再見到這張書籤,把這次的八八水災、年少時的六三水災與母親兒時的八七水災全部串連在一起~~~ 再加上岳母剛好提及捐款的事,原本就想盡點微薄心力,便不再遲疑,利用線上信用卡付款以兩個小朋友的名義捐了款。

八八水災是台灣歷史上僅次於八七水災的大型水患災難,如果你也有意透過捐款方式提供受災戶協助,以下是很方便的線上信用卡捐款管道: (參考來源)

Posted 16 August 2009 12:29 AMJeffrey | no comments
Filed under:
【日記】機車又擱淺了

七點半下班途中,在復興南路上等紅燈。綠燈亮起的當下,催了油門,車才剛起步就馬上失去動力... 幹得好,我又擱淺了。

油門把手變得毫無阻力,看來是油門線斷了。趕緊用腳把車划向路邊,機車沒有故障燈可以打,所幸跟在我後方的幾部機車跟汽車的車主都是紳士,很有風度地放慢速度,禮讓我靠岸,也沒按喇叭,多謝~~

剛好清粥小菜街旁就有停車格,把車划進停車格,確認了一下真是油門斷線,DIY修復是不可能了。我想起平時保養機車的車行有免費拖車服務,但手機裡沒記電話號碼! 此時才發現店家附送擋泥板的用心良苦... 難怪上回換輪胎時老板很熱心地主動幫我換了塊全新的。按擋泥板上的號碼打了"免費道路救援專線"(其實全車行就只有那支電話,哈),老板先詢問大概的故障狀況,我告知是油門線斷了,接著問了地點,就請我在原地稍等,他立刻派師傅出來載車回去。

適逢下班,路上車仍多,在路旁苦等了約莫半個小時,看到對面就有機車材料行(我記得簡單的零件材料行也可代裝),附近巷子也應找得到機車行,開始有些後悔是不是該就地找車行修,不用大老遠讓機車趕回娘家。(事後證明這個猶豫是多餘的)

不久,車行師傅開著小貨車到了,載著我與座騎返航。一路上跟師傅閒聊,說最遠曾開到淡水救車,但屬特例,不然來回四個小時,除非是大修,否則油錢+耗時肯定是賠錢生意。不過,老板也會先用電話過濾,先排除是沒汽油之類的烏龍(據說真有過車主堅稱有油,事後檢查沒油的案例,這種裝笑維的狀況,就要收運費),或是小故障則會請車主自行就近找車行修。

回到車行,開始更換油門線。我才發現換油門線是大工程,整台幾乎都快拆光了才能搞定,而前後耗了近一個小時。此時我很慶幸沒有隨便在路旁找車行,而是把車子交給我信任的維修師傅處理。既然車子都拆到幾近全裸,就順便連上回疑似半夜被人推倒而呈翹鬍子仁丹狀的右剎車拉桿也換新,兩百大洋(沒想到碟剎拉桿價格硬是比鼔剎多五十),加上剎車線六百大洋(依其換裝的複雜度,確有其價值),我的愛駒又再度變成一尾活龍...

【小常識】當油門線、剎車線變得緊繃,要花力氣才能驅動時,代表內部金屬線材已有氧化現象,宜請車行師傅評估是否乾脆預先更換,以免行駛途中斷裂徒增麻煩,還可能危害行車安全。話說,換完油門線後,我才發現原來全新的油門只要兩根手指頭就能轉動。

PS: 今天遇到一位爆胎先生身上沒帶錢,原本要押證件改天來付,但老板娘念其遠住汐止,往返耗時,就寫張欠條給了帳號請他直接轉帳,有空再來取回欠條,另外剛好遇到另一位太太在申請"修車期間代步車服務",這等CRM水準應該算是機車行界的翹楚吧~~~ 見到這一幕,我相信在場的每一位車主,應該都會變成忠實顧客,這就是做生意的學問吧!

Posted 15 August 2009 06:50 PMJeffrey | 5 comment(s)
Filed under:
【茶包射手日記】是誰射下了太陽?

【聲明: 這篇文章只想聚焦於網路傳播現象的個人感想,暫不考慮MSN太陽行動本質,亦不想衍生活動本身相關議題的討論,請見諒】

今天,我上了一堂網路傳播實習。

中午時刻,一向善良又熱心的老同事標題掛著小太陽,用MSN傳來一則訊息:

在msn暱稱前面 上小太陽標誌 (# ) *MSN就會捐 給颱風災後重建 ~~大家快加上小太陽吧 (不會出現的話...把空白移掉即可)

面對這種訊息,我的習慣是要先排除網路謠言的可能性,特別Google了一下,找到相關網頁以及新聞報導,不過由這些資料來看,MSN加上太陽是真,損款一事恐怕只是謠傳。不過我覺得大家齊力在MSN上建立起一片太陽海,象徵大家對這塊土地的認同以及對同胞的關懷,應該是件令人感動的事。跟同事澄清了一下無捐款的事實,我也掛起了小太陽,並且還在Plurk噗了一則鼓勵大家響應(當然,包含活動網頁以及新聞連結,凡事必須有足夠事證才能形成推論是茶包射手終生奉生的信仰)。

在此同時,我也收到同事寄了相關信件推廣太陽行動。看起來,訊息的傳遞是同時進行、四處擴散的。很快的,不到一個小時,我的MSN清單上出現一片太陽海,讓人好生感動,再一次感受到網路傳播力量的偉大!

下午3:30,早上傳訊通知我掛太陽的同事傳來另一則MSN:

快刪除 .( # )剛剛收 消息MSN顯示"太陽" 有人打 微軟詢 他們說沒有因此而捐錢 放太陽反而容易讓駭客找

餏我淺薄的資安知識,想不出所謂透過MSN標題鎖定受害者的神奇駭客手法,當下起疑,於是詢問有無佐證URL,同事表示是聽客戶得知,並無其他進一步資訊。即便我提出對駭客說的質疑,同事也認同,但太陽從同事的MSN標題上消失已成事實...

之後,在河道上,開始出現不少關於太陽駭客說的討論:

  • 有人聽聞掛太陽會被駭客鎖定,趕緊發噗通告親友
  • 有人吐露在親朋好友逼迫撤下了MSN上的太陽
  • 有人花了不少唇舌跟好心相勸的朋友說明駭客之事子虛烏有
  • 有人仍搞不清楚捐錢、駭客何者為真何者為假

到了下午,MSN的太陽海消失了大半,甚至有人MSN Title寫上"加太陽變被駭人"的字樣。

水能載舟亦能覆舟!

我相信很多人是衝著"掛太陽就損錢"的謠言而掛上小太陽,很多人也是因為"掛太陽會被駭"的謠言而緊急(甚至被迫)撤下MSN上的太陽。

如果網友們沒有輕信謠言,不會那麼快地聚成太陽海(舉手之勞形成實質幫助的動機畢竟強過純粹精神支持與祝福許多)...

如果網友們沒有輕信謠言,太陽海也不會瞬間消失(如果不是害怕掛上太陽會遭不測,我不覺得大部分的人會因為沒有對應捐錢就連精神上的祝福祈願也不願意給,至少我的同事是得知駭客才撤太陽的)...

想推廣的一方與想抵制的一方都充分地掌握了網路世代的特性,巧妙地運用人性達成了他們的目的。善良的網友們成了被玩弄的棋子,而真相在角落哭泣!

我寧可大家是認清了活動本質,認同其價值後才依自己的判斷付諸行動,也不要浮濫不知為何的盲目響應,即使如此熱度會減半。看到大家因為資安認知不足而衍生的莫名恐懼,則有幾份莫名的惋惜,過度而無謂的驚慌除了增加大眾對網路安全的不信任感,對實質的資訊安全並無助益。

我想說的是,既已有Google這等劃世代的知識整合工具存在,而由資訊源判別真偽亦不困難。當在網路上收到讓人存疑的訊息時,先花幾十秒做一下查證,再決定要不要轉寄出去,就能有效降低謠言在網路流竄的機會,網路世界將會更美好。

用我今天的另一則噗浪為太陽駭客風波寫下註腳:

據說在MSN加太陽會被駭客鎖定,另外我還聽說,只要能拔到獅子的鬃毛,掉落的頭髮就會長回來...

不要再相信沒有根據的說法了,大家拿出小學教育裡再三強調的"科學精神",做個網路世代的智者吧! 如果你是因為恐懼太陽駭客傳聞而撤下太陽,請讓太陽再升起來發光吧!

【後記】講到網路謠言,就不得不大推台灣版的謠言終結者--網路追追追,針對MSN太陽駭客事件已有完整的調查報告出來了。原來,"MSN暱稱加Blah就會引來駭客"是老梗!

MEMO-利用DataColumn.Expression自動計算百分比

【註: 沒營養的老人備忘筆記系列又來了,熱血青年請自行迴避。】

以下範例展示如何利用.NET DataColumn.Expression自動計算百分比: (適用: Mini C# Lab)

using System;
using System.IO;
using System.Threading;
using System.Data;
//REFDLL System.Data;System.Xml
public class CSharpLab
{
    public static void Test()
    {
        DataTable t = new DataTable();
        t.Columns.Add("CatgName", typeof(string));
        t.Columns.Add("Amount", typeof(int));
        t.Columns.Add("Percentage", typeof(float)).Expression
            = "Amount / IIF(Sum(Amount) > 0, Sum(Amount), 1)";
        t.Rows.Add("PC", 200);
        t.Rows.Add("NB", 300);
        t.Rows.Add("PDA", 500);
        foreach (DataRow r in t.Rows)
            Console.WriteLine("{0}: {1:P}", 
                r["CatgName"], r["Percentage"]);
    }
}

執行結果如下:

Built successfully in 254ms!
Prepare to run...
==================================================
PC: 20.00%
NB: 30.00%
PDA: 50.00%

==================================================
Execution time: 2 ms

【2009/08/13更新】Expression加入IIF判斷,防止SUM()==0會發生除以零的狀況。

90萬人次紀念

90萬人次到嚕~~~ 訂閱人數也達963新高,希望100萬時可以順便破千。

感謝大家長期的支持與愛護,未來還是要經常來光顧哦!

【成長歷程】

【插花生活雜記一則】

前幾天,兒子聽到電視在報日本石垣(ㄩㄢˊ)島的新聞,忽然喃喃自語道: "好奇怪哦~ 怎麼會有島的名字叫石垣? 難道島上有很多拾塊錢嗎?" 語畢,一屋子大人相視兩秒,接著哄然而笑...

只是我有一事不明,這... 這... 分明就是個不折不扣的冷笑話,要是出自我輩之口,肯定被人嫌冷兼罵無聊,若是慣犯還可能被拉到牆角餵磚頭。為什麼? 為什麼同樣一句話從五歲小孩嘴裡說出來,反而會得到"卡哇依"、"好聰明"之類的讚賞,這實在太不公平了!

Posted 12 August 2009 04:13 PMJeffrey | 5 comment(s)
Filed under:
Connection Pooling之SqlDataReader.Close()測試

早上貼了OracleConnection Pooling觀察文後,引發另一個疑問:

那麼沒呼叫SqlDataReader.Close()也會有相同問題嗎?

程式經過修改,就做出了SQL版:

public static void ShowSessionCount(string tag)
{
    using (SqlConnection cn = new SqlConnection(cnStrMonitor))
    {
        cn.Open();
        SqlCommand cmd = new SqlCommand(@"
select count(*) as c 
from sys.sysprocesses where loginame = 'testCnnUser'",
            cn);
        SqlDataReader dr = cmd.ExecuteReader();
        dr.Read();
        Console.WriteLine("[{1}] Session Count={0}", dr["c"], tag);
        dr.Close();
        cn.Close();
    }
}
 
public static void TestConnPool()
{
    using (SqlConnection cn = new SqlConnection(cnStrTester))
    {
        ShowSessionCount("Before Open");
        cn.Open();
        ShowSessionCount("After Open");
        SqlCommand cmd = new SqlCommand(
            "select getdate()", cn);
        SqlDataReader dr = cmd.ExecuteReader();
        dr.Read();
        Console.WriteLine(dr[0]);
        //dr.Close();
        SqlConnection.ClearPool(cn);
        ShowSessionCount("Before Close");
        cn.Close();
        ShowSessionCount("After Close");
    }
    ShowSessionCount("After Using");
    SqlConnection.ClearAllPools();
    ShowSessionCount("After ClearAllPools");
}

用我的工作機Windows 2008 + VS 2008 + SQL 2008進行測試,發現SqlDataReader.Close()不影響Connection的釋放。

[Before Open] Session Count=0
[After Open] Session Count=1
2009/08/12 上午 09:22:16
[Before Close] Session Count=1
[After Close] Session Count=0
[After Using] Session Count=0
[After ClearAllPools] Session Count=0

不過,DataReader用完就Close()絕對是好習慣,不要過度依賴系統的防呆機制,肯定是良好的開發態度。

如果有人有其他平台的測試結果,歡迎留言跟大家分享。

OracleConnection Pooling及OracleDataReader.Close()

依過去的經驗,Oracle資料庫重啟或連線中斷後,Connection Pool裡會存留一些無效連線,除了IISRESET或重啟程式外,似乎無法透過程式自行剔除Pool裡的連線。但我今天才發現,原來從.NET 2.0起,SqlConnection與OracleConnection早就新增了兩個Static Method可以用來解決類似問題: ClearPoolClearAllPools

寫了以下範例,cnStrMonitor與cnStrTester用了是不同的Username,於是我就可以透過統計v$session特定Username及機器名稱來觀察Oracle觀點看到的連線數。

public static void ShowSessionCount(string tag)
{
    using (OracleConnection cn = new OracleConnection(cnStrMonitor))
    {
        cn.Open();
        OracleCommand cmd = new OracleCommand(@"
select count(*) as c from v$session 
where username = 'schemaName' and machine='machineName'",
            cn);
        OracleDataReader dr = cmd.ExecuteReader();
        dr.Read();
        Console.WriteLine("[{1}] Session Count={0}", dr["c"], tag);
        dr.Close();
        cn.Close();
    }
}
 
public static void TestConnPool()
{
    using (OracleConnection cn = new OracleConnection(cnStrTester))
    {
        ShowSessionCount("Before Open");
        cn.Open();
        ShowSessionCount("After Open");
        OracleCommand cmd = new OracleCommand(
            "select sysdate from dual", cn);
        OracleDataReader dr = cmd.ExecuteReader();
        dr.Read();
        Console.WriteLine(dr[0]);
        dr.Close();
        //OracleConnection.ClearPool(cn);
        ShowSessionCount("Before Close");
        cn.Close();
        ShowSessionCount("After Close");
    }
    ShowSessionCount("After Using");
    OracleConnection.ClearAllPools();
    ShowSessionCount("After ClearAllPools");
}

首先跑第一個測試:

[Before Open] Session Count=0
[After Open] Session Count=1
2009/08/12 上午 04:55:23
[Before Close] Session Count=1
[After Close] Session Count=1
[After Using] Session Count=1
[After ClearAllPools] Session Count=0

如預期地,Connection Pool發生效果,cn.Close()並不會真的將Session關閉,直到using結束(cn.Dispose()),Session一直活著(存在於Pool中),直到ClearAllPools()為止。

接著我們將OracleConnection.ClearPool(cn)那一列的註解拿掉。依ClearPool的文件說明,cn會被先加上註記,在cn.Close()後連線就會被從Pool中清除。

[Before Open] Session Count=0
[After Open] Session Count=1
2009/08/12 上午 05:02:14
[Before Close] Session Count=1
[After Close] Session Count=0
[After Using] Session Count=0
[After ClearAllPools] Session Count=0

由測試結果來看,如同預期,cn.Close()後,Session數就降到零了。

最後這個測試很重要,我們將dr.Close()那一列註解掉,看看會發生什麼事?

[Before Open] Session Count=0
[After Open] Session Count=1
2009/08/12 上午 05:03:56
[Before Close] Session Count=1
[After Close] Session Count=1
[After Using] Session Count=1
[After ClearAllPools] Session Count=1

看到沒? 因為OracleDataReader沒有Close(),即便cn.Close()、cn.Dispose()、ClearAllPools(),連線仍然留存(也許已不在Pool中,但對Oracle來說,Session仍存在),直接Process關閉時才會消失。

這個實驗突顯了一個事實,看來Connection.Close()並非完整釋放連線的保證。因此切記要養成DataReader用完就Close()的習慣,不能光依賴Connection.Close()。如果不放心,用using包住DataReader應是不錯的做法。

若不想讓多餘的Oracle連線耗用不必要的資源,記得要OracleDataReader.Close()哦!

PS:我有點好奇這是By Design或是Bug,但在Windows 2003 + VS 2005 + Oracle Client 9.2、Windows 2008 + VS2008 + Oracle Client 10.2連線Oracle 9.2 DB都得到同樣結果,不像單一Oracle Client版本的問題,我想養成關閉DataReader的習慣有益無害。大家如果有其他版本的Oracle環境也幫忙測看看吧!

國小低年級組黏土作品七件

為了拍背包搬出腳架溜相機,順手就把女兒在夏令營的作品都拍起來。

 

 

 

大家喜歡哪一件呢?

Posted 03 August 2009 11:48 PMJeffrey | 5 comment(s)
Filed under:
更多文章 下一頁 »

搜尋

Go

<August 2009>
SunMonTueWedThuFriSat
2627282930311
2345678
9101112131415
16171819202122
23242526272829
303112345
 
RSS
【工商服務】
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


BlogLook Score and Rank

Syndication