C 語言裡面的 ??! 符號

Hacker News Daily 上看到這個奇怪的知識:「What does the ??!??! operator do in C? (stackoverflow.com)」,原文在 Stack Overflow 上:「What does the ??!??! operator do in C?」。

這是 trigraph,在 C89 就有了,從 Rationale for International Standard—Programming Languages—C 這邊的 5.2.1.1 可以看到 trigraph 的歷史原因:

Trigraph sequences were introduced in C89 as alternate spellings of some characters to allow the implementation of C in character sets which do not provide a sufficient number of non-alphabetic graphics

而且是強制要求實做:

Implementations are required to support these alternate spellings, even if the character set in use is ASCII, in order to allow transportation of code from systems which must use the trigraphs. AMD1 also added digraphs (see §6.4.6 and §MSE.4).

其中遇到的問題就是當年得決定 C 可以用的 charset,得考慮到很多不同機器 charset 相容性的問題:

The C89 Committee faced a serious problem in trying to define a character set for C. Not all of the character sets in general use have the right number of characters, nor do they support the graphical symbols that C users expect to see. For instance, many character sets for languages other than English resemble ASCII except that codes used for graphic characters in ASCII are instead used for alphabetic characters or diacritical marks. C relies upon a richer set of graphic characters than most other programming languages, so the representation of programs in character sets other than ASCII is a greater problem than for most other programming languages.

然後就使用了 ISO/IEC 646 這個標準 (要記得 Unicode 1.0.0 是 1991 年才出現):

The solution is an internationally agreed-upon repertoire in terms of which an international representation of C can be defined. ISO has defined such a standard, ISO/IEC 646, which describes an invariant subset of ASCII.

The characters in the ASCII repertoire used by C and absent from the ISO/IEC 646 invariant repertoire are:

[ ] { } \ | ~ ^

後面就是定義 ?? 當作 escape digraph。

算是一個歷史產物,現在不太需要用到了...

找出非同溫層的書籍

前幾天在 Hacker News Daily 上看到有趣的服務:「Break the Bubble!」,這是由 A Book Like Foo 提供的服務,你輸入至少兩本你喜歡的書,然後他就丟一些非同溫層外的書籍出來...

我隨便丟了 1984 + TAOCP + CLRS 三本進去,推薦出來的書丟到 wikipedia 翻了一下,看起來的確都是平常不會想看的內容 XDDD

找時間問一下同事,看看這後面的演算法會怎麼玩...

離開 Vim 的方法

先說明最基本的方法是按個幾下 Esc 確保離開 insert mode,然後輸入 :q 然後 enter 就可以離開。

在「How to exit vim」這邊則是創意大賽,尤其是前面幾個方式:

:!ps axuw | grep vim | grep -v grep | awk '{print $2}' | xargs kill -9

想練功的可以看一下指令,然後用 man 看一下說明學一些東西 XD

寫了一個可以用 '/' 在 AWS 上快速切換服務的小工具

AWS Management Console 上切換服務需要用到滑鼠,而這個 Userscript 工具提供了 / 快速鍵可以直接拉出服務選單,另外也可以用 Esc 鍵關閉服務選單:「AWS Web Console Service Shortkeys」。

這個套件應該是支援多個瀏覽器,但是需要先安裝 Tampermonkey 這類可以跑 Userscript 的套件。

主要是常常在切的時候發現需要拿滑鼠,寫了這個 script 後多了一個方式可以用,而不需要把手移開鍵盤,會順手一些...

不過還是希望這個功能直接變成內建的 :o

cron 裡面的百分比符號 % 有特殊意義...

在試了一陣子後才發現的問題,crontab 裡的百分比符號 % 是特殊字元:「escaping double quotes and percent signs (%) in cron」。

取自 Debian 上的 crontab(5)

Percent-signs (%) in the command, unless escaped with backslash (\), will be changed into newline characters, and all data after the first % will be sent to the command as standard input.

有點奇怪就是了,因為有 echo 可以用才對?

Filter Input & Escape Output...

維基百科上有一篇「Secure input and output handling」說明要怎麼處理 input 與 output (對資安方面的說明)。標題的 Filter Input 與 Escape Output 是 Gasol 之前提到後才知道的名詞,以前只知道要 validate & escape,沒想過一個比較好記的念法...

這邊都以 PHP 為主,其他程式語言也應該會有對應的方式...

Input 有很多管道,有可能是使用者或是 3rd party 廠商透過 Form 傳進來的資料 (在 PHP 裡可能是 $_GET 或是 $_POST),也有可能是 cookie 的資料 (因為使用者可以修改,所以視為不安全的資料)。從檔案讀資料進來 (可能是普通的文字檔,或是 XML,也可能是圖片) 也算是 input。

Output 也有很多管道,像是 HTML、JSON,或是組 SQL statement 時使用變數。

Filter Input

在處理 input data 時,一般常常忘記的是「先強制轉成 Non-null UTF-8 string」(現在一般都是用 UTF-8,所以這邊就只講 UTF-8)。

這是因為很多 PHP function 對非 Non-null UTF-8 string 有非定義行為 (undefined behavior,不保證效果與輸出結果的正確性),加上一般常見的產品需求可以用 Non-null UTF-8 string 滿足,所以在 PHP 內拿到資料時可以先 filter 過。

Update:下面說的步驟錯了,請參考「關於 Non-null string 的處理...」這篇的說明。

有兩個步驟要做,第一個是確保他是 Non-null,直接把 \0 以及以後的東西幹掉:

$str_out = preg_replace('/\0.*/g', '', $str_in);
$str = preg_replace('/\0.*/g', '', $str); // 直接取代原來字串

第二個是確保他是 UTF-8 string。這點可以直接用 iconv() 轉,而不用自己寫 regex 處理了:

$str_out = iconv('UTF-8', 'UTF-8', $str_in);
$str = iconv('UTF-8', 'UTF-8', $str);

iconv() 從 UTF-8 轉到 UTF-8 是一個特別的用法,我是在 Cal Henderson 的「Building Scalable Web Sites: Building, Scaling, and Optimizing the Next Generation of Web Applications」上看到的 (有中譯版)。

如果傳進來的值本來就假設是整數,那麼就用 intval() 轉一次。如果假設是非負整數 (包含 0),那麼就用 abs()intval()。如果是文字類的 (像是 e-mail),可以再用其他的 regex 檢查。

一般來說,白名單會比黑名單好。不過這也是一般性,很多時候還是很囧的...

Escape Output

Escape 指的是 Escape character (轉義字元)。不同的情況下會有不同的 escape character,所以保護的方式也不一樣。

以 HTML 來說,想要顯示小於符號 <,實際上要用 &lt; 表示。而在 PHP 裡面常用的 htmlspecialchars() 定義了「只 escape 五個符號」,剛好可以拿來用:

<div><?= htmlspecialchars($str) ?></div>

也有人推薦 htmlentities(),不過因為轉的比較多 (所以要考慮的行為比較多),我比較不喜歡...

對於 XML,在 XML 的規範裡規定一定要把 <& 換成 &lt;&amp; (Character Data and Markup),但 > 則可以 (非必要) 換成 &gt;。也允許把雙引號 " 換成 &quot;,以及單引號 ' 換成 &apos;

所以 XML 剛好可以直接用 htmlspecialchars() (確認完全符合 spec 要求),反過來 htmlentities() 就不保證了。

再來是 MySQL 的 escape 與 charset 有關,所以要用 mysql_real_escape_string() 或是 PDO::quote(),但更好的方法應該是使用 prepare & execute (binding variables)...

而 shell 的 escape 則應該用 escapeshellarg() 再帶入 string 裡。

每一種 output 所需要的 escape function 不同,都不能直接拿 addslashes() 來用... (這個 function 的設計就很 PHP...)

純粹 JSON 的話,用 json_encode() 就可以幫你處理好。問題是在 HTML + JSON 時會讓你處理到抱頭痛哭... 這就是另外的故事了 +_+

結論?

Filter Input + Escape Output 只是最基本的一步,不過在大多數的狀況下,這個方法就可以擋下不少問題了,算是很實用的 policy...

PHP 5.4 的 json_encode 增加 JSON_UNESCAPED_SLASHES...

剛剛翻資料發現 json_encode 奇怪的老問題總算有解...

這樣的程式碼:

<?php
echo json_encode("http://www.google.com/"), "\n";

會輸出這樣的結果:

"http:\/\/www.google.com\/"

這是合法的 JSON 沒錯 (JSON 規格允許 string 裡面使用 \/),但看起來就很不爽啊,明明 / 就是可以合法輸出的字元... 然後剛剛看到 PHP 5.4.0 加上這些東西:

JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, and JSON_UNESCAPED_UNICODE options were added.

測了 JSON_UNESCAPED_SLASHES,看起來舒服多了:

<?php
echo json_encode("http://www.google.com/", JSON_UNESCAPED_SLASHES), "\n";

輸出結果是:

"http://www.google.com/"

網路安全歡樂記...

Twitter 上看到 John Resig 提到一串 reddit 上超歡樂的安全性議題:

討論串在「Creating a user from the web problem.」這邊。不僅是 escape 的問題,還有給予 web server 過大的權限...

貼圖也很好笑啊 XD

(噗)