2019年9月11日 星期三

[macOS] 修正 Python 錯誤訊息 (caused by URLError(SSLError(1, u'[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:590)'),))

查了一下,就是 openssl 版本不夠新。一開始還以為是自己用 MacPorts 維護,導致不是用系統 Python 的關係,但在追細一點就可以知道單純更新 openssl 就搞定了。

$ python -V
Python 2.7.16
$ /usr/bin/python -V
Python 2.7.10
$ /usr/bin/python -c "import json, urllib2; print json.load(urllib2.urlopen('https://www.howsmyssl.com/a/check'))['tls_version']"
TLS 1.2
$ python -c "import json, urllib2; print json.load(urllib2.urlopen('https://www.howsmyssl.com/a/check'))['tls_version']"
TLS 1.2

$ openssl version -a
OpenSSL 1.0.2s  28 May 2019
built on: reproducible build, date unspecified
platform: darwin64-x86_64-cc
options:  bn(64,64) rc4(ptr,int) des(idx,cisc,16,int) idea(int) blowfish(idx)
compiler: /usr/bin/clang -I. -I.. -I../include  -fPIC -fno-common -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk -arch x86_64 -O3 -DL_ENDIAN -Wall -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM -DECP_NISTZ256_ASM
OPENSSLDIR: "/opt/local/etc/openssl"


就好好把 openssl 更新即可修正。感謝佛心 www.howsmyssl.com 測試服務:https://www.howsmyssl.com/

另外也可以直接拿 openssl 上場問問 Google server:

$ openssl s_client -connect google.com:443 -tls1_2

若自己的 openssl 不支援 TLS 1.2 時,會直接回應 unknown option -tls1_2

2019年8月22日 星期四

[macOS] 使用 cURL command 上傳檔案且修改 Content-Disposition filename 欄位資訊

故事是為了檢驗 embedded linux 上,一些 CGI 資安問題。例如有隻 CGI 提供檔案上傳時,通常上傳完後就把檔案擺在指定位置。但擺放到指定位置時,若沒有寫好,直接拿 filename 去擺放時,就有機會做邪惡的事了!

而透過 cURL 上傳檔案,通常是用以下簡單的指定就完成:

$ curl -F "[email protected]/tmp/sample.txt" http://192.168.1.1/cgi-bin/upload.cgi

但如果細看表單內容物時,產出的結果:

POST /cgi-bin/upload.cgi HTTP/1.1
Host: 192.168.1.1
Content-Type: multipart/form-data
Content-Length: ##

----------########
Content-Disposition: form-data; name="file";
filename="sample.txt"

...


這時,有些 CGI 開發上會直接拿上述 filename 參數資訊來用,寫不好就會產生跳脫問題,如:

Content-Disposition: form-data; name="file";
filename="../etc/sample.txt"


若底層是直接用 sprintf(des_filepath, "/tmp/%s", filename); 就會有機會逃脫去覆蓋掉東西。幸運的是 embedded linux 大多會設定檔案系統是 Read-only file system 來保護 :P 若是用 PHP 時,大多都靠 basename 來保護了。

最後,補上靠 curl 串改 filename 的招數:

$ curl -F "[email protected]/tmp/sample.txt; filename=../etc/sample.txt" http://192.168.1.1/cgi-bin/upload.cgi

2019年8月7日 星期三

[開箱] 小米 A3 / Android One

小米A3

上一次買小米是小米2S的時代了 XD 用完小米2S就改用 iPhone SE 至今!

為何想買支 Android phone 呢?其實數個月來一直被 Pixel 3 / Pixel 3a 打中慾望,再加上手上的測試機 ASUS 表現不怎好,而左想右想自己也不會跳去改用 Android phone 了,就花 Pixel 3a 半個預算買隻接近 Android 原生手機,Android One 還可有兩年作業系統更新、三年月月安全更新的保證,雖說 Android 系統改朝換代時,硬體需求變化極快,有些手機硬體規格都撐不到兩年就不敷使用了。

關於 Android One 的故事,可以參考 WIKI:https://zh.wikipedia.org/wiki/Android_One

小米A3

而我自己下單前也有點遲疑,像是 NFC 功能無法測試 <囧> 而有的人是嫌棄他的 CPU 運算能力不夠新不夠強悍。目前使用的感覺非常良好!重溫 Android 中。

小米A3

Google Analytics 筆記 - 透過 gtag.js 回報 pageview 時,進行 Custom dimensions 回報

在 gtag 的自訂維度的文件上,是以 event 來教學的:gtagjs/custom-dims-mets 且 gtag 的 pageview 文件也是滿精簡:gtagjs/pages

遙想以前 ga 可以很直觀地靠 set 指令 處理:

ga('create', 'UA-XXXX-Y', 'auto');
// 設定指數 1 的自訂維度值。
ga('set', 'cd1', 'Level 1');
// 送出自訂維度值及瀏覽量匹配資料。
ga('send', 'pageview');


最後東摸摸西摸摸,找到一招,就是要發送自訂維度時,一口氣完成定義跟使用:

var params = {
'send_page_view': true,
'page_title' : title,
'page_path': path,
};

var custom_value = getSpecialInfo();
if (custom_value) {
params['ab_user'] = custom_value;
params['custom_map'] = {
'dimension1': 'ab_user',
};
}
gtag('config', 'UA-XXXX-Y', params);


而為何需要使用到這些,其實就是想多做更多的用戶分類,例如 A/B Test 時,更細膩的追蹤用戶行為。若採用 Google Analytics 實驗功能,可以直接靠 實驗/實驗ID 來區分。另外 Google 實驗在 2019/08/07 起已經移師到 Google 最佳化工具囉。只是使用 optimize.google.com 服務時,有些使用情境不太適合,像是網頁一初始畫面就要依照 A/B Test 分群做 UI 改變時,設計上只能先等 optimize.google.com js sdk 初始化才能做事,這就會稍微給人體驗不佳。如果操作上跟網頁初始化無關,如用戶點擊按鈕才依照 A/B Test 分群做不一樣的效果,那就還滿適合善用 optimize.google.com 提供的功能。

目前單靠 google analytics 自訂維度,再靠土炮 A/B 分群方式,把用戶標籤記錄在 cookie 使用:

// AB Test
function getRandomValue(min,max){
return Math.floor(Math.random()*(max-min+1))+min;
}

if (!getCookie(ab_test_cookie_name)) {
var user_flag = getRandom(1,10) > 5 ? '201908-a' : '201908-b';
setCookie(ab_test_cookie_name, user_flag, 365, '/');
}


後續在 google analytics 專案內的行為流程中,就可以在"新增區隔",從 "進階條件" 撈出自訂維度來看行為。

2019年7月24日 星期三

[C] 透過 CMAKE 搜尋 OpenSSL 函式庫產出 MD5 資料

最近幫同事 debug embedded linux 問題,恰好需要用 MD5 來做 hash ,隨意找找就用 OpenSSL 函式庫就對了。再加上原先寫的小程式已經靠 CMAKE 維護了,就繼續整合使用

CMakeLists.txt:

$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project (MD5)

SET(SOURCE_DIR ${CMAKE_SOURCE_DIR}/src)

# macOS
# $ mkdir b ; cd b;
# $ cmake .. -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib
#
find_package(OpenSSL REQUIRED)
include_directories(${OPENSSL_INCLUDE_DIR})

ADD_EXECUTABLE(main
${SOURCE_DIR}/main.c
)
TARGET_LINK_LIBRARIES(main OpenSSL::SSL)


程式碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <openssl/md5.h>

const char * get_md5_string(const unsigned char *in, char *output) {
int i;
for(i = 0 ; i < MD5_DIGEST_LENGTH ; i++) {
sprintf(output + (i)*2, "%02x", in[i]);
}
output[MD5_DIGEST_LENGTH*2] = '\0';
return output;
}

int main(int argc, char *argv[]) {
unsigned char digest[MD5_DIGEST_LENGTH];
char readable_digest[MD5_DIGEST_LENGTH*2 + 1];

char *test = "HelloWorld";
char *input;

if (argc > 1)
input = argv[1];
else
input = test;

MD5_CTX context;
MD5_Init(&context);
MD5_Update(&context, input, strlen(input));
MD5_Final(digest, &context);

get_md5_string(digest, readable_digest);

printf("MD5: [%s]\n", readable_digest);

return 0;
}


編譯跟運行:

$ mkdir b
$ cd b ; çmake ..
$ make
$ ./main HelloWorld
MD5: [68e109f0f40ca72a15e05cc22786f8e6]
$ echo -ne "HelloWorld" | md5
68e109f0f40ca72a15e05cc22786f8e6