Bot Framework: Δημιουργήστε έναν Βοηθό Ασφαλείας AI με ευκολία

Πώς να δημιουργήσετε έξυπνα Bots με το Bot Framework

Bot Framework AI Security Assistant

Εισαγωγή

Σε μια εποχή όπου οι απειλές για την κυβερνοασφάλεια είναι μεγάλες, η ανάγκη για προσεκτικά και ανταποκρινόμενα μέτρα ασφαλείας δεν ήταν ποτέ μεγαλύτερη. Το Microsoft Bot Framework SDK, με τις ισχυρές του δυνατότητες AI, προσφέρει ένα νέο όριο στη διαχείριση της ασφάλειας. Αυτή η ανάρτηση ιστολογίου θα εμβαθύνει στην ανάπτυξη ενός τέτοιου βοηθού ασφαλείας AI, παρουσιάζοντας πώς να αξιοποιήσετε το SDK για την ερμηνεία των αρχείων καταγραφής ασφαλείας, τη δημιουργία ερωτημάτων KQL και την παροχή ειδοποιήσεων ασφαλείας σε πραγματικό χρόνο. Θα διερευνήσουμε πώς να ενσωματωθούμε με την υπάρχουσα υποδομή ασφαλείας και θα αξιοποιήσουμε τη δύναμη της τεχνητής νοημοσύνης για να δημιουργήσουμε τον δικό μας Βοηθό Ασφαλείας AI. Ελάτε μαζί μας καθώς εξερευνούμε αυτή τη συναρπαστική διασταύρωση τεχνητής νοημοσύνης και ασφάλειας στον κυβερνοχώρο, όπου τα έξυπνα bot φρουρούν ενάντια στο συνεχώς εξελισσόμενο τοπίο των ψηφιακών απειλών.

Setup

Πριν ξεκινήσουμε με το Bot Framework SDK, πρέπει να προετοιμάσουμε το περιβάλλον ανάπτυξής μας. Αυτή η ενότητα θα σας καθοδηγήσει στα απαραίτητα βήματα για να ρυθμίσετε τον "καμβά" σας και να ξεκινήσετε με τη δημιουργία του βοηθού γραφής που λειτουργεί με AI.

Προαπαιτούμενα:

  • Visual Studio: Βεβαιωθείτε ότι έχετε εγκαταστήσει το Visual Studio με το φόρτο εργασίας ανάπτυξης επιτραπέζιου υπολογιστή .NET. Μπορείτε να το κατεβάσετε από τον επίσημο ιστότοπο της Microsoft.
  • Συνδρομή Azure: Απαιτείται μια ενεργή συνδρομή Azure για πρόσβαση στο Copilot SDK και τις σχετικές υπηρεσίες του. Εάν δεν έχετε ήδη ένα, μπορείτε να εγγραφείτε για μια δωρεάν δοκιμή.
  • Εξομοιωτής Bot Framework: Αυτό το εργαλείο σάς επιτρέπει να δοκιμάσετε το bot σας τοπικά πριν το αναπτύξετε στο Azure. Κατεβάστε το από τον ιστότοπο Bot Framework.

Δημιουργία νέου έργου Bot:

  • Install the Bot Framework SDK: Open Visual Studio and create a new project. Choose the “Echo Bot (Bot Framework v4)” template. This template provides a basic bot structure to get you started quickly.
  • Install the required NuGet Packages:
    • Azure.AI.OpenAI
    • Azure.Core
    • Microsoft.Bot.Builder.Integration.AspNet.Core
  • Configure Your Bot: In the appsettings.json file, you’ll need to configure your bot with the appropriate API keys and endpoints for the Copilot service. You can obtain these credentials from your Azure portal.
{
  "MicrosoftAppType": "xxxxxx", // Leave it empty until publish
  "MicrosoftAppId": "xxxx", // Leave it empty until publish
  "MicrosoftAppPassword": "xxxx", // Leave it empty until publish
  "MicrosoftAppTenantId": "xxxxxx", // Leave it empty until publish
  "AzureOpenAI": {
    "ApiKey": "xxxxxxxxxx",
    "DeploymentName": "gpt-4o",
    "Endpoint": "https://xxxx.openai.azure.com"
  },
  "AzureSentinel": { // Log Analytics
    "ClientId": "xxxx",
    "ClientSecret": "xxxxx",
    "TenantId": "xxxx",
    "WorkspaceId": "xxxxx"
  }
}

When you open the Echo Bot we need to make changes to our code in order to achieve 3 things:

  • Azure OpenAI Chat Interaction and generic advice
  • KQL Query generation
  • KQL Query execution against a Sentinel Workspace \ Log Analytics

The main program is EchoBot.cs (you can rename as needed).

using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Azure.AI.OpenAI;
using Azure;
using System.Collections.Generic;
using System.Linq;
using System;
using System.Text.RegularExpressions;

namespace SecurityBot.Bots
{
    public class Security : ActivityHandler
    {
        private readonly HttpClient _httpClient;
        private readonly AzureOpenAIClient _azureClient;
        private readonly string _chatDeployment;
        private readonly IConfiguration _configuration;
        private Dictionary<string, int> eventMapping; // Declare eventMapping here

        public Security(IConfiguration configuration)
        {
            _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
            _httpClient = new HttpClient();

            // Load event mappings from JSON file
            string eventMappingPath = Path.Combine(AppContext.BaseDirectory, "eventMappings.json");
            if (File.Exists(eventMappingPath))
            {
                var json = File.ReadAllText(eventMappingPath);
                eventMapping = JsonConvert.DeserializeObject<Dictionary<string, int>>(json);
            }

            // Azure OpenAI Chat API configuration
            var endpoint = configuration["AzureOpenAI:Endpoint"];
            var apiKey = configuration["AzureOpenAI:ApiKey"];
            _chatDeployment = configuration["AzureOpenAI:DeploymentName"]; // Your Chat model deployment name

            // Initialize the Azure OpenAI client
            _azureClient = new AzureOpenAIClient(new Uri(endpoint), new AzureKeyCredential(apiKey));
        }

        protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            var userInput = turnContext.Activity.Text.ToLower();

            // Detect if the user wants to generate a query
            if (userInput.Contains("generate"))
            {
                // If the user says "generate", extract event and date, then generate the query
                var kqlQuery = await BuildKQLQueryFromInput(userInput, turnContext, cancellationToken);
                await turnContext.SendActivityAsync(MessageFactory.Text($"Generated KQL Query: {kqlQuery}"), cancellationToken);
            }
            else if (userInput.Contains("run"))
            {
                // If the user says "run", extract event and date, then run the query
                var kqlQuery = await BuildKQLQueryFromInput(userInput, turnContext, cancellationToken);
                var queryResult = await RunKqlQueryAsync(kqlQuery);
                await turnContext.SendActivityAsync(MessageFactory.Text($"KQL Query: {kqlQuery}\n\nResult: {queryResult}"), cancellationToken);
            }
            else
            {
                // For other inputs, handle the conversation with Azure OpenAI
                await GenerateChatResponseAsync(turnContext, userInput, cancellationToken);
            }
        }

        // Generate responses using the Azure OpenAI Chat API without streaming
        private async Task GenerateChatResponseAsync(ITurnContext<IMessageActivity> turnContext, string userInput, CancellationToken cancellationToken)
        {
            var chatClient = _azureClient.GetChatClient(_chatDeployment);

            // Set up the chat conversation context
            var chatMessages = new List<ChatMessage>
            {
                new SystemChatMessage("You are a cybersecurity assistant responding only to Security related questions. For irrelevant topics answer with 'Irrelevant'"),
                new UserChatMessage(userInput)
            };

            // Call the Azure OpenAI API to get the complete chat response
            var chatResponse = await chatClient.CompleteChatAsync(chatMessages);

            // Access the completion content properly
            var assistantMessage = chatResponse.Value.Content.FirstOrDefault()?.Text;

            if (!string.IsNullOrEmpty(assistantMessage))
            {
                // Send the entire response to the user at once
                await turnContext.SendActivityAsync(MessageFactory.Text(assistantMessage.ToString().Trim()), cancellationToken);
            }
            else
            {
                await turnContext.SendActivityAsync(MessageFactory.Text("I'm sorry, I couldn't process your request."), cancellationToken);
            }
        }

        // Build a KQL query from the user's input using Text Analytics
        private async Task<string> BuildKQLQueryFromInput(string input, ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            // Start with a base KQL query
            string kqlQuery = "SecurityEvent | where 1 == 1 ";

            // Use the eventMapping dictionary to map the user's input to an EventID
            var matchedEventId = eventMapping.FirstOrDefault(mapping => input.Contains(mapping.Key)).Value;

            if (matchedEventId != 0) // EventID was found
            {
                kqlQuery += $"| where EventID == {matchedEventId} ";
            }
            else
            {
                // Fallback if no matching EventID is found
                await turnContext.SendActivityAsync(MessageFactory.Text("Sorry, I couldn't find a matching event ID for your request."), cancellationToken);
                return null; // Exit early if no valid EventID is found
            }

            // Extract the DateRange (e.g., "7 days") and add it to the query
            var dateRange = ExtractDateRange(input);
            if (!string.IsNullOrEmpty(dateRange))
            {
                kqlQuery += $"| where TimeGenerated > ago({dateRange}) | project TimeGenerated, Account, Computer, EventID | take 10 ";
            }

            return kqlQuery;  // Return the constructed KQL query
        }

        private string ExtractDateRange(string input)
        {
            // Simple extraction logic to detect "7 days", "3 days", etc.
            var match = Regex.Match(input, @"(\d+)\s+days?");
            if (match.Success)
            {
                return $"{match.Groups[1].Value}d";  // Return as "7d", "3d", etc.
            }
            return null;  // Return null if no date range found
        }

        // Run KQL query in Azure Sentinel / Log Analytics
        private async Task<string> RunKqlQueryAsync(string kqlQuery)
        {
            var _workspaceId = _configuration["AzureSentinel:WorkspaceId"];
            string queryUrl = $"https://api.loganalytics.io/v1/workspaces/{_workspaceId}/query";
            var accessToken = await GetAccessTokenAsync();  // Get Azure AD token

            var requestBody = new
            {
                query = kqlQuery
            };

            var jsonContent = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");

            _httpClient.DefaultRequestHeaders.Clear();
            _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");

            var response = await _httpClient.PostAsync(queryUrl, jsonContent);
            var responseBody = await response.Content.ReadAsStringAsync();

            return responseBody;  // Return the query result
        }

        // Get Azure AD token for querying Log Analytics
        private async Task<string> GetAccessTokenAsync()
        {
            var _tenantId = _configuration["AzureSentinel:TenantId"];
            var _clientId = _configuration["AzureSentinel:ClientId"];
            var _clientSecret = _configuration["AzureSentinel:ClientSecret"];
            var url = $"https://login.microsoftonline.com/{_tenantId}/oauth2/v2.0/token";
            var body = new Dictionary<string, string>
            {
                { "grant_type", "client_credentials" },
                { "client_id", _clientId },
                { "client_secret", _clientSecret },
                { "scope", "https://api.loganalytics.io/.default" }
            };

            var content = new FormUrlEncodedContent(body);
            var response = await _httpClient.PostAsync(url, content);
            var responseBody = await response.Content.ReadAsStringAsync();
            dynamic result = JsonConvert.DeserializeObject(responseBody);

            return result.access_token;
        }
    }
}

Event ID Mapping

Let’s map most important Event ids to utterances. The Solution can be enhanced with Text Analytics and NLU, but for this workshop we are creating the dictionary.

{
  "failed sign-in": 4625,
  "successful sign-in": 4624,
  "account lockout": 4740,
  "password change": 4723,
  "account creation": 4720,
  "logon type": 4624,
  "registry value was modified": 4657,
  "user account was changed": 4738,
  "user account was enabled": 4722,
  "user account was disabled": 4725,
  "user account was deleted": 4726,
  "user account was undeleted": 4743,
  "user account was locked out": 4767,
  "user account was unlocked": 4768,
  "user account was created": 4720,
  "attempt was made to duplicate a handle to an object": 4690,
  "indirect access to an object was requested": 4691,
  "backup of data protection master key was attempted": 4692,
  "recovery of data protection master key was attempted": 4693,
  "protection of auditable protected data was attempted": 4694,
  "unprotection of auditable protected data was attempted": 4695,
  "a primary token was assigned to process": 4696,
  "a service was installed in the system": 4697,
  "a scheduled task was created": 4698,
  "a scheduled task was deleted": 4699,
  "a scheduled task was enabled": 4700,
  "a scheduled task was disabled": 4701,
  "a scheduled task was updated": 4702,
  "a token right was adjusted": 4703,
  "a user right was assigned": 4704,
  "a user right was removed": 4705,
  "a new trust was created to a domain": 4706,
  "a trust to a domain was removed": 4707,
  "IPsec Services was started": 4709,
  "IPsec Services was disabled": 4710
}

Make all required updates to Program.cs and Startup.cs for the Namespace and the public class.

// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.22.0

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace SecurityBot
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Testing

Run the Application and open the Azure Bot Emulator to test the Bot. All you need is to add the localhost URL to the Emulator and make some Chat interactions for example:

  • What is a SOAR ? Using OpenAI Chat
  • Generate a KQL query for failed sign-in logs on the past 3 days
  • Run a KQL query for failed sign-in logs on the past 3 days
Bot Framework Emulator for AI Security Assistant

We have correct executions and KQL against our Sentinel\Log Analytics workspace. Let’s build this Bot on Azure and use it from our Teams Client as our Trusted Security Assistant !

Build on Azure

The logic behind a Bot build on Azure is to create an Azure Web App and then the relevant Azure Bot Service. All the steps are published in Microsoft Documentation.

You will find the ARM Templates on the Solution Window in Visual Studio 2022:

AI Security Assistant Visual Studio
Deployment ARM Templates

Use the following commands to create your app registration and set its password. On success, these commands generate JSON output.

  • Use the az ad app create command to create an Microsoft Entra ID app registration.
  • This command generates an app ID that you’ll use in the next step.
  • az ad app create –display-name “<app-registration-display-name>” –sign-in-audience “AzureADMyOrg”
  • Use AzureADMyOrg for a single tenant app.
  • Use the az ad app credential reset command to generate a new password for your app registration. ad app credential reset --id "<appId>"
  • Record values you’ll need in later steps: the app ID και password from the command output.

Once you have the App Registration ready and configured deploy the Web App on Azure using the deployment Templates.

Create the App Service and the Azure Bot resources for your bot. Both steps use an ARM template and the az deployment group create Azure CLI command to create the resource or resources.

  1. Create an App Service resource for your bot. The App service can be within a new or existing App Service Plan.For detailed steps, see Use Azure CLI to create an App Service.
  2. Create an Azure Bot resource for your bot.For detailed steps, see Use Azure CLI to create or update an Azure Bot.

az deployment group create –resource-group <resource-group> –template-file <template-file-path> –parameters “@<parameters-file-path>”

Now time to build and Publish the Bot, make sure you have run the Bot resource ARM deployment as we did with the Web App

Create the deployment file for the Bot:

  1. Switch to your project’s root folder. For C#, the root is the folder that contains the .csproj file.
  2. Do a clean rebuild in release mode.
  3. If you haven’t done so before, run az bot prepare-deploy to add required files to the root of your local source code directory. This command generates a .deployment file in your bot project folder.
  4. Within your project’s root folder, create a zip file that contains all files and sub-folders.

I suggest after this to run either: Run the az webapp deploy command from the command line to perform deployment using the Kudu zip push deployment for your app service (web app).

Or select the Publish option from the Solution Explorer and Publish using the created Web App

AI Security Assistant publish in Azure
Publish to Azure

Remember to add the App ID and the relevant details to appsettings.json we saw earlier. In case you need to re test with the Emulator, remove the App Type, the App ID , Password and Tenant ID settings before running the App locally!

Upon success make sure the Bot Messaging Endpoint has the Web App URL we created, followed by the /api/messages suffix. In case it is missing add it:

AI Security Assistant in Bot Application

Now we must add the correct API Permissions to the App registration in Entra ID. Select the App Registration, go to API Permissions, add permission and select API My Organization uses. Find the Log analytics and add the Application Permissions for Read:

AI Security Assistant Web App Permissions
Log Analytics API
AI Security Assistant Log Analytics permissions
Application Permissions

This way we are able to run\execute KQL against our Sentinel – Log Analytics Workspace.

Bot Channels – Teams

Now that our Bot is active and we can Test in “Test in Web Chat”, we can create the Teams Integration. It is really a simple step, where we select the Teams option from the Channels and verify the configuration.

AI Security Assistant Bot channels
Enable Teams Channel

Once we enable that, we can get the HTTPS code from the Get Embed option in the Channels Menu, or open he URL Directly when we select the Teams Channel:

AI Security Assistant Teams Channel
Teams URL

Before we start using the Bot we must make a significant configuration in Teams Admin Center. Otherwise the Bot will probably show-up but unable to get messages from the Chat.

AI Security Assistant Teams Admin
Teams Admin setting for Bot

Bot in Teams

Finally we are able to use our Security Assistant Bot in Teams, Web or Desktop App. The Bot will provide generic advice from Azure OpenAI Chat model, will generate KQL queries for a number of Events and execute those Queries in Log Analytics and we will see the results in our UI.

AI Security Assistant in action
Bot in Teams

We can always change the appearance of the results, in this workshop we have minimal presentation for better visibility. The next phase of this Deployment can utilize Language Service where all Event IDs are dynamically recognized through a Text Analytics service.

Κλείσιμο

In conclusion, this workshop demonstrated the seamless integration of Azure’s powerful AI services and Log Analytics to build a smart, security-focused chatbot. By leveraging tools like Azure OpenAI, Log Analytics, and the Bot Framework, we’ve empowered bots to provide dynamic insights and interact meaningfully with data. Whether it’s querying log events or responding to security inquiries, this solution highlights the potential of AI-driven assistants to elevate security operations. Keep exploring and building with Azure, and unlock new possibilities in automation and intelligence!

AI Security Assistant Bot Architecture
Architecture

Μοιραστείτε το!

Αφήστε το σχόλιο σας