How to secure a Phoenix app?

This week i focus to web security. I read blogposts watch videos . As far as i know ecto queries protect us from SQL injection, changesets filters forms, phoenix sanitizes user inputs to prevent XSS. In additon to these never fetch a file from a url param, sanitize inputs in frontends too, never hardcode api keys… Elixir community has alot experienced developers what are some your advices or resources to help me learn more?

2 Likes

See Securing Rails Application. I know it’s neither Elixir nor Phoenix, but the basic ideas about security are the same. You just need to implement these ideas with Elixir/Plug/Phoenix if they are not implemented by default.

3 Likes

Make sure you don’t log sensitive things like passwords or PII. I added opt in logging for query parameters to Phoenix a while ago, where you have to be explicit about which things you allow to be logged.

config :phoenix, :filter_parameters, {:keep, ["id", "order"]}
4 Likes

Also don’t forget…

:023:

5 Likes

@aethereus rails guide is good thanks
@blatyo I never thought about logs, really important thing.
@AstonJ @griffinbyatt having a elixir library is really nice.

2 Likes

“Secure” distribution

If your app is distributed over multiple machines, make sure the nodes communicate over a secure channel like vpn (almost every cloud provider supports some sort of private networking) or tls [1, 2] (if you use several cloud providers for your app and it needs to keep a shared state).

[1] http://erlang.org/doc/apps/ssl/ssl_distribution.html
[2] https://www.erlang-solutions.com/blog/erlang-distribution-over-tls.html

DDoS attack mitigation

or at least an attempt at

Check out [3] and [4]. Or, if you use haproxy (usually a good idea), maybe look into [5]. You can also use haproxy to terminate tls connections, since it would probably do a better job at it than erlang.

Note #1: if you do end up using haproxy for tls termination and/or load balancing, the general advice seems to be to pick a machine with a small number of high frequency cores [6].

Note #2: erlang, and by extension cowboy, are not particularly well suited for serving static assets, especially over tls on linux (freebsd seems to have some support for sendfile over tls [7]), so maybe pick nginx or h2o [8] for it.

[3] https://github.com/michalmuskala/plug_attack
[4] https://news.ycombinator.com/item?id=17061281
[5] https://www.haproxy.com/blog/use-a-load-balancer-as-a-first-row-of-defense-against-ddos/
[6] https://cbonte.github.io/haproxy-dconv/1.8/intro.html#3.5
[7] https://people.freebsd.org/~rrs/asiabsd_2015_tls.pdf
[8] https://h2o.examp1e.net/

“Secure” configs

Some cloud providers have tools like azure key vault [9]. But you can also host hashisorp vault [10] yourself. These are good for storing sensitive configuration information like database credentials and the like.

[9] https://azure.microsoft.com/en-us/services/key-vault/
[10] https://www.vaultproject.io/

6 Likes

A good resource is OWASP. Here’s a checklist of good coding practices:

https://www.owasp.org/index.php/OWASP_Secure_Coding_Practices_Checklist

9 Likes

In addition to the application, there is a lot that you can do to add security when deploying it: https://www.cogini.com/blog/improving-app-security-with-the-principle-of-least-privilege/

Phoenix’s ability to proxy connections efficiently allows some very interesting architectures for better security: https://www.cogini.com/blog/secure-web-applications-with-graphql-and-elixir/

6 Likes

Happily working my way through this thread and all the suggestions and resources posted in it.

It has been a few years since the last posts. Any new suggestions and resources for those of us (including me) that are trying to learn the ins and outs of applied web/Phoenix security in 2022?

1 Like

We are migrating our SaaS application to LiveView and this documentation helped a lot.

https://hexdocs.pm/phoenix_live_view/security-model.html

In our case we use Guardian + Ueberauth.

5 Likes

Don’t forget to secure on a server level too, disable root login or use passwordless authentication, change SSH ports, block unused ports in your firewall, consider installing software like fail2ban/Denyhosts etc.

Usually your host will have tips/guides on how to do it for your OS, or they may even do a lot of it for you.

7 Likes

Also, GitHub - mirego/mix_audit: 🕵️‍♀️ MixAudit provides a mix deps.audit task to scan a project Mix dependencies for known Elixir security vulnerabilities can provide some help.

4 Likes

Using Sobelow and mix_audit were already mentioned, both are great tools.

If your app takes URLs as user input, then does some HTTP request based on them, you should check for server side request forgery (SSRF) It can lead to very bad security incidents, for example it caused the Capitol One breach. There is an open source Elixir library, SafeURL, to help prevent it.

The EEF publishes secure coding and deployment hardening guidelines. Recently Podium released the Elixir Secure Coding Training as a series of LiveBooks as well.

In my experience, Phoenix discourages you from writing code with common web app vulnerabilities (SQL injection, XSS, CSRF). Security incidents still happen, often due to someone attacking the site with a bot:

  • Performing thousands of automated login attempts, using a leaked credential database, to compromise user accounts. This is called credential stuffing, and can be mitigated with 2FA (see NibleTOTP from Dashbit), ensuring password are not being re-used with an Elixir library like ex_pwned, or bot detection.
  • Uses a bot to automate new account creation, then attempts hundreds of purchases using stolen credit cards. The goal of the attacker is to figure out which cards work, it’s called a carding attack.
  • If your app allows users to send emails through a form, for example a project management tool where you can invite people to a project with a brief message, spammers will use this function to send out scam emails. In addition to your users being upset about the spam, your backend email provider may ban your account over this.

Disclosure, I run Paraxial.io, which is a company that blocks attacks like this. Big companies have anti-bot products as well (Google reCaptcha, Cloudflare bot defense), however I would not recommend them.

There’s a number of open source Elixir libraries that can help as well:

I also publish some security related Phoenix posts on the Paraxial.io blog, for example Detecting SQL Injection in Phoenix with Sobelow. If you have questions about this stuff, but don’t want to reply here, feel free to dm me.

12 Likes

Hey there, I use Paraxial and it’s great!

I recently looked at 404s in my logs and found a lot of suspicious requests like these:

Where can I find a more complete list of typical URLs used in exploit scans?

I’m thinking of adding a plug in my app so I can block IPs performing these requests. The idea would be to remember the IP and block it from the plug, and maybe also submit it to a Paraxial’s honeypot. For that, if would be useful to only block them for 24 hours or so, because attackers may be using temporary IPs and I don’t want to ban them forever.

2 Likes

Where can I find a more complete list of typical URLs used in exploit scans?

Just run fail2ban on your server for a couple weeks and check the logs. :smiley:

1 Like

You can make use of tools included in your default OS installation. For example, on FreeBSD there’s PF, a firewall, which can easily be integrated with fail2ban, or blacklistd. Not everything should be built into your service.

I noticed Phoenix project files have database credentials, for example if you Google

phoenix config.exs example

the top result shows putting in a username and password. (It doesn’t actually include one, just a placeholder, but the risk is there of sharing it when sharing a Phoenix project with the world.)

This could possibly be fine for a local application where no one can connect to the database remotely but if you are going to check your Phoenix application into source control, for example to share it with other developers as the example shows, it could make sense to put those values in environmental variables to reduce the risk of sharing your database password, especially for a hosted database solution that isn’t on localhost.

For example I plan to share a complete phoenix app here, my postgresql database will be hosted, so if I shared the app including config file I would end up sharing the admin password to my database.

Instead ChatGPT suggests replacing the lines in config.exs with these lines, I hope it would work:

config :real_world, RealWorld.Repo,
adapter: Ecto.Adapters.Postgres,
username: System.get_env(“DB_USERNAME”),
password: System.get_env(“DB_PASSWORD”),
database: “real_world_dev”,
hostname: “localhost”,
pool_size: 10

Is this correct?

Yes, you would not usually hardcode those values except for in the dev and test environments and that example does show how to get them from Environment Vars. You can also use Dotenvy to use a local .env file, which you can add to .gitignore so it does not get committed. I don’t think you need chatgpt for this one though :slight_smile:

If you make a new app with “mix phx.new,” take a look at runtime.exs. You will see the database URL gets set up with “System.get_env(“DATABASE_URL”)” n the :prod environment, and you’d have to set that environment value for the app to run.

2 Likes

Probably not - using System.get_env in config.exs means that the configuration is read at compile time, not when the system actually starts.

As @Aadmaa points out, the Phoenix generators already output a configuration in config/runtime.exs that uses a DATABASE_URL environment variable to encapsulate user/pass/host/options. That gets loaded at system-start time.

4 Likes

Thanks, both. It’s helpful to know this.