
The Danger of Exposing Localhost to Internet
You definitely don’t want to invite a thief or criminal to your house. The same goes for your machine. Exposing localhost to internet may put you at a high risk. This article will explain why it is dangerous and how to mitigate the risks.
If you’re developing an application that requires a webhook; for example, Github webhooks, JIRA webhooks, Slack application, you probably want to use a local tunneling service that can expose your localhost to internet through a public-accessible domain name. So that you can test your software easily without deploying your code to a server. This technique reduces turn around time substantially. It’s an undoubtedly useful service that can boost the development speed.
There are many local tunneling services out there, and the popular one that you might have come across is ngrok. I’ll use ngrok as an example because it’s one of the early local tunneling services. It was born in 2013 which is the same year that Slack was launched to the public. The service integration was a hot topic back then, and the market is huge today. As of today, there are more than 2,400 Slack apps in the marketplace, and many more that are not listed in the marketplace. I believe Ngrok is part of this success.
Disclaimer: This article is based on my speculation. I didn’t perform an actual attack since it is illegal to do so. Ngrok is used as an example, but it might be more secure than other services with similar functionality.
Most Local Tunneling Services Do Not Hide Your URL
Most local tunneling services use a wildcard subdomain. Usually, it will be a random string. For a premium service, you may be able to create your own subdomain that is easy to remember.

I’m using the free version of ngrok which does not allow me to have my own subdomain. If you’re using a different version of ngrok, the pattern of the subdomain may be different. For my ngrok, the subdomain contains 5 groups of strings. The first group is a 5-characters-long random alphanumeric string. The rest is my IP address assigned by my internet provider.
TIP: You can query your own IP address using Google: https://www.google.com/search?q=my+ip
While the service support both http and https, only https is recommended. So that no one can eavesdrop or tamper your HTTP payload.
Do not Use an Unencrypted HTTP Ever
I know it’s pretty basic, but I have to mention it anyway. Don’t leave me yet!
When you work at a coffee shop, you’re using an untrusted network. Your traffic may be monitored by attackers. Using Ngrok’s HTTP URL will disclose your payload. The payload may contain sensitive information such as your personal information and your credentials. I don’t have to tell you how bad it is when your invaluable information get into bad guys hands.
Using HTTPS Does Not Hide Your Server
Let’s take a look at the domain https://7607–172-x-xxx-xxx.ngrok.io
It’s true that HTTPS encrypts everything. Your HTTP body along with headers, method, and URL (the full URL including domain, paths, and query) will be encrypted as a part of SSL/TLS protocol. The domain though could be disclosed during the IP resolution or the initial TLS handshake. Your HTTP client (your web browser included) has to ask DNS for an IP address representing a domain name. This process may not be encrypted depending on the DNS server you are using. There are several factors that can compromise the confidentiality of the address of your server.
You can check how secure your DNS is here: https://www.cloudflare.com/ssl/encrypted-sni/#results

I used the curl command to call the https endpoint provided by ngrok. From the Wireshark screenshot above, we can tell that the curl command uses TLS 1.2, and you still can see the server_name clearly in plaintext.
Why do We Need to Hide an Address? Why is It a Big Deal?
We all know google.com and a ton of other websites are not hiding their addresses. On the contrary, they want everybody to know them. Why we need to hide our address then?
You have to know the fact that those services are operating behinds firewalls, and sophisticated networks. The services are running with very limited permissions just enough to function properly. Those systems are designed and maintained by experts. Even a hacker somehow gain access to a server, the damage is usually limited. Those services get security patches as quick as a few hours to a few days.
Your local server does not have that level of security, and there is almost nothing to protect you from a well-crafted malicious HTTP request coming from a hacker. You are not probably running your local server with the latest patch because you may think that your application is just in a development phase or you are just working on a toy project.
Have you heard of Log4j 2 vulnerability that allows an attacker to perform remote code execution (RCE) just by logging a certain string? If not, here you go CVE-2021–44228 and the second vulnerability CVE-2021–45046. The working proof-of-concept code is published here. You can try it yourself.
If that is not bad enough, another critical vulnerability was report on March 31st, 2022 (just a few days ago). Security researchers found a new high vulnerability (Spring4shell CVE-2022–22965) on the Spring framework, one of the most popular Java frameworks, that leads to remote code execution. An attacker can execute arbitrary code on a compromised target. If you’re using Spring, and you haven’t updated the library for a couple weeks from now, the chance is your system is susceptible to this vulnerability.
Imagine that a hacker can download anything from your machine. If I was a hacker, the first thing I’d look for would be ~/.ssh/id_rsa
. I believe many people do not protect their ssh key with a passphrase because it’s not convenient which makes it more convenient for a hacker. A hacker might try installing a keylogger, but the system should prompt you for a permission. So, this is not a problem unless you are a type of person who types a password when the system asks you to do so without reading what it asks for. Then, that is a problem. A huge one at that. I’m just giving you some examples about what could happen when your machine is compromised through RCE. A real attacker is usually more creative than me.
You can see that attacking individual is much easier than attacking a well tested website. This is based on the assumption that you may be running a security compromised service, and expose the security hole to the internet without realizing it.
Finding a Target is Easier Than You Thought
Previously, I mentioned about a domain name and a subdomain, which is used as your local tunnel identification, is not always encrypted. Still, it’s not easy to exploit that since it needs a lot of effort and preparation.
There is a better way to find who are exposing their local servers to the internet. We can just do it by brute force!
We can analyze the pattern of the subdomain; for instance, my ngrok shows me xxxx-ddd–ddd–ddd–ddd.ngrok.io
.
The first group comprises of alphanumeric characters ([a-z0–9]{4}). There will be 1,679,616 (36 * 36 * 36 * 36) possible combinations. ddd-ddd-ddd-ddd
is an IP address. It’s not difficult to find the public IP ranges of internet providers. Therefore, you don’t have to run all the possible IPs in the world. It’s totally doable to find online users this way.
Some services use a name generator instead of a random string. If you know the algorithm or the source of the names, finding a running server may be faster than grabbing a coffee when your name got called.
How to Mitigate the Risk?
Always Use HTTPS with Password Protected
Ngrok is generally secure, but that depends solely on you. Ngrok supports both HTTPS endpoint and authentication. You should use them both. We already knew why HTTPS is very important. We can add an additional security to our endpoint using a password. To leverage the password protection feature, you need to create an account with ngrok. Don’t worry, this feature is available for free users as well.
To enable the authentication, you have to use -auth
switch.
ngrok http -auth="username:password" 8080
Your endpoint will be protected by the Basic Auth. If you don’t provide a username and password in a Basic Auth format, the request will fail with 401 Unauthorized.
The Base Auth format is
"Basic " + base64(username + ":" + password)
Assuming that your username is “username”
and your password is “password”
, your request with basic auth will look like the following:
curl --location --request GET 'xxx.ngrok.io' \--header 'Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ='
The problem is that most webhooks do not support a custom header.

The above screenshot is Slack Events subscription. There is only one field. How can we pass our credentials to the URL?
We can use the userinfo subcomponent of the Uniform Resource Identifier (URI) as defined in RFC3986 Section-3.2.1, and the URL will look like the following.
https://username:password@xxx.ngrok.io/events
A username and password will be used as Basic Auth by the HTTP client of the server that provides this webhooks. This is not guaranteed, and this field is deprecated as stated in the RFC. You have to test it with the webhooks you intend to use before to make sure that it works. I have tested with Slack and curl, and they both worked.
The passing of authentication information in clear text has proven to be a security risk in almost every case where it has been used.
— RFC3986 Section-3.2.1
Note that the username and password that you pass through a URI will be visible in cleartext. It will be encrypted as part of SSL/TLS protocol though. The userinfo is not part of a domain. As a result, it won’t be transmitted in a cleartext at any given time.
While it’s generally a bad practice to store a password in cleartext, we are not using password as a password in this case. I would like you to think about a password here as an extra bits to make the URL harder to guess. It should be secret to anyone outside of your team. Even you use a simple and stupid username and password like dumb:ch1ck3n
. It is still very hard to guess. I wish Ngrok returns 404 or other codes instead of giving a hint that the password is required by returning 401.
Run Your Application In a Docker
Running your application in a docker container may make it more difficult to hack because that is another layer that a hacker needs to go through. You have to weight between the development turnaround time, and the benefit that this extra layer provides. However, you should not put too much trust to a container because it’s also breakable. A hacker still can escape your docker and gain root access to the host machine by leveraging a privilege escalation.
Always Update Your Libraries
I know you might be working on your for-fun side project, and that project is not intended to deploy anywhere. As soon as you expose your local server to the internet, it’s exactly the same as deploying to an actual server to the cloud. The difference is you won’t think about securing your application much when you are running it locally.
Use Your Own Solution
This can be very secure because it’s not a public service that anyone knows about. If you believe this statement, you’re deceiving yourself. Your own solution could be worse in terms of security compared to other services out there. There are many open source projects that you can use instead of coding it yourself.
Check out this list: https://github.com/anderspitman/awesome-tunneling
Even though you choose to run one of the popular open source, you probably need to have a security expert to review the code and your configuration to make sure that it is secure enough. Also, don’t forget to keep the software updated regularly. The number of stars on Github doesn’t indicate the level of security. It just means a lot of people are using it. If you were a hacker, you would know that the more users means the more targets.
Is Blocking Ngrok and Other Local Tunneling Services a Solution?
Ngrok and other local tunneling may be blocked by your company network. The reasons are varied. Security people won’t be happy when data are moving freely both ingress and egress through a 3rd party service that they don’t know what is behind the nice domain name.
In addition, ngrok is used by hackers to deliver phishing attacks. Ngrok can bypass a firewall, and it uses a random temporary subdomain which makes it hard to detect. Hackers see this as an opportunity to create a server that can deliver a malicious code to any one who clicks the URL in a phishing email. Read more about the research on this topic here.

Are these legit reasons to block ngrok and friends? Probably yes. But, it could cause an adverse affect. Think about who will be the main users of ngrok. Yes, it will be developers. These are power user. If you don’t provide them a solution, they will find a solution themselves. To be honest, not all developers care about security. Some of us find that security is just an annoying thing that gets in our way. Now you know how dangerous the situation becomes when everyone tries to find their own solution in their own ways. Blocking alone is not an answer. We have to provide an alternative approach that is secure and auditable.
I’m currently working on the local tunneling project focusing on security and ease of use. It will be written in Rust and open source. I will write up a tutorial on how to set it up in a public cloud like AWS, and how to scale it out. Follow me if you’re interested, and I’ll announce through this medium.
Thank you for reading. Stay safe and do not invite a stranger to your home.