1/29/2014

CAPTCHA

CAPTCHA 是 Completely Automated Public Turing test to tell Computers and Humans Apart 的縮寫,主要功能防止有人使用惡意程式大量送出網頁表單

網頁 CAPTCHA
最基本的 CAPTCHA 是利用程式產生一組數字或文字圖像,讓使用者辨識,並輸入圖像中看到的文字,連同網頁表單一起送出,Server 判斷使用者輸入的答案是否正確,後來發展出解計算題,選擇題,西洋棋遊戲等等,各式各樣不同的作法,在此我們就基本文字 CAPTCHA 的實作方式做討論,因 HTTP 協定是 Stateless,所以網頁在實作 CAPTCHA 面臨如何記憶 CAPTCHA 答案的問題,常見的實作方式有三種

將答案記憶在
  1. web session
  2. 實作方便,但如果使用的人多,當 session 量一大,會有拖慢 web server 速度的風險
  3. database
  4. 使用資料庫相較其他兩種方法,實作較為複雜,建置資料庫也需要額外的開銷
    實作資料庫 CAPTCHA 可參考 CodeIgniter
    首先建立 Table
    CREATE TABLE captcha (
        captcha_id bigint(13) unsigned NOT NULL auto_increment,
        captcha_time int(10) unsigned NOT NULL,
        ip_address varchar(16) default '0' NOT NULL,
        word varchar(20) NOT NULL,
        PRIMARY KEY `captcha_id` (`captcha_id`),
        KEY `word` (`word`)
    );
    
    當使用者送出表單時,先刪除舊有驗證碼資料,然後使用 ip_address 和 captcha_time 作條件檢查輸入的驗證碼資料是否正確
  5. cookie 或隱藏欄位中
  6. 此法網路上也有許多實作的範例可參考,大致上的做法是將答案加密後放在隱藏欄位或 cookie 中,當表單送出的同時將加密後的解答一併送出到 Server 端,Server 端將使用者的答案與解密後的解答做比對,這個作法安全性是三種裡頭最差的,因為只要人工辨識一次後,將辨識後的解答與加密後的密碼記憶下來,未來送出表單時只要填入這一組解答和加密後的密碼即可輕易破解

實作 CAPTCHA 小技巧
  • 使用 Data URI scheme,圖檔使用內嵌資料寫法,增加網頁內容檢驗的難度
  • 檢查使用者送出的 CAPTCHA Code 不可為空值,避免成為漏洞
  • 若使用者輸入錯誤,則重新產生新的 CAPTCHA 圖形
  • 隨機產生不同的圖形格式(JPEG,GIF,PNG),或使用 GIF 動畫也是增加破解難度的方式之一

網路上有提供一些免費的 CAPTCHA 服務,若不想自己寫,可考慮使用

實際上現今 CAPTCHA 阻擋惡意程式的效果相當有限,一些 OCR 可輕易的破解(PWNtcha - captcha decoder),只能說防君子不防小人了
1/21/2014
3:25:00 PM 0

Java Atomic Operation

定義
代表一種不可再分割的操作,在 multithreading 的環境中 Atomic Operation 是達成 thread-safe 的方法之一
至於為什麼會取名 Atomic 呢? 可能是過去原子被認定為物質的最小單位,無法再分割有關

舉例來說,當我們在 Java 中執行宣告 int i = 12 會配置 32 bits 的記憶體空間並將 12 這個值寫到記憶體區塊中,將整數 12 寫入記憶體這個操作是一個 Atomic Operation,不會只做一半就被其他操作中斷,而影響指派(assignment)值的正確性

使用硬體裝置支援的 Atomic Operation 可有效地提高程式效率, Intel Software Developer’s Manual 有說明哪些操作 Guaranteed Atomic Operations

Java 除了 double 和 long 這兩個資料型別以外,所有的 primitive types & reference types 讀取和寫入操作都是 Atomic Operation, 宣告為 volatile 的 double 和 long 讀取和寫入操作也視為 Atomic Operation

從 Java 1.5 開始,增加了package java.util.concurrent.atomic,可以在不使用 lock 的狀況下,達到 thread-safe 的目的

舉個例子
我們如果要寫個 thread-safe 的計數器,Java 1.4 的作法
public class Counter {
    private int count = 0;

    public synchronized int incrementCount() {
        return ++count;
    }
}
在 Java 中 ++count 這個操作,不是 atomic operation 因此是 non-thread-safe,做個細部分解,++count至少包含三個以上的動作
  1. 讀取count的值
  2. 將步驟1讀取出的值加1
  3. 將步驟2計算完成的值寫回變數count
從 x86 CPU 的角度來看,會使用三條指令來處理
  1. 從 memory 讀取 count 值到 register
  2. 對 register 的值加 1
  3. 把新值寫回 count 所在的 memory address
在多執行緒的環境下為了 thread-safe,所以必須使用 synchronized 關鍵字,synchronized 可達到 mutex & visibility 的目的

Java 1.5 後就可以使用 AtomicInteger 來替代 lock,相當於使用了 CPU 的 compare and swap 指令,在 x86 的架構中稱作 compare and exchange,使用 AtomicInteger 的另一項優點是 visibility,所以記憶體的值是一致的(memory consistency)
public class Counter {
    private final AtomicInteger count = new AtomicInteger(0);

    public int incrementCount() {
        return count.incrementAndGet();
    }
}
所有 java.util.concurrent.atomic package classes 都使用 Atomic 開頭, 包含以下 classes
  • AtomicBoolean
  • AtomicInteger
  • AtomicIntegerArray
  • AtomicIntegerFieldUpdater
  • AtomicLong
  • AtomicLongArray
  • AtomicLongFieldUpdater
  • AtomicMarkableReference
  • AtomicReference
  • AtomicReferenceArray
  • AtomicReferenceFieldUpdater
  • AtomicStampedReference
1/16/2014
7:58:00 PM 0

Java time zone data

今天發現 Java 的 MIT Time Zone 會因為版本的不同而有不同的結果
System.out.println("java version=" + System.getProperty("java.version"));
Date now = new Date();  
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");  
sdf.setTimeZone(TimeZone.getTimeZone("MIT"));  
System.out.println("MIT time=" + sdf.format(now));
以下為測試各版本JRE跑出來的結果
output
------------------------------------------------------------
java version=1.4.2
MIT time=2014-01-15 14:01:06.937-1100
------------------------------------------------------------
java version=1.5.0_22
MIT time=2014-01-15 14:01:26.025-1100
------------------------------------------------------------
java version=1.6.0_45
MIT time=2014-01-16 15:02:03.133+1400
------------------------------------------------------------
java version=1.7.0
MIT time=2014-01-15 14:02:14.400-1100
------------------------------------------------------------
java version=1.7.0_51
MIT time=2014-01-16 15:02:47.562+1400

在 1.4.2 版時 MIT 時區的 Raw Offset 是 GMT-1100,到了1.6.0_45 版時 MIT 時區的 Raw Offset 改成了 GMT+1400,而 1.7.0 版卻又變成了 GMT-1100, 看起來 Java 版本和時區是並沒有直接相關, 其實 Java 的時區資訊跟使用的 tz database 版本有關,關於 tz database 可參考 http://en.wikipedia.org/wiki/Tz_database

JRE 內部有一份屬於自己的 tz database,在 JRE 安裝目錄下的 lib/zi 目錄內記載著時區相關資料,因此時區資料不會因為OS的更新而改變,JRE 使用的 tzdata 版本資訊可參考 http://www.oracle.com/technetwork/java/javase/tzdata-versions-138805.html
至於如何得知JRE所用的 tzdata 版本,簡易的方法是使用文字編輯器打開JRE安裝目錄下的 lib/zi/ZoneInfoMappings 檔案,如下圖標註紅色的部分即是目前 tzdata 的版本
另外一個方法就是使用 TZupdater 來檢視和更新JRE的時區資訊
關於 TZupdater 用法可參考
http://www.oracle.com/technetwork/java/javase/timezones-137583.html
http://www.oracle.com/technetwork/java/javase/dst-faq-138158.html
http://www.symantec.com/business/support/index?page=content&id=TECH58669

印出所有時區 RawOffset 資訊
for (String id : TimeZone.getAvailableIDs())
{
    TimeZone tz = TimeZone.getTimeZone(id);
    System.out.println("ID:" + tz.getID() + "|Name:"+ tz.getDisplayName(Locale.ENGLISH) + "|RawOffset:" + tz.getRawOffset());
}
TimeZone class 也提供 inDaylightTime 來測試是否使用日光節約時間

MIT 是 Midway Islands Time 的縮寫, 屬於 West Samoa Time 時區,至於 Raw Offset 為什麼會改變,可能是政治或經濟因素而改變的吧
1/10/2014
3:06:00 PM 0

C# Class and Struct

1.Memory Allocation

Class => Reference Type
Struct => Value Type

new operator on a class => heap
instantiate a struct => stack

以上的差異造成傳遞資料到 method 中會有不同的結果

Class => a reference is passed
Struct => a copy of the struct is passed

以下引用 MSDN 的範例程式 http://msdn.microsoft.com/en-us/library/aa288471(v=vs.71).aspx
// struct2.cs
using System;

class TheClass
{
    public int x;
}

struct TheStruct
{
    public int x;
}

class TestClass
{
    public static void structtaker(TheStruct s)
    {
        s.x = 5;
    }
    public static void classtaker(TheClass c)
    {
        c.x = 5;
    }
    public static void Main()
    {
        TheStruct a = new TheStruct();
        TheClass b = new TheClass();
        a.x = 1;
        b.x = 1;
        structtaker(a);
        classtaker(b);
        Console.WriteLine("a.x = {0}", a.x);
        Console.WriteLine("b.x = {0}", b.x);
    }
}
Struct傳遞的是複本, 所以結果 a.x = 1, 而 b.x = 5

2.Constructors

Struct 不可使用無參數的constructor ,結構成員也不可設定初始值, 也不可使用 destructor

錯誤
public struct TheStruct1
{
    public TheStruct1()
    {      
    }
    public int x = 0;
}
正確
public struct TheStruct1
{
    public TheStruct1(int c)
    {
        x = c;
    }
    public int x;
}
TheStruct1 a = new TheStruct1();
Console.WriteLine("a.x = {0}", a.x);
TheStruct1 b = new TheStruct1(2);
Console.WriteLine("b.x = {0}", b.x);
TheStruct1 c = default(TheStruct1);//use default keyword
Console.WriteLine("c.x = {0}", c.x);
TheStruct1 d;
d.x = 99;//Initialize
Console.WriteLine("d.x = {0}", d.x);
當執行個體化結構且建構子未傳入值時,結構成員會自動初始化

執行結果
a.x = 0
b.x = 2
c.x = 0
d.x = 99

3.Inheritance

結構沒有繼承,但可實作介面

4.自訂結構在記憶體中的安排方式

使用 StructLayout(LayoutKind.Union) 和 FieldOffset

5.使用時機

struct被定義為輕量化的物件,與物件相比較 struct GC 的成本小,微軟建議在下面的狀況下可考慮使用struct

√ CONSIDER defining a struct instead of a class if instances of the type are small and commonly short-lived or are commonly embedded in other objects.
X AVOID defining a struct unless the type has all of the following characteristics:
  • It logically represents a single value, similar to primitive types (int, double, etc.).
  • It has an instance size under 16 bytes.
  • It is immutable.
  • It will not have to be boxed frequently.
引用自 http://msdn.microsoft.com/en-us/library/ms229017.aspx

參考
Structs  http://msdn.microsoft.com/en-us/library/saxz13w4.aspx
Classes and Structs  http://msdn.microsoft.com/en-us/library/ms173109.aspx
Class and struct differences  http://msdn.microsoft.com/en-us/library/aa664471(v=vs.71).aspx