DisGuard: Protecting Your Discord Server from Malicious Threats

Aadarsh KannanAadarsh Kannan
13 min read

Hey there, fellow Discord enthusiasts and Devs! Let's talk about something super important—keeping our Discord servers safe and secure. As someone who dives into various communities and chats regularly, I've come to realize just how vital it is to ensure our online spaces are free from any potential risks.

Discord isn't just a place for casual conversation; it's where developers, companies, and tons of other folks come together. And with that comes the need to be vigilant about safeguarding our shared space. From malicious links to data breaches, there's a lot to watch out for. Join me on this blog journey as I create a Discord bot using Pangea to make servers the safest, most enjoyable spots on the internet.

The initial design

As I kickstart this project, I've been brainstorming a name for the bot that captures its essence - and DisGuard seems to fit the bill perfectly! For the logo, I've dived into Canva and quickly crafted a simple logo (which you can see in the header image) that compliments the project.

Getting the bot ready

I am gonna use Python as a primary programming language for this bot. If you are new to creating a Discord bot, check out this getting started guide from the Discord-py documentation.

Steps to follow:

  • Create a bot account - Check this guide on Creating a Bot Account (discordpy.readthedocs.io).

    Here is the flow to follow (refer to the following images as well):
    ➩ Create an application > Add the General Information of the bot
    ➩ Go to OAuth2 > URL Generator - Tick the bot score and provide the right bot permissions based on the need. I am gonna provide the administrator access for now > At the end you will see the link, copy and paste it into the browser and add the bot to your server
    ➩ Go to Bot > Enable the message content intent and

  • Once you are done, you will be able to see the bot (Offline) on your server. Now let's open the code editor (choose as per your wish) and create a bot.py file. First, let's start the bot and log the messages. Before that, you need the Bot Token, which you can find on the Bot Page while creating. It can only be viewed only once. So make sure you write it down somewhere. Install the discord.py package as well using the command py -3 -m pip install -U discord.py.

      import discord
    
      class MyClient(discord.Client):
          async def on_ready(self):
              print(f'Logged on as {self.user}!')
    
          async def on_message(self, message):
              print(f'Message from {message.author}: {message.content}')
    
      intents = discord.Intents.default()
      intents.message_content = True
    
      client = MyClient(intents=intents)
      client.run('bot token goes here')
    

    This code defines a Discord bot that prints a message to the console whenever it logs in and whenever it receives a message. The MyClient class inherits from the discord.Client class, which provides the basic functionality for creating a Discord bot. The on_ready() method is called when the bot logs in, and the on_message() method is called whenever the bot receives a message. For more check the quick Introduction page.

  • Open the Terminal and run python bot.py and you will notice the bot is online and it logs all the messages that are sent in the channel. You will be able to see something like the below.

It's time to redact API Keys

Imagine having to pay for the misuse of an API key you accidentally shared in a chat!

It's a common scenario for developers to copy and paste code, including credentials, without realizing it. When asking the community for help, it's easy to miss redacting API keys from the shared code. We also share the credentials with the peers in the chat. Which is not recommended.

But what if there was a way to automatically redact API keys in chats?

That's where the idea of redacting API keys in chats comes in. It would help to protect developers from accidentally exposing their API keys and being charged for their misuse.

Redaction is the process of removing sensitive information from texts, documents and other files. This can be done for a variety of reasons, such as to protect the privacy of individuals, to comply with regulations, or to prevent sensitive information from being leaked to the public.

Here's how it could work:

  • The bot would scan chats for API keys.

  • The bot would then notify the user that an API key has been redacted and send the redacted message.

For this, we are gonna use Pangea's Redact service API for redacting the sensitive credentials that are being shared in the channel.


About the Pangea

Pangea is a comprehensive cloud platform tailored for application developers, offering a range of security services. These services cover essential aspects like user authentication, authorization, audit logging, secrets management, entitlement and licensing, PII redaction, embargo, and intelligent handling of files, IP addresses, URLs, and domains. For developers prioritizing secure applications compliant with GDPR data residency regulations, Pangea Cloud emerges as a reliable choice.

Sign up here and once you are in the Pangea Console, get the following keys: pangea_token and domain, which we need primarily to run the Pangea services.


Coming back to redaction, You will find the service called Redact in the Pangea Console. To learn more about the Redact service and get started, I suggest you check out their documentation here.

Steps to follow:

Activate the Redact service and set up a token > Set the Redact rules that suit your requirements > Run a test on the service > Integrate the Redact service into your application

Rulesets: A set of rules consists of redaction rules organized into categories. In our specific scenario, we will activate the Secrets rulesets, which encompass guidelines designed to recognize typical credentials used across a range of widely utilized services. Use the Test Rules feature to test out the rules and understand how it works.

Pangea's Redact Ruleset Service

As you can see from the above image, I've tested out the Pangea Toekn rule by passing a token to it. It in response sends us the redacted text by mentioning <PANGEA_TOKEN> by replacing the key.

Now let's implement this service through Pangea's Python SDK.

redact_response = redact.redact(text=text, rulesets=["SECRETS"])

The redact function takes two arguments:

text: The text to be redacted.

rulesets: A list of rulesets to use for redaction. The SECRETS ruleset is a set of rules that we have enabled above in the console.

The redact function works by applying the rulesets to the text. For each rule in a ruleset, the function checks to see if the text matches the rule. If it does, the function redacts the matching text.

The redact_response variable will contain the redacted text. Here is what the response will look like.

Now let's integrate the redact feature with the above written piece of code in the bot.py file. Don't forget to install the Pangea python package. Install it using the following command: pip3 install pangea-sdk.

import discord
import pangea.exceptions as pe
from pangea.config import PangeaConfig
from pangea.services import Redact
import re
import json

# Pangea Cloud authentication token
token = "your_pangea_token"
# Pangea Cloud domain
domain = "your_pangea_domain"
# Configuration object for Pangea Cloud
config = PangeaConfig(domain=domain)

# Function to perform redaction using Pangea Cloud
def go_redact(text):
    # Create a Redact service instance with the provided token and configuration
    redact = Redact(token, config=config)
    print(f"Redacting PII from: {text}")
    try:
        # Call the redact method with the specified text and rulesets
        redact_response = redact.redact(text=text, rulesets=["SECRETS"])
        # Print the redacted text - Just for logging in the terminal - can be removed!
        print(f"Redacted text: {redact_response.result.redacted_text}")

        # Check if the original text was redacted
        if text == redact_response.result.redacted_text:
            return ""
        # Return the redacted text
        return redact_response.result.redacted_text

    except pe.PangeaAPIException as e:
        # Handle Pangea API exceptions
        print(f"Embargo Request Error: {e.response.summary}")
        for err in e.errors:
            print(f"\t{err.detail} \n")
        return False

# Discord client class
class MyClient(discord.Client):

    async def on_ready(self):
        # Event handler when the bot is ready and connected
        print(f'Logged on as {self.user}!')

    async def on_message(self, message):
        # Event handler for incoming messages

        # Print the details of the incoming message - Just for the log :)
        print(f'Message from {message.author}: {message.content}')

        # Ignore messages from the bot itself
        if message.author == self.user:
            return

        # Perform redaction on the message content
        redacted_msg = go_redact(message.content)

        # Check if redaction resulted in any changes
        if redacted_msg != "":
            # Delete the original message
            await message.delete()
            # Send a notification about the detected API key
            await message.channel.send("API Key found!")
            # Send the redacted message
            await message.channel.send(redacted_msg)

# Set up Discord intents
intents = discord.Intents.default()
intents.message_content = True

# Create an instance of the Discord client
client = MyClient(intents=intents)
# Run the bot with the provided token
client.run('your_bot_token')

What the above code does is that If the bot detects a potential API key in a user's message, it promptly deletes the original message, notifies the channel about the finding, and sends the redacted version to maintain data security.

Even if you pasted a code, it redacts the key and sends the updated code in the chat.

Malicious links shared on Discord channels pose a serious threat by potentially leading users to phishing sites, malware downloads, or other harmful destinations. These links often disguise their true nature, attempting to trick users into clicking by masquerading as legitimate websites. Once clicked, they can compromise user accounts, install malicious software, or harvest sensitive information.

Discord channels can become targets for malicious link sharing due to their communal nature and the potential for a large user base. I've seen so many people spamming the channels. Users should exercise caution and be wary of unexpected or suspicious links to safeguard their online security and protect the integrity of the Discord community.

Let's try to secure the server with our bot by flagging the malicious links that are shared in the channels.

Before that let's quickly understand the difference between domain and URL. A domain is a human-readable label assigned to a specific IP address on the internet, organizing and identifying resources. For instance, in "example.com," "example" is the subdomain, and ".com" is the top-level domain. On the other hand, a URL, such as "https://www.example.com/page," is a specific web address comprising a protocol ("https"), the domain ("www.example.com"), and a path ("/page") that directs to a particular resource, offering a precise means to access content on the internet. In essence, a domain is a broader organizational identifier, while a URL is a detailed address specifying the location of a specific online resource.

Pangea offers two distinct services, one catering to domains and the other to URLs. The Domain Intel service enables the retrieval of information concerning established domain names, providing insights into a domain's reputation. Meanwhile, the URL Intel service facilitates the retrieval of information about established URLs, offering insights into the reputation of a specific web address.

Just like enabling the redact service as we did above, do the same in the URL intel and Domain intel service in the Pangea console. You can see the green dot 🟢 which indicates the enabled services.

Let's implement this! The required parameter is the URL which needs to be looked up. The optional parameter is the provider, which uses reputation data from the provider: "crowdstrike".

response = url_intel.reputation(
    url="url",
    provider="crowdstrike",
)

Here is the updated code with the URL Intel service which flags the message whenever a malicious URL is found. I've added the go_url_intel which calls the URL Intel service and returns us with a response.

import discord
import pangea.exceptions as pe
from pangea.config import PangeaConfig
from pangea.services import Redact
from pangea.services import UrlIntel
import re
import json

# Set Pangea authentication token and domain
token = "your_pangea_token"
domain = "aws.us.pangea.cloud"
config = PangeaConfig(domain=domain)

# Redaction function using Pangea Cloud
def go_redact(text):
    redact = Redact(token, config=config)
    print(f"Redacting PII from: {text}")
    try:
        redact_response = redact.redact(text=text, rulesets=["SECRETS"])
        print(f"Redacted text: {redact_response.result.redacted_text}")

        # Check if the original text was redacted
        if text == redact_response.result.redacted_text:
            return ""
        return redact_response.result.redacted_text

    except pe.PangeaAPIException as e:
        print(f"Embargo Request Error: {e.response.summary}")
        for err in e.errors:
            print(f"\t{err.detail} \n")
        return False

# URL intelligence function using Pangea Cloud
def go_url_intel(url):
    intel = UrlIntel(token, config=config)

    try:
        response = intel.reputation(
            url=url,
            provider="crowdstrike",
            verbose=True,
            raw=True,
        )

        print(f"Response: {response.result}")
        return response.result.data.verdict

    except pe.PangeaAPIException as e:
        print(f"Request Error: {e.response.summary}")
        for err in e.errors:
            print(f"\t{err.detail} \n")
            return err.detail

# Discord client class
class MyClient(discord.Client):

    async def on_ready(self):
        # Event handler when the bot is ready and connected
        print(f'Logged on as {self.user}!')

    async def on_message(self, message):
        # Event handler for incoming messages

        # Print the details of the incoming message
        print(f'Message from {message.author}: {message.content}')

        # Ignore messages from the bot itself
        if message.author == self.user:
            return

        # Redact sensitive information in the message content
        redacted_msg = go_redact(message.content)

        # Check if redaction resulted in any changes
        if redacted_msg != "":
            # Delete the original message
            await message.delete()
            # Send a notification about the detected API key
            await message.channel.send("API Key found!")
            # Send the redacted message
            await message.channel.send(redacted_msg)

        # Search for URLs in the message content
        match = re.search(r'(http|https)://(?P<hostname>[a-zA-Z0-9-]{1,63}(?:\.[a-zA-Z0-9-]{1,63})*)[^\s]+', message.content)

        if match:
            # Extract the detected URL
            detected_url = match.group()
            print(f"Detected URL: {detected_url}")
            # Check the intelligence of the URL
            url_verdict = go_url_intel(detected_url)
            if url_verdict != "":
                # If the URL is deemed malicious, send a warning to the channel
                if url_verdict == "malicious":
                    await message.channel.send("Malicious website found!",  reference=message)

# Set up Discord intents
intents = discord.Intents.default()
intents.message_content = True

# Create an instance of the Discord client
client = MyClient(intents=intents)
# Run the bot with the provided token
client.run('your_bot_token')

Here is a graphical view of how the response from url Intel will look like. We will check the verdict key to know whether the URL is malicious or not.

As you can see in the above output, the bot flags the malicious URL.


Note: In the provided code, the API key is directly embedded as a string in the token variable. Storing API keys directly in code is considered insecure, as it poses a risk of accidental exposure if the code is shared or made public. Additionally, it makes it challenging to manage and rotate API keys regularly for security purposes.

To enhance security, consider employing environment variables to store sensitive information such as API keys. Instead of hardcoding the key in the script, retrieve it from the environment at runtime. This practice not only adds an extra layer of security by keeping sensitive information separate from the codebase but also allows for easy key rotation without modifying the script.


!Whois <domain>

Let's add a command-based functionality for our bot. The user will be able to retrieve the information regarding the domain.

Pangea provides a Whois API that Retrieves who is for a domain from a provider, including an optional detailed report. You can find this in the domain intel. Go ahead and enable it.

We will look for the text that starts with !whois and then find the domain from the text. Then we pass it to Pangea's Whois API which provides us with the response related to the domain. Here is what the response looks like.

Let's write a function called go_who for the same.

def go_whois(domain):
    print("Checking domain...")

    try:
        response = intel.who_is(domain=domain, provider="whoisxml", verbose=True, raw=True)
        print(f"Response: {response.result.data}")
        domain_name = response.result.data.domain_name
        domain_availability = response.result.data.domain_availability
        created_date = response.result.data.created_date
        registrar_name = response.result.data.registrar_name
        registrant_organization = response.result.data.registrant_organization
        return [domain_name, domain_availability, created_date, registrar_name, registrant_organization]

    except pe.PangeaAPIException as e:
        print(f"Request Error: {e.response.summary}")
        for err in e.errors:
            print(f"\t{err.detail} \n")
            return None

The complete code can be found in the GitHub repo. Here is what the response from the bot will look like.

There's a lot we can implement with Pangea to enhance security and stay secure online. We need to work more and I know that the code is not optimized and not ready for production. I will try to make it more optimized, and error-free and will work on more features.

Resources

Conclusion

In conclusion, prioritizing the safety and security of our Discord servers is paramount in creating a positive and thriving online community. As we navigate through diverse conversations and collaborations, the potential risks, from malicious links to data breaches, highlight the importance of vigilance.

There is a lot more to do with the bot and this is just getting started. Contribution and feedback are welcome :).

Thanks for the read - Aadarsh.

13
Subscribe to my newsletter

Read articles from Aadarsh Kannan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Aadarsh Kannan
Aadarsh Kannan

Status: 102