DNS rebinding attacks protection in Rails 6
Tags: rails
Published: 2019-09-02 12:34:53

---
title: DNS rebinding attacks protection in Rails 6
published: true
tags: rails
canonical_url: https://prathamesh.tech/2019/09/02/dns-rebinding-attacks-protection-in-rails-6/
---

It is common practice to run a Rails app using a custom domain locally. `app.lvh.me` is very common. We also use `ngrok.io` sometimes to interact with third party services. If you are using a custom domain on a Rails 6 app, you will see an error with the very first request itself when you hit `app.lvh.me` in the browser.

<!--kg-card-begin: image-->

![](https://prathamesh.tech/content/images/2019/09/Action-Controller--Exception-caught-2019-09-02-11-36-34.png)

<!--kg-card-end: image-->

But if we just try `localhost:3000` then it works. We see this error only when using a custom domain.

### DNS rebinding attack

This error is raised because Rails 6 has added protection against DNS rebinding attacks. But what is DNS rebinding attack in the context of a Rails app running locally?

It is a form of attack where a web page can cause malicious JavaScript code to run against local Rails app by changing the DNS address of the original server to localhost. The malicious script can compromise the system by executing random code against your Rails app running locally.

### Web console 

Rails ships with web-console in development environment to help debug errors while developing the app. web-console gem was vulnerable to the DNS rebinding attacks till Rails 6 as the attacker could steal the session id used by web-console and then make Ajax requests using this session id to execute arbitrary code.

An [example of this attack](https://github.com/nccgroup/singularity/blob/master/html/payloads/rails-console-rce.js) is a script to open calculator app on your system while the Rails app is running locally.

### Guard against DNS rebinding attack in Rails 6

In Rails 6, a new middleware `HostAuthorization` is added which provides a guard against the DNS rebinding errors. The middleware is included in all environments but it gets kicked in development environment by default.

It works as follows:

- For a given incoming request, it checks the value of `config.hosts` . This configuration option is supposed to hold values of the hosts which are allowed by the Rails app. 
- If  `config.hosts` is not empty, then the host of the incoming request is checked against all the allowed values by `config.hosts`. This is done by comparing the `Host` header as well `X-Forwarded-Host` header of the incoming request with the allowed hosts from `config.hosts`.
- If there is at least one match then the request is authorized. But if there is no match then the the `Blocked host` error is thrown that we saw above.

Let's check what is the default value of this option.

<!--kg-card-begin: code-->
```
irb(main):002:0> Rails.application.config.hosts
=> [#<IPAddr: IPv4:0.0.0.0/0.0.0.0>#<IPAddr:IPv6:0000:0000:0000:0000:0000:0000:0000:0000/0000:0000:0000:0000:0000:0000:0000:0000>, 
    ".localhost"]
irb(main):003:0>
```
<!--kg-card-end: code-->

These values make sense as they are only allowing the requests from `localhost` and `0.0.0.0` which are the most likely places from where a request will be made to the Rails app running locally.

So if we are using a custom domain like `app.lvh.me`, we see the above error about host being blocked. The fix is simple, we need to allow `lvh.me` in the `config.hosts`

We can fix it by adding following code in `config/enviroments/development.rb`.

<!--kg-card-begin: code-->
```
config.hosts << '.lvh.me'

```
<!--kg-card-end: code-->

> By prefixing the host with `.` it allows all the subdomains of lvh.me as well. So `api.lvh.me` will also be allowed by above code. It even allows Procs, regular expressions and IPAddr objects as values for `config.hosts`.

### What about production?

In production, the value for `config.hosts` is not set by Rails. So no check is performed by the `HostAuthorization` middleware. If you want to perform the header check then you will have manually add the allowed domain to the `config.hosts`.

<!--kg-card-begin: code-->
```
config.hosts << '.myawesomeapp.io`
```
<!--kg-card-end: code-->
### Road to Rails 6

Want to know more about such small but important changes which are part of Rails 6 and which affect your day to day development? Subscribe to my [newsletter](https://prathamesh.tech/road-to-rails-6) and be on the course for making your app Rails 6 ready.