[C#] JEV 之後 Cloudflare 也有 Clef!實測 XSS 判斷與誤判修正

  • 17
  • 0

前陣子 JEV 這個 Decision Model 還蠻紅的,它跟一般 LLM 不太一樣,不是拿來聊天或產生長篇文字,而是針對問題直接做判斷,然後回傳機率
 

當然用法很多,就詳細去看其他大大的介紹文..

最近在翻 Cloudflare Workers AI 的模型清單,意外發現 Cloudflare 竟然也推出了類似的東西,叫做 Clef,而且 API 跟 JEV 的 System One 相容,而且免綁信用卡免費...

這種模型我覺得還蠻適合拿來做資料分類,像是客服工單、安全事件,或是 AI Agent 前面的任務分流,不需要每次都呼叫一般 LLM,再要求它按照指定格式回答

所以這次拿 XSS 來測試看看,這應該在實務上比較實用...

我準備五段不同的使用者輸入,有正常文字、一般 HTML,也有幾段常見的 XSS 測試字串。讓 Clef 逐筆判斷,最後把疑似 XSS 的機率整理出來,比較容易看出差異。

1. Clef 跟一般 LLM 有什麼不同?

Clef 是 Cloudflare 推出的 Decision Model,主要是針對指定的問題回傳判斷結果,而不是自由產生文字,反正我看起來就跟 JEV 差不多

目前提供三種問題類型:

Type功能
noulYes / No,回傳 Yes 的機率
choice從指定選項中做選擇
score根據指定等級進行評分

這次只用 noul,因為我只想知道一段輸入有沒有 XSS 特徵,並且取得機率。

例如:

<img src=x onerror=alert(1)>

這是常見的 XSS 測試字串,利用圖片載入失敗時的 onerror 事件嘗試執行 JavaScript。

我們只需要問 Clef,這段內容是不是具有疑似 XSS 執行特徵。

如果結果是 0.9883,就代表模型對這個問題給出 98.83% 的肯定機率,不需要再從一大段自然語言回答裡面解析資料。

但這個機率只是模型的判斷,不是網站真正被攻擊成功的機率..
 

2. Cloudflare API Token

如果之前已經申請過 Workers AI Token,可以直接沿用。

還沒有的話,可以從這裡進去:

Cloudflare Workers AI Dashboard

取得 Account ID,再建立具有 Workers AI 推論權限的 API Token
 


 

這次只是 Console 範例,為了方便測試,就直接把 Account ID 跟 Token 放在 var 裡面,記得實際狀況要抽成環境變數(血淚談)

3. 五組測試資料

這次使用五組固定字串:

TestPayload預期
Normal TextHello World正常
Normal HTML<b>Hello World</b>正常
Script Tag<script>alert(1)</script>疑似 XSS
Event Handler<img src=x onerror=alert(1)>疑似 XSS
JavaScript URL<a href="javascript:alert(1)">Click</a>疑似 XSS

 

後面三筆都是常見的 XSS 測試方式,不過這次不會讓它們在瀏覽器執行,只是當成字串送進 API,接下來就看看 Clef 是不是可以判斷出來

4. Program.cs 完整程式碼

使用 .NET 10,不需要額外安裝 NuGet 套件。

這次直接用 foreach 跑五組資料,每次呼叫 Clef 後取得 answers.xss.noul,最後集中輸出判斷機率、耗時與 Input Tokens

using System.Diagnostics;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

// 測試用,正式環境請改用環境變數或 Secret Manager
var accountId = "你的CloudflareAccountID";
var apiToken = "你的CloudflareAPIToken";

var endpoint = $"https://api.cloudflare.com/client/v4/accounts/{accountId}/ai/run/@cf/cloudflare/clef";

// 五組固定測試資料
var testCases = new (string Name, string Payload)[]
{
    ("Normal Text", "Hello World"),
    ("Normal HTML", "<b>Hello World</b>"),
    ("Script Tag", "<script>alert(1)</script>"),
    ("Event Handler", "<img src=x onerror=alert(1)>"),
    ("JavaScript URL", "<a href=\"javascript:alert(1)\">Click</a>")
};

using var client = new HttpClient
{
    Timeout = TimeSpan.FromSeconds(120)
};

client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", apiToken);

var results = new List<(string Name, double? Probability, long Ms, int InputTokens, string Status)>();

foreach (var test in testCases)
{
    Console.WriteLine($"\nTesting: {test.Name}");

    var request = new
    {
        model = "clef",
        state = new
        {
            source = "Website user input",
            input = test.Payload,
            context = "Untrusted text that may be rendered as HTML"
        },
        questions = new
        {
            xss = new
            {
                type = "noul",
                instructions =
                    "Does this input contain executable JavaScript, " +
                    "HTML event handlers, javascript: URLs, or other " +
                    "clear XSS execution patterns? " +
                    "Ordinary HTML tags without script execution " +
                    "must be classified as No."
            }
        }
    };

    var timer = Stopwatch.StartNew();

    try
    {
        using var response = await client.PostAsJsonAsync(endpoint, request);
        var json = await response.Content.ReadAsStringAsync();

        timer.Stop();

        if (!response.IsSuccessStatusCode)
        {
            Console.WriteLine($"HTTP {(int)response.StatusCode}: {json}");
            results.Add((test.Name, null, timer.ElapsedMilliseconds, 0, $"HTTP {(int)response.StatusCode}"));
            continue;
        }

        using var document = JsonDocument.Parse(json);
        var result = document.RootElement.GetProperty("result");

        // Clef 回傳的機率位於 result.answers.xss.noul
        var probability = result
            .GetProperty("answers")
            .GetProperty("xss")
            .GetProperty("noul")
            .GetDouble();

        var inputTokens = 0;
        if (result.TryGetProperty("usage", out var usage) &&
            usage.TryGetProperty("input_tokens", out var tokenElement))
        {
            inputTokens = tokenElement.GetInt32();
        }

        results.Add((test.Name, probability, timer.ElapsedMilliseconds, inputTokens, "OK"));

        Console.WriteLine($"XSS Probability: {probability:P2}");
        Console.WriteLine($"Input Tokens: {inputTokens}");
        Console.WriteLine($"Elapsed: {timer.ElapsedMilliseconds} ms");
    }
    catch (Exception ex)
    {
        timer.Stop();
        Console.WriteLine($"Error: {ex.Message}");
        results.Add((test.Name, null, timer.ElapsedMilliseconds, 0, "Error"));
    }
}

// 集中輸出五筆測試結果
Console.WriteLine("\n========== XSS TEST RESULTS ==========");
Console.WriteLine($"{"Test",-20} {"XSS Probability",18} {"Time",12} {"Tokens",8} {"Status",16}");
Console.WriteLine(new string('-', 82));

foreach (var item in results)
{
    var probability = item.Probability?.ToString("P2") ?? "N/A";
    Console.WriteLine($"{item.Name,-20} {probability,18} {item.Ms,8} ms {item.InputTokens,8} {item.Status,16}");
}

Console.WriteLine($"\nTotal Tests: {results.Count}");
Console.WriteLine($"Successful: {results.Count(x => x.Status == "OK")}");
Console.WriteLine($"Failed: {results.Count(x => x.Status != "OK")}");

/*
實際測試結果(第二輪,調整 instructions 後)

Testing: Normal Text
XSS Probability: 0.57%
Input Tokens: 195
Elapsed: 1221 ms

Testing: Normal HTML
XSS Probability: 0.89%
Input Tokens: 200
Elapsed: 431 ms

Testing: Script Tag
XSS Probability: 99.03%
Input Tokens: 201
Elapsed: 400 ms

Testing: Event Handler
XSS Probability: 98.83%
Input Tokens: 204
Elapsed: 923 ms

Testing: JavaScript URL
XSS Probability: 98.41%
Input Tokens: 208
Elapsed: 1346 ms

========== XSS TEST RESULTS ==========
Test                    XSS Probability         Time   Tokens           Status
----------------------------------------------------------------------------------
Normal Text                       0.57%     1221 ms      195               OK
Normal HTML                       0.89%      431 ms      200               OK
Script Tag                       99.03%      400 ms      201               OK
Event Handler                    98.83%      923 ms      204               OK
JavaScript URL                   98.41%     1346 ms      208               OK

Total Tests: 5
Successful: 5
Failed: 0
*/

5. 執行與測試結果

原本我只問 Clef 這段輸入有沒有 Potential XSS Characteristics,結果連 <b>Hello World</b> 這種普通 HTML,都被判斷成 98.16% 疑似 XSS。

後來把 instructions 改得更明確,要求判斷有沒有 JavaScript、HTML Event Handler、javascript: URL 等執行特徵,同時告訴模型,一般 HTML 標籤不要直接當成 XSS。

重新跑一次,結果就正常多了。

TestXSS ProbabilityTimeTokens
Normal Text0.57%1221 ms195
Normal HTML0.89%431 ms200
Script Tag99.03%400 ms201
Event Handler98.83%923 ms204
JavaScript URL98.41%1346 ms208

 

五次 Request 全部成功,平均耗時大約 864ms,總共使用 1,008 Input Tokens。

這次 Normal HTML 從原本的 98.16% 降到 0.89%,另外三筆有 JavaScript 執行特徵的字串則都維持在 98% 以上

我覺得這次反而測出一個重點,就是 Decision Model 雖然回傳的是機率,但問題定義得不夠明確,一樣可能出現很離譜的結果

這五筆只能算是基本測試,還不能說 Clef 對 XSS 有多高的準確率,至少還需要更多正常 HTML 與各種編碼情境來驗證

畢竟 AI 有很多不可控性,很多時候真的就是當作參考,不過我個人是覺得因為它的速度跟價格+寧殺錯不放過 在一些專案考量下

我個人會參考採用

一些我的想法的看法

之前 JEV 比較吸引我的地方,就是它讓模型可以直接回傳決策機率,不需要生成一大段文字。

Cloudflare 也推出 Clef 後,對 C# 來說,直接透過 HttpClient 就能使用,確實蠻方便的。

這次五筆測試都成功,而且修改問題後,一般 HTML 的誤判也明顯改善。

後面如果要繼續測,可以把資料增加到幾百筆,再比較 Clef 對正常 HTML、XSS、SQL Injection 等不同輸入的分類結果。

或是拿它做 AI Agent 前面的任務分流,應該也蠻有意思。

先筆記到這邊。

參考資料

 

 

---

Before you read this, the article was in a superposition of human and AI. Upon observation, the wave function collapsed into a sleep-deprived developer.