<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nick Doty</title>
    <description>The latest articles on DEV Community by Nick Doty (@nldoty).</description>
    <link>https://dev.to/nldoty</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F216146%2F5a46a97e-4083-4b2d-99a7-64bd9835a8ef.jpeg</url>
      <title>DEV Community: Nick Doty</title>
      <link>https://dev.to/nldoty</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nldoty"/>
    <language>en</language>
    <item>
      <title>Links to help those impacted by the wildfires in Hawaii</title>
      <dc:creator>Nick Doty</dc:creator>
      <pubDate>Mon, 14 Aug 2023 22:42:31 +0000</pubDate>
      <link>https://dev.to/nldoty/links-to-help-those-impacted-by-the-wildfires-in-hawaii-1io1</link>
      <guid>https://dev.to/nldoty/links-to-help-those-impacted-by-the-wildfires-in-hawaii-1io1</guid>
      <description>&lt;p&gt;To locate missing loved ones:1-800-RED-CROSS&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.hawaiicommunityfoundation.org/maui-strong"&gt;Office of the Governor, Hawai‘i Community Foundation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.redcross.org/donate/donation.html/"&gt;The American Red Cross of Hawaii donation site&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://give.feedingamerica.org/a/hawaii-wildfires"&gt;Feeding America Food Banks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mauifoodbank.org/donate/"&gt;Maui Food Bank&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mauihumanesociety.org"&gt;Help Pets: Maui Humane Society&lt;/a&gt; &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Discord bot to filter out swear words</title>
      <dc:creator>Nick Doty</dc:creator>
      <pubDate>Tue, 29 Sep 2020 16:06:52 +0000</pubDate>
      <link>https://dev.to/nldoty/building-a-discord-bot-to-filter-out-swear-words-46h5</link>
      <guid>https://dev.to/nldoty/building-a-discord-bot-to-filter-out-swear-words-46h5</guid>
      <description>&lt;p&gt;Now that it's been 7 years since quarantine began, I've been expanding my assortment of random Discord bots.&lt;br&gt;
My latest experiment has been a bot that can determine if a message has a "bad word" in it, and if so, deletes the message, and then shames the user. It also can determine if the user is trying to get around the filter by using "leetspeak". This is a short write-up of how that bot works.&lt;/p&gt;

&lt;p&gt;Disclaimer: This bot (and the following write-up) revolves around swear words. I will be talking about swear words in this write-up. If that bothers you, I would recommend not reading this. Personally, I have much worse things to worry about in 2020.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bot information
&lt;/h2&gt;

&lt;p&gt;If you'd like to install the bot on your Discord server, simply &lt;a href="https://discord.com/api/oauth2/authorize?client_id=756276859225768057&amp;amp;permissions=8192&amp;amp;scope=bot" rel="noopener noreferrer"&gt;click this link&lt;/a&gt;. FYI: You must be an administrator on the Discord server to install it. It requires the &lt;code&gt;manage messages&lt;/code&gt; permission as it deletes messages with bad words.&lt;br&gt;
If you'd like to take a look at the bot, all of the code is available &lt;a href="https://github.com/nldoty/PottyMouthBot" rel="noopener noreferrer"&gt;here on Github&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
If you have any requests or bugs to report, you can report an issue &lt;a href="https://github.com/nldoty/PottyMouthBot/issues" rel="noopener noreferrer"&gt;here on Github&lt;/a&gt; or you can reach out to me on Twitter, &lt;a href="https://twitter.com/nldoty" rel="noopener noreferrer"&gt;@nldoty&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building the bot
&lt;/h2&gt;

&lt;p&gt;In building this bot there were 4 areas that I focused on in development and I'll be focusing on each shortly below.&lt;/p&gt;
&lt;h3&gt;
  
  
  Bad word list
&lt;/h3&gt;

&lt;p&gt;The first problem is establishing a list of "bad words". Depending on what you're comfortable with, this can vary quite a bit. You could choose a classic, the &lt;a href="https://www.youtube.com/watch?v=kyBH5oNQOS0" rel="noopener noreferrer"&gt;7 words you can't say by George Carlin&lt;/a&gt;. Another article I found had &lt;a href="https://www.rypeapp.com/blog/english-swear-words/" rel="noopener noreferrer"&gt;26 swear words&lt;/a&gt; listed. But I wanted to be as thorough as possible - so I chose this list of &lt;a href="https://www.cs.cmu.edu/~biglou/resources/bad-words.txt" rel="noopener noreferrer"&gt;1300 words from CMU&lt;/a&gt;. That being said - there were a lot of words that could be seen as "controversial" that I didn't feel the need to censor. Words like "Republican" and "Democrat". I narrowed it down to about 1000 words.&lt;/p&gt;
&lt;h3&gt;
  
  
  Data Structure
&lt;/h3&gt;

&lt;p&gt;Next we need a way to see if any words in a message happens to be in the designated list of "bad words". &lt;br&gt;
The naive approach would be a list search. Every time you come across a word, you check to see if it exists in your list of "bad words". &lt;br&gt;
The problem with this approach is time complexity. Searching a python list (&lt;code&gt;x in list&lt;/code&gt;) is &lt;a href="https://wiki.python.org/moin/TimeComplexity" rel="noopener noreferrer"&gt;O(n)&lt;/a&gt; where &lt;code&gt;n&lt;/code&gt; is the number of items in your list. If your list has only 7 words in it, this might be acceptable! But with over a thousand words, this isn't going to work.&lt;br&gt;
Thankfully, this is almost a textbook use-case for a &lt;a href="https://en.wikipedia.org/wiki/Trie" rel="noopener noreferrer"&gt;Trie&lt;/a&gt;. &lt;/p&gt;
&lt;h4&gt;
  
  
  So uhh, what's a Trie?
&lt;/h4&gt;

&lt;p&gt;A Trie, or prefix tree, is an ordered-tree structure used to store dynamic information that can be searched for. It's a unique type of tree because unlike a binary tree, no node in the tree stores the key that you're searching for - instead, the key itself is distributed. &lt;/p&gt;

&lt;p&gt;The purpose of this post isn't to teach Tries: so if you'd like to read more, &lt;a href="https://www.geeksforgeeks.org/trie-insert-and-search/" rel="noopener noreferrer"&gt;this GeeksforGeeks&lt;/a&gt; post is a great resource. But here's a TL;DR:&lt;br&gt;
Tries are made up of nodes, and each node contains two properties: &lt;code&gt;children&lt;/code&gt; representing the nodes beneath the node (usually an array), and a way to designate the &lt;code&gt;END&lt;/code&gt; of a word. The implementation is stored &lt;a href="https://github.com/nldoty/PottyMouthBot/blob/master/src/trie/TrieNode.py" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but the code is very simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrieNode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;_MAX_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_MAX_SIZE&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_end_of_word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;_MAX_SIZE&lt;/code&gt; is 40 because that is the size of my dictionary:&lt;br&gt;
&lt;code&gt;'abcdefghijklmnopqrstuvwxyz12345670(|@$[!'&lt;/code&gt;&lt;br&gt;
The Trie itself has two base functions: &lt;code&gt;insert&lt;/code&gt; and &lt;code&gt;search&lt;/code&gt;. To "build" the Trie you will insert all of your words into the structure, and then you can search to see if a word is in your Trie. &lt;br&gt;
Here is an example image from &lt;a href="http://theoryofprogramming.com/2015/01/16/trie-tree-implementation/" rel="noopener noreferrer"&gt;TheoryOfProgramming&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe7i9w9oljtr0gg9q9bsb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe7i9w9oljtr0gg9q9bsb.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So for my example, I want to not only add all my words from my bad words list to my Trie, but I also wanted to add &lt;a href="https://en.wikipedia.org/wiki/Leet" rel="noopener noreferrer"&gt;leet speak&lt;/a&gt; variations to Trie to catch as many foul words as possible. &lt;/p&gt;

&lt;p&gt;To do this, I use some recursion to add extra TrieNodes when I find a letter that has a leetspeak equivalent - passing the remainder of the letters as the word to insert to fill in the Trie with all possibilities. &lt;br&gt;
The code can be seen &lt;a href="https://github.com/nldoty/PottyMouthBot/blob/master/src/trie/Trie.py" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Let's look at how this would work in practice, with the word &lt;code&gt;shit&lt;/code&gt;. &lt;br&gt;
Looking at each individual letter, &lt;code&gt;s&lt;/code&gt; &lt;code&gt;h&lt;/code&gt; &lt;code&gt;i&lt;/code&gt; &lt;code&gt;t&lt;/code&gt;:&lt;br&gt;
&lt;code&gt;s&lt;/code&gt; has two equivalents, &lt;code&gt;$&lt;/code&gt; and &lt;code&gt;5&lt;/code&gt;&lt;br&gt;
&lt;code&gt;h&lt;/code&gt; has no equivalents&lt;br&gt;
&lt;code&gt;i&lt;/code&gt; has two equivalents, &lt;code&gt;|&lt;/code&gt; and &lt;code&gt;!&lt;/code&gt;&lt;br&gt;
&lt;code&gt;t&lt;/code&gt; has one equivalent, &lt;code&gt;7&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This ultimately gives us 18 different options to search for:&lt;br&gt;
&lt;code&gt;[shit, shi7, sh|t, sh|7, sh!t, sh!7, $hit, $hi7, $h|t, $h|7, $h!t, $h!7, 5hit, 5hi7, 5h|t, 5h|7, 5h!t, 5h!7]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Searching through 18 different words for one word would be slow. But the Trie is quick and simple, because the complexity is limited to the length of your word.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fekybuy6byt7uoomek4hd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fekybuy6byt7uoomek4hd.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The more words you're searching for, the more time you'll be saving. This is the beauty of using a Trie. &lt;/p&gt;
&lt;h3&gt;
  
  
  Discord integration
&lt;/h3&gt;

&lt;p&gt;The Discord integration code can be seen &lt;a href="https://github.com/nldoty/PottyMouthBot/blob/master/src/main.py" rel="noopener noreferrer"&gt;here&lt;/a&gt; and is very basic: it listens for all messages to a server, parses the message into words and searches the Trie for each word. If a word is found in the Trie, the bot will delete the message and then shame the user using a random assortment of shaming messages.&lt;/p&gt;

&lt;p&gt;The only somewhat interesting part of this code snippet is this line:&lt;br&gt;
&lt;code&gt;text = text.translate(str.maketrans(table))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A major problem I ran into in working on the bot is determining the question, "what is a word?" This was one of the hardest things for me to solve. When people type, they use things like punctuation - commas, periods, dashes, you name it. A typical way to divide a string in python is using &lt;code&gt;.split()&lt;/code&gt;, which will break things up by space. However, if you had the sentence&lt;br&gt;
&lt;code&gt;I like spinach, corn, and beans.&lt;/code&gt;&lt;br&gt;
Using &lt;code&gt;.split()&lt;/code&gt;, your result would be &lt;br&gt;
&lt;code&gt;['I', 'like', 'spinach,', 'corn,', 'and', 'beans.']&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In building my Trie, I don't include punctuation like periods and commas. So if you wrote the sentence&lt;br&gt;
"Shut up you stupid fucker."&lt;br&gt;
&lt;code&gt;.split()&lt;/code&gt; would break this up as &lt;br&gt;
&lt;code&gt;['Shut', 'up', 'you', 'stupid', 'fucker.']&lt;/code&gt;&lt;br&gt;
and my Trie would miss the word &lt;code&gt;fucker&lt;/code&gt; because there is a period as part of the word, so I would not see the &lt;code&gt;is_end_of_word&lt;/code&gt; at the right point.&lt;/p&gt;

&lt;p&gt;To avoid this, I use &lt;a href="https://www.programiz.com/python-programming/methods/string/translate" rel="noopener noreferrer"&gt;string.translate&lt;/a&gt;. Using the defined dictionary, it maps keys to their respective values - in my case, I map all the common characters that aren't part of my Trie leetspeak to &lt;code&gt;None&lt;/code&gt;. This solved quite a few of my problems, but obviously not all of them. &lt;/p&gt;

&lt;p&gt;The last major issue I have is one not easily solved. A really easy way to get around the bot is to use spaces in the middle of the word - instead of &lt;code&gt;shit&lt;/code&gt;, just write &lt;code&gt;sh&lt;/code&gt; &lt;code&gt;it&lt;/code&gt;. The bot sees this as two words, and doesn't connect that without the space, a swear word was made. I could add spaces to the Trie at every level, but how would words be ultimately divided? How could I pass words to the Trie so that &lt;code&gt;shit&lt;/code&gt;, &lt;code&gt;sh&lt;/code&gt;, and &lt;code&gt;it&lt;/code&gt; are all checked? This gets more and more complicated the more words/spaces your message has. You could search for all possible iterations with a moving window of sorts, but then you're running into major speed issues. &lt;br&gt;
The best answer would be some way for a program to know that &lt;code&gt;fu ck&lt;/code&gt; is really just one word, but &lt;code&gt;oh hm&lt;/code&gt; should be treated as two words. I think this problem is best explained using &lt;a href="https://xkcd.com/1425/" rel="noopener noreferrer"&gt;this XKCD comic&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzmur3r32iz5bl5o570iw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzmur3r32iz5bl5o570iw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Dockerization
&lt;/h3&gt;

&lt;p&gt;Finally, I like to build my Discord bots into Docker containers. It just makes deployment a lot easier. And the &lt;a href="https://github.com/nldoty/PottyMouthBot/blob/master/Dockerfile" rel="noopener noreferrer"&gt;Dockerfile&lt;/a&gt; for this one is very simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
# set base image (host OS)
FROM python:3.7.7

# set the working directory in the container
WORKDIR /code

# copy the dependencies file to the working directory
COPY requirements.txt .

# install dependencies
RUN pip install -r requirements.txt

# copy the content of the local src directory to the working directory
COPY src/ .

# command to run on container start
CMD [ "python", "./main.py" ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, building the container in the top directory is as simple as&lt;br&gt;
&lt;code&gt;docker build -t pottybot .&lt;/code&gt;&lt;br&gt;
and running the container is just&lt;br&gt;
&lt;code&gt;docker run pottybot&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And that's it!&lt;br&gt;
If you want to run the bot yourself, it's fairly straight-forward. Follow along with &lt;a href="https://realpython.com/how-to-make-a-discord-bot-python/" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; to create a bot in the &lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;Discord Developer Portal&lt;/a&gt;. Once you have a token, create a &lt;code&gt;.env&lt;/code&gt; file in the &lt;code&gt;src/&lt;/code&gt; folder with one line:&lt;br&gt;
&lt;code&gt;DISCORD_TOKEN='YOUR_TOKEN_HERE'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then build and run the Docker container and you're set! If you have any questions or comments, feel free to reach out to me and I'm happy to help!&lt;/p&gt;

&lt;p&gt;Stay safe and healthy!&lt;/p&gt;

</description>
      <category>discord</category>
      <category>python</category>
      <category>trie</category>
      <category>bot</category>
    </item>
    <item>
      <title>How to secure your Raspberry Pi - or - how my Raspberry Pi was hacked</title>
      <dc:creator>Nick Doty</dc:creator>
      <pubDate>Wed, 22 Apr 2020 19:32:36 +0000</pubDate>
      <link>https://dev.to/nldoty/how-to-secure-your-raspberry-pi-or-how-my-raspberry-pi-was-hacked-43oo</link>
      <guid>https://dev.to/nldoty/how-to-secure-your-raspberry-pi-or-how-my-raspberry-pi-was-hacked-43oo</guid>
      <description>&lt;p&gt;If you want to skip past the meat of the article and just read how to better secure your Raspberry Pi, do the following steps:&lt;/p&gt;

&lt;p&gt;Don't use the default login of &lt;code&gt;U:pi PW:raspberry&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you don't need SSH, disable it.&lt;/p&gt;

&lt;p&gt;If you do need SSH, consider only using &lt;code&gt;ssh&lt;/code&gt; keys, and disabling username/password login via &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; and changing your &lt;code&gt;PasswordAuthentication&lt;/code&gt; field from yes or commented out to&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PasswordAuthentication no&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;If ssh and passwords are a must, take some steps to make brute force attacks harder:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PermitRootLogin no&lt;br&gt;
AllowUsers your_username&lt;br&gt;
LoginGraceTime 30&lt;br&gt;
MaxAuthTries 1&lt;br&gt;
MaxStartups 2&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Really, read the &lt;a href="https://www.raspberrypi.org/documentation/configuration/security.md" rel="noopener noreferrer"&gt;official security guide&lt;/a&gt; from raspberrypi.org.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On to my idiocy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A quick back-story: I host a couple things at home - a personal website, a Jenkins instance and a Minecraft server. I've got a NUC that runs all of these things, and I route traffic through a Raspberry Pi 3 which I use as a reverse proxy and DNS client. Most of my workflow can be accessed through Firefox, so I haven't logged into the NUC or the Pi in quite some time. &lt;/p&gt;

&lt;p&gt;On February 18th I received the following email from Verizon Security: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnb8l9i9tqz8uvaarz3gk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnb8l9i9tqz8uvaarz3gk.png" alt="Verizon Email"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first, when I saw this, I thought nothing of it. Given my experiences with Verizon in the past, I thought this was related to my self hosting of my website but was nothing serious to worry about. I logged into my Verizon account and was greeted by this poorly formatted security message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F81l7arje6csfd85w2sjc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F81l7arje6csfd85w2sjc.png" alt="Verizon Warning Message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, I was suspicious. The &lt;a href="https://en.wikipedia.org/wiki/Mirai_(malware)" rel="noopener noreferrer"&gt;Mirai Malware&lt;/a&gt; name was specifically listed, and as someone who has almost 20 IoT devices on their network, I was a bit concerned. However, given all this information, I didn't really know where to start.&lt;/p&gt;

&lt;p&gt;I have one of the original &lt;a href="https://on.google.com/hub/" rel="noopener noreferrer"&gt;Google OnHub&lt;/a&gt; routers that I bought years ago. With it comes an app which allows you to do tasks most routers let you do - port forwarding, set static IPs, and do remote restarts. &lt;/p&gt;

&lt;p&gt;Another major feature the router supports is viewing devices on your network. You can see IP addresses, give priority speeds to a device, and see the amount of data that has been consumed. Having read up on Mirai and how it operates, I figured it was a decent place to start. Here was a look at my network devices, by consumption, for 7 days between February 11 and February 17:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdx01vzlhvehwhlfao1mg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdx01vzlhvehwhlfao1mg.jpeg" alt="Week internet traffic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We use our Apple TV constantly as background noise, so this was kind of shocking. I understand that traffic coming into the network for my website goes through the Raspberry Pi, but 95GB of data is absurd. &lt;/p&gt;

&lt;p&gt;I &lt;code&gt;ssh&lt;/code&gt;'d into my Raspberry Pi and decided to see what was running, because I could see connections being made in real time through the Google Wifi app. I did this through using the command `top: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb0ai8wro2o91p44e8l5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb0ai8wro2o91p44e8l5v.png" alt="Output of top command"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most of this doesn't seem nefarious, but what stuck out was the random &lt;code&gt;curl&lt;/code&gt; command (on &lt;code&gt;PID 6658&lt;/code&gt; and &lt;code&gt;PID 7740&lt;/code&gt;) and the &lt;code&gt;wget&lt;/code&gt; commnad (on &lt;code&gt;PID 7690&lt;/code&gt;) all running as &lt;code&gt;root&lt;/code&gt;. They would appear and disappear so they were hard to determine, but they definitely weren't traffic to my website. If you hit &lt;code&gt;c&lt;/code&gt; while running &lt;code&gt;top&lt;/code&gt; it will show the full command being run, and this was the output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fftrxqx2phlfdswexgvkj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fftrxqx2phlfdswexgvkj.png" alt="Output of top c command"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you go to the IP address in the &lt;code&gt;wget&lt;/code&gt; command (please don't) it's an html site with just the word "ghost". If you add the i686 to the URL, it downloads a file. At this point, it's pretty clear that not only do I have major issues, but the issues will continue unless I figure out what I did wrong.&lt;/p&gt;

&lt;p&gt;I started searching for how this could happen to my Raspberry Pi. There are two main issues that when combined can make your raspberry pi exploitable right out of the box. The issues being SSH server being enabled and the default username and password for the pi being the same for all Pis. If you have a Raspberry Pi purchased after November 2016 (or have updated it since) then ssh is disabled, and you are required to change the default user and password. But if you just plugged it in, years ago and forgot about it, you could be vulnerable. &lt;/p&gt;

&lt;p&gt;By default, the raspberry pi login is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;username: pi&lt;br&gt;
password: raspberry&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you have a pi, you should &lt;em&gt;really&lt;/em&gt; check if this login still works. I know that I changed the password for this account and disabled it, in favor of an account named &lt;code&gt;nick&lt;/code&gt; (duh). However, I definitely have SSH turned on as that's the only way I access my Raspberry Pi. When setting up my reverse proxy, I opened up my Pi to ports &lt;code&gt;80&lt;/code&gt; and &lt;code&gt;443&lt;/code&gt; through the Google Wifi app. I decided to check and see if those were still functioning and if I'd changed anything else. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw6u23w0erd61xp1r5xvu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw6u23w0erd61xp1r5xvu.png" alt="Forwarded ports"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point I knew I had made a mistake, because port &lt;code&gt;22&lt;/code&gt; was open to the world on my Pi. I was playing around with the idea of having a web server for SSH into my NUC, but it never came to fruition. Forgetting to remove port forwarding on &lt;code&gt;22&lt;/code&gt; to my Pi was mistake number one.&lt;/p&gt;

&lt;p&gt;Mistake number two I don't recall, but I checked the users list on my Pi using nano &lt;code&gt;/etc/passwd&lt;/code&gt; and found a user named &lt;code&gt;temp&lt;/code&gt;. Out of a sheer guess, the password was also temp. I don't recall making this user but having spent many a late frustrating night working on my reverse proxy, there's no doubt in my mind that I did it. The third issue (which I had resolved the week before) was that the url &lt;code&gt;pi.doty.dev&lt;/code&gt; routed to my raspberry pi. So anyone could type &lt;code&gt;ssh temp@pi.doty.dev&lt;/code&gt; and enter password temp and they'd have access to my entire Raspberry Pi. &lt;/p&gt;

&lt;p&gt;Whoops.&lt;/p&gt;

&lt;p&gt;According to the Verizon Notice, the Mirai malware can be removed if the device is pulled from the network and power cycled. After multiple attempts I noticed that definitely did not work for me, so I ended up erasing the entire thing using the &lt;a href="https://github.com/raspberrypi/noobs" rel="noopener noreferrer"&gt;NOOBS Recovery System&lt;/a&gt; and everything now works like a charm.&lt;/p&gt;

&lt;p&gt;Now, with a clean-slate Raspberry Pi, I decided I should look into steps I can take so this doesn't happen again (beyond being just a total idiot). Here are some basic steps you can take to protect your Raspberry Pi, or any generic Linux system with an open network:&lt;/p&gt;

&lt;p&gt;Prologue: Obviously, don't do all the stuff I did unless you &lt;strong&gt;absolutely&lt;/strong&gt; know what you're doing.&lt;br&gt;
Don't port forward port 22 on your router.&lt;br&gt;
Don't make a user &lt;code&gt;temp&lt;/code&gt; with a password of &lt;code&gt;temp&lt;/code&gt; on any machine that touches the internet.&lt;br&gt;
Check in on your devices every once in a while to make sure they're not downloading gigabytes of data a day for no reason.&lt;/p&gt;

&lt;p&gt;First: Change the default username and password &lt;em&gt;immediately&lt;/em&gt; upon using your Pi and connecting it to the internet. It should require that you do this, because pi/raspberry is not a secure combo, especially when everyone knows it.&lt;/p&gt;

&lt;p&gt;Next: Make sure your SSH is disabled by default, unless you &lt;em&gt;absolutely&lt;/em&gt; need it. If you're using the Pi locally to tinker, you probably don't need SSH turned on.&lt;/p&gt;

&lt;p&gt;Next: IF at all possible, use SSH keys for login and &lt;em&gt;ONLY SSH keys&lt;/em&gt;. This makes being brute-forced virtually impossible, as only you will have the private key to use. Doing this ensures that you're only likelihood of being compromised is if your host machine is stolen or compromised itself. &lt;a href="https://blog.vpscheap.net/the-importance-of-ssh-keys-and-how-to-create-them/" rel="noopener noreferrer"&gt;This article&lt;/a&gt; talks about the importance of using SSH keys. If your device needs to be publicly available, you likely cannot disable password login. Nonetheless, you can disable it via editing &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; and setting the value to no:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PasswordAuthentication no&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This field might be yes or commented out, or both. Make sure to uncomment it if so as yes is the default value.  If you run into issues this &lt;a href="https://raspberrypi.stackexchange.com/questions/46994/how-do-i-do-ssh-passwordless-login-with-raspberry-pi" rel="noopener noreferrer"&gt;StackExchange&lt;/a&gt; post has some useful information.&lt;/p&gt;

&lt;p&gt;If you're going to open up SSH to the world, expect brute force attacks. Make sure you're using secure passwords. I'd highly recommend using a password manager for everything. Otherwise, make sure it has some length to it - the longer, the better. &lt;a href="https://www.howtogeek.com/195430/how-to-create-a-strong-password-and-remember-it/" rel="noopener noreferrer"&gt;Check out this guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If SSH is open, you'll need to edit your &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;. Here are some settings that will help:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;PermitRootLogin no&lt;br&gt;
AllowUsers your_username&lt;br&gt;
LoginGraceTime 30&lt;br&gt;
MaxAuthTries 1&lt;br&gt;
MaxStartups 2&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you want to read more on some smart options to choose when using openSSH, I highly recommend &lt;a href="https://www.thegeekstuff.com/2011/05/openssh-options/" rel="noopener noreferrer"&gt;this article&lt;/a&gt; and reading the &lt;a href="https://www.raspberrypi.org/documentation/configuration/security.md" rel="noopener noreferrer"&gt;official guide&lt;/a&gt; from raspberrypi.org.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>security</category>
    </item>
  </channel>
</rss>
