OWASP ZAP 憑證安裝的方式(OWASP ZAP Certificate)

問題

透過 ZAP 去錄 https 時,如果沒有安裝憑證時,在 Browser 就會出現「 “Your connection is not private”, “你的連線並不安全”」的警告訊息,如下圖所示。
Chrome的警告

Firefox的警告

解法

為什麼會出現那個警告訊息呢? 因為 Browser 用的那個憑證已經不是原本連接的那個 host 的憑證,而變成了 OWASP Zed Attack Proxy Root CA 了哦! 而這個憑證並沒有被電腦 Trust 所以就會有那個警告訊息哦!
那要怎麼辦呢? 就是把憑證裝進去電腦的信任區。
以下我們就一步步來說明,

1.儲存 ZAP 憑證

開啟 OWASP ZAP ,Tools -> Options -> Dynamic SSL Certificates -> Save
儲存 ZAP 憑證

2.匯入 ZAP 憑證

DbClick剛才儲存的憑證檔,按下「Install Certificate…」,並將憑證存到「Trusted Root Certification Authorities」之中,如下,
Certificate Import Wizard

記得要將憑證存到「Trusted Root Certification Authorities」之中
存到「Trusted Root Certification Authorities」

最後按下 Finish 就可以了哦!
Import 完成

FIREFOX 匯入 ZAP 憑證

因為 firefox 憑證是自已管理的,所以 firefox 也要匯入 ZAP 憑證 ,如果會用 firefox 來錄的話,可以省略這步哦!
firefox 選項 -> 隱私權與安全性 (或在網址列輸入 about:preferences#privacy) -> 檢視憑證
隱私權與安全性

匯入 ZAP 憑證
匯入 ZAP 憑證

勾選「信任此憑證機構以識別網站」
勾選「信任此憑證機構以識別網站」

透過上述的方式,我們就可以錄 https 的網站,而不會一直出現警告訊息。 如果不會再錄的話,就請記得將 ZAP 的憑證移除哦!

透過 WinDbg 來找出 ASP.NET CPU 100% ASP.NET 程式的問題

問題

我們有一個 ASP.NET 的系統部署到 IIS 上(Windows 2012, .NET 4.x),有時候會導致 w3wp.exe 吃掉所有的 CPU 資源,一直要等到應用程式回收後,程式再重新啟動後就正常了。這種狀況不定期會發生。

解法

我們可以使用 WinDbg 來找出導致 IIS CPU 100% 的原因,方法如下,

  • 透過「工作管理員」來「建立傾印檔案」
    當發生 IIS 導致 CPU 100% 時,開啟「工作管理員」,按右鍵選取「建立傾印檔案」。要依 Web 應用程式的平台(x86/x64)來開啟「工作管理員」(x86的工作管理員在 C:\Windows\SysWOW64\taskmgr.exe )。
  • 安裝 WinDbg
    請參考 Debugging Tools for Windows (WinDbg, KD, CDB, NTSD) 來安裝對應的版本。
  • 設定 Symbol Path
    先建立一個 C:\RTX64_SYMBOLS 目錄(依您自行定義),然後開啟 Command 設定 _NT_SYMBOL_PATH 的環境變數,如下,

    set _NT_SYMBOL_PATH=srv*C:\RTX64_SYMBOLS*https://msdl.microsoft.com/download/symbols

    set _NT_SYMBOL_PATH所以在 command 中,輸入 set 後,就可以看到 _NT_SYMBOL_PATH 的設定值哦,

    _NT_SYMBOL_PATH當然,如果常常會用到的話,就直接設定到 環境變數之前,下次 debug 時,就不用再設定一次了哦! 詳細請參考Set up your system to use Microsoft’s public Symbol Server

  • 在 WinDbg 中找問題
    • 載入 sos.dll
      WinDbg 有 x86/x64 的版本,我是使用 x64 的版本,所以 sos.dll 的路徑是 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos.dll,您輸入如果發生錯誤的話,請使用 C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll 。
    • 設定 Symbol 檔目錄
      !sym noisy
      .cordll -ve -u -l
    • 透過 !runaway 顯示Threads所佔的時間
      輸入 !runaway 後,會顯示各 Thread 所花費的時間,花最多的會在最上面,如下圖,

      !runaway

      !runaway

    • 透過 ~[thread]s 切換到該 thread 的位置
      由上圖所示,Thread 40 佔最多時間,所以我們切到它的位罝去,

      ~40s

      ~40s

    • 透過 !clrstack 來查看呼叫堆疉
      !clrstack

      !clrstack從上圖可以發現,應該是有關 Dictionary 操作的問題,而它是由我們系統中 TemplateCfg.initial() 這個 Method 所引起的。

  • 檢視並調整程式碼
    開啟 TemplateCfg 程式碼,在 initial 這個 Method 之中會建立 Dictionary 物件,Clear 它,並 Insert 資料,但這些 Dictionary 的變數卻又設定成 static 。TemplateCfg.initial()即然這些 Dictionary 是 static 的,而且它們值又都是相同的,就沒有必要每次 request 時,就重新建立並 insert 這些資料。
    所以將 initial 裡面的 Code 搬到 static constructor 。
    調整完程式碼後,從去年觀察到目前,已經沒有再發生 CPU 100% 的狀況。

參考資料

Debugging in Production Part 1 – Analyzing 100% CPU Usage Using Windbg
WinDBG 應用實例:找出 ASP.NET CPU 100% 原因
Debugging Tools for Windows (WinDbg, KD, CDB, NTSD)
Set up your system to use Microsoft’s public Symbol Server
High CPU Hangs – 05
中小型研发团队架构实践:生产环境诊断利器WinDbg帮你快速分析异常情况Dump文件
Intro to WinDBG for .NET Developers
.NET Debugging Demos Lab 4: High CPU Hang – Review

無法載入檔案或組件 ‘log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=692fbea5521e1304’ 或其相依性的其中之一

最近同事詢問他們使用 Crytal Report Viewer 時,會發生 log4net 版本衝突的問題。如下圖所示,
CrystalReportViewer Runtime Error

如果是不同版本衝突的話,可以參考 如何讓不同 PublicKeyToken 的 DLL assemblyBinding 到可以用版本? 透過 assemblyBinding 的方式就可以解決。 但剛好我們的 AP 自已用的 log4net 版本也是 1.2.10.0,所以是 相同版本,不同的 PublickToken 的衝突。 詳細訊息如下,

沒有辦法解決 “log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821” 和 “log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=692fbea5521e1304” 之間的衝突。
任意選擇 “log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821”。

有查到 Error: Could not load log4net assembly 似乎是因為不同的平台(x86/x64) 所用的 log4net 會不同。
x86(32位元)用的 PublicToken 是 692fbea5521e1304,x64(64位元)或是 AnyCPU 用的 PublicToken 則是 1b44e1d426115821。
而我們 ap 平台是 x64 的,但 Crystal Report Viewer 卻使用了 x86(32位元) 的 log4net 。

嗯… 我想應該是那裡設定出了錯吧 …

跟同事討論後,同事又說只有在某台機器上才會出現那個錯誤,如果是透過 Browser 直接使用系統就正常,透過 VS.NET 啟動程式去 Debug 時,就會出現那個錯誤。
我想是因為如果透過 Browser 去使用系統的話,是連到 IIS ,而 IIS 設定的是 x64(64位元);如果透過 VS.NET 則是連到 IIS Express,預設它是使用 x86(32位元)。
於是筆者將 IIS 上的應用程式集區設定成 啟用 32 位元,再執行程式,則一樣會出現 log4net 的衝突狀況(  重現問題了 …^_^)。
所以就請同事在 VS.NET 中設定讓 IIS Express 使用 64 位元就沒問題了哦!
工具 -> 選項 -> (search textbox 中輸入 iis) , 並勾選 將 64 位元版本的 IIS Express 用於網站和專案(U) 選項勾起來就可以了哦! 如下,
將 64 位元版本的 IIS Express 用於網站和專案(U)

如果您是系統出了錯,請 Check 應用程式集區是否設定成了 「啟用32位元應用程式」哦!

分享 Call Language Understanding (LUIS) predictions api 的 3 種方式

在前一篇 「線上客服 + Chatbot = 智慧客服」我們透過 LUIS + Vital ESP(FAQ系統)  來打造智慧客服 Chatbot 。 在上線之前,需要先透過一堆問題去 Call LUIS  的 api ,來驗證我們在 LUIS  中設定的意圖及 Entities 是否正確。

即然是 Call LUIS  api 雲端服務,就會有費用問題,所以我們先來看一下 LUIS api 的價格說明,

luis.ai api 價格說明

  1. LUIS API – 免費: 每月最多可以 Call 10,000 次
  2. LUIS API – 基本: 每月最多可以 Call 10,000 次 (免費),超過後,每 Call 1,000 次約台幣 45.09 元
    詳細請參考 認知服務定價 – Language Understanding (LUIS)

目前我們可以在 LUIS 上手動測試意圖,但如果想要更有彈性的話,就要透過程式去呼叫它的 api ,所以以下我們就介紹 Call api 的 3 種方式,

Call luis.ai predictions api 的 3 種方式

在 LUIS predictions GET api 中有各語言的使用範例,以C#來看,使用非常方便,只要設定一些參數就可以取回 LUIS 解析的資料,本文將跟大家分享 Call 正式 api 的 GET/POST 方式,及 Call 測試 api 的方式,共 3 透方式。

  • call 正式 api 使用 GET,如下,
    static async Task<string> GetLuisResult(string reqString)
    {
    //最多是 500 個 ascii chars,中文我先取 230 個
    if (reqString.Length > 230)
    {
    reqString = reqString.Substring(0, 230);
    }
    var client = new HttpClient();
    var queryString = HttpUtility.ParseQueryString(string.Empty);
    const string appId = “你的appid”;
    const string subscriptionKey = “你的訂閱key”;
    // Request headers
    client.DefaultRequestHeaders.Add(“Ocp-Apim-Subscription-Key”, subscriptionKey);
    // Request parameters
    //如果回傳的內容中需要各 Intent 的話,請將 verbose 設定為 true
    queryString[“verbose”] = “true”;
    //queryString[“spellCheck”] = “{boolean}”;
    //queryString[“staging”] = “{boolean}”;
    //queryString[“bing-spell-check-subscription-key”] = “bing key”;
    //queryString[“log”] = “{boolean}”;
    var uri = $”https://eastasia.api.cognitive.microsoft.com/luis/v2.0/apps/{appId}?q=” + HttpUtility.UrlEncode(reqString)+ “&” + queryString;;
    var response = await client.GetAsync(uri);
    var result = await response.Content.ReadAsStringAsync();
    return result;
    }
  • call 正式 api 使用 POST (筆者試了老半天都試不出來,後來詢問 MS Herman 大大才知道,POST 的 body 前後要加 雙引號),如下,
    static async Task<string> GetLuisResultPost(string reqString)
    {
    //最多是 500 個 ascii chars,中文我先取 230 個
    if (reqString.Length > 230)
    {
    reqString = reqString.Substring(0, 230);
    }
    var client = new HttpClient();
    var queryString = HttpUtility.ParseQueryString(string.Empty);
    const string appId = “你的appid”;
    const string subscriptionKey = “你的訂閱key”;
    // Request headers
    client.DefaultRequestHeaders.Add(“Ocp-Apim-Subscription-Key”, subscriptionKey);
    // Request parameters
    //如果回傳的內容中需要各 Intent 的話,請將 verbose 設定為 true
    queryString[“verbose”] = “true”;
    //queryString[“spellCheck”] = “{boolean}”;
    //queryString[“staging”] = “{boolean}”;
    //queryString[“bing-spell-check-subscription-key”] = “bing key”;
    //queryString[“log”] = “{boolean}”;
    var uri = $”https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/{appId}?” + queryString;
    HttpResponseMessage response;
    // Request body,前後要加上 雙引號 哦!
    byte[] byteData = Encoding.UTF8.GetBytes(“\”” + reqString + “\””);
    using (var content = new ByteArrayContent(byteData))
    {
    response = await client.PostAsync(uri, content);
    var result = await response.Content.ReadAsStringAsync();
    return result;
    }
    }
  • 註: 在 LUIS 設定好,按下 Train 後,請記得要到 PUBLISH , 按下 「Publish to production slot」,這樣才會部到各區域去哦!

知道如何 Call Production 的 api 之後,如果我們想要像 線上客服 + BOT 之路 一文中,先用個 Excel 檔去試看看的話(目前 luis.ai 上 support 上傳 json , 整批測試),多試個幾次,免費的 quota 應該一下子就沒了吧! 如下所示,
超過免費可呼叫的Quota

那怎麼辦呢?

  • 1.到 Azure 那買個服務,然後再將訂閱的 Key 加到 luis.ai 之中,如下,從Azure買付費的方案
  • 2.使用 luis.ai 測試的 api ,您可以在測試時,錄一下 network ,如下,luis.ai 測試的 api

所以 Call luis.ai 測試 api 的方式如下,

static async Task<string> GetLuisResultTest(string reqString)
{
//最多是 500 個 ascii chars,中文我先取 230 個
if (reqString.Length > 230)
{
reqString = reqString.Substring(0, 230);
}
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
const string appId = “你的appid”;
const string subscriptionKey = “你的免費訂閱key”;
// Request headers
client.DefaultRequestHeaders.Add(“Ocp-Apim-Subscription-Key”, subscriptionKey);
var uri = $”https://westus.api.cognitive.microsoft.com/luis/webapi/v2.0/apps/{appId}/versions/0.1/predict?example=” + HttpUtility.UrlEncode(reqString);
var response = await client.GetAsync(uri);
var result = await response.Content.ReadAsStringAsync();
return result;
}

這個測試的 api 跟正式機的差別除了內容不太相同,它取出的 Entity 中間會有空白,所以再使用上需要將空白清掉哦! 因為它是測試用的,所以在整批 Call 的時候,有時會 block 一段時間,我的做法是如果出錯就停個 15 秒,讓它再重試一次,後來就可以正常再 Call 了哦!

正式 api 的結果如下 ( verbose 參數為 true, 內容才會有 intents ),

{
“query”: “下載下來可以用了,可是什麼是網址識別”,
“topScoringIntent”: {
“intent”: “FAQ”,
“score”: 1.0
},
“intents”: [
{
“intent”: “FAQ”,
“score”: 1.0
},
{
“intent”: “None”,
“score”: 0.05330094
}
],
“entities”: [
{
“entity”: “網址”,
“type”: “Keyword”,
“startIndex”: 14,
“endIndex”: 15,
“score”: 0.9081365
},
{
“entity”: “識別”,
“type”: “Keyword”,
“startIndex”: 16,
“endIndex”: 17,
“score”: 0.9102927
},
{
“entity”: “下載”,
“type”: “Action”,
“startIndex”: 0,
“endIndex”: 1,
“score”: 0.9999258
}
]
}

測試 api 的結果如下( entityPredictions 中的 phrase 字之間會有空白哦!!! ),

{
    "id": null,
    "text": "下載下來可以用了,可是什麼是網址識別",
    "alteredText": "下載下來可以用了,可是什麼是網址識別",
    "tokenizedText": [
        "下"
    ],
    "intentPredictions": [
        {
            "id": "9e70ed0a-561c-45f4-b8a4-2439de42acb1",
            "name": "FAQ",
            "score": 1.0
        },
        {
            "id": "8d898fa2-667f-471d-b87f-309011627b5d",
            "name": "None",
            "score": 0.05
        }
    ],
    "entityPredictions": [
        {
            "id": "cb403a63-347c-448c-9b08-3e48d6ac0c61",
            "entityName": "Keyword",
            "startTokenIndex": 14,
            "endTokenIndex": 15,
            "phrase": "網 址",
            "entityType": 1
        },
        {
            "id": "cb403a63-347c-448c-9b08-3e48d6ac0c61",
            "entityName": "Keyword",
            "startTokenIndex": 16,
            "endTokenIndex": 17,
            "phrase": "識 別",
            "entityType": 1
        },
        {
            "id": "1771cbab-fb7b-4a83-b46d-4c085ba88341",
            "entityName": "Action",
            "startTokenIndex": 0,
            "endTokenIndex": 1,
            "phrase": "下 載",
            "entityType": 1
        }
    ],
    "tokenMetadata": null
}

預設 call 正式 api 時,會 log 查詢的資料,最近幾筆資料可以從 「 Review endpoint utterances 」 看到,如下
Review endpoint utterances

那如果我需要的是全部的 Log 呢? 可以到「 My apps 」,在那個 app 右邊的 … ,選取「 Export Endpoint Log 」,就可以了哦,如下,
Export Endpoint Logs

線上客服 + Chatbot = 智慧客服

相信很多產品都會有線上客服系統及線上 FAQ 系統,提供給使用者線上詢問或查詢來排除操作系統所發生的問題。
如果加上 FAQ Chatbot 可以服務更多的使用者(當線上客服人員下班時),也可以讓線上客服人員可以服務復雜的問題。
我們要如何加上 FAQ Chatbot 呢? 讓我們繼續看下去 …

繼續閱讀 “線上客服 + Chatbot = 智慧客服”