Elixir’s Phoenix Security: Token Auth & CSRF Prevention

Avatar

By squashlabs, Last Updated: June 21, 2023

Elixir’s Phoenix Security: Token Auth & CSRF Prevention

What is Elixir?

Elixir is a functional, concurrent programming language designed for building scalable and maintainable applications. It runs on the Erlang virtual machine (BEAM) and leverages the Erlang ecosystem, making it a useful tool for creating distributed systems. Elixir's syntax is inspired by Ruby, making it easy to learn and read.

Related Article: Internationalization & Encoding in Elixir Phoenix

What is the Phoenix Framework?

Phoenix is a web development framework built with Elixir that follows the model-view-controller (MVC) architectural pattern. It provides a robust set of tools and conventions for building high-performance, fault-tolerant, and real-time web applications. Phoenix embraces the "convention over configuration" principle, allowing developers to focus on application logic instead of boilerplate code.

What is Guardian in the Context of Phoenix?

Guardian is a token-based authentication library for Phoenix applications. It provides a flexible and secure way to handle user authentication by using JSON Web Tokens (JWT). With Guardian, developers can easily implement token-based authentication and authorization in their Phoenix applications.

How Does Token-based Authentication Work?

Token-based authentication involves issuing a token to a user upon successful login, and then using that token to authenticate subsequent requests. This eliminates the need for sessions and cookies, making it a stateless authentication mechanism.

To implement token-based authentication in Phoenix with Guardian, follow these steps:

1. Install the Guardian package by adding it to your mix.exs file and running mix deps.get:

defp deps do
  [
    {:guardian, "~> 2.0"}
  ]
end

2. Generate a secret key for signing and verifying tokens. This key should be kept secret and not shared with anyone:

# Generate a secret key
config :my_app, MyApp.Guardian,
  secret_key: "your_secret_key"

3. Create a module that implements the Guardian behavior. This module will be responsible for encoding and decoding tokens, as well as handling authentication and authorization logic:

defmodule MyApp.Guardian do
  use Guardian, otp_app: :my_app
  
  def subject_for_token(user, _claims) do
    {:ok, to_string(user.id)}
  end
  
  def resource_from_claims(claims) do
    user_id = String.to_integer(claims["sub"])
    {:ok, MyApp.Accounts.get_user(user_id)}
  end
end

4. Protect the routes that require authentication by adding a :require_authenticated_user plug to your Phoenix router:

pipeline :auth do
  plug Guardian.Plug.VerifyHeader, realm: "Bearer"
  plug Guardian.Plug.EnsureAuthenticated, handler: MyApp.Guardian
end

scope "/api", MyApp do
  pipe_through :auth
  
  # Routes that require authentication
  get "/users", UserController, :index
  post "/users", UserController, :create
  # ...
end

5. Generate tokens for authenticated users upon successful login and include them in the response:

defmodule MyAppWeb.SessionController do
  use MyAppWeb, :controller
  
  def create(conn, %{"email" => email, "password" => password}) do
    case MyApp.Accounts.authenticate_user(email, password) do
      {:ok, user} ->
        token = Guardian.encode_and_sign(user, MyApp.Guardian, :access)
        conn
        |> put_status(:ok)
        |> put_resp_header("authorization", "Bearer #{token}")
        |> render("create.json", %{token: token})
      
      {:error, _} ->
        conn
        |> put_status(:unauthorized)
        |> render("error.json", %{message: "Invalid email or password"})
    end
  end
end

Now, when a user successfully logs in, they will receive a token that they can include in subsequent requests to authenticate themselves.

Related Article: Phoenix with Bootstrap, Elasticsearch & Databases

How Can I Prevent CSRF Attacks in Phoenix?

Cross-Site Request Forgery (CSRF) attacks occur when an attacker tricks a user into performing unintended actions on a website without their consent. To prevent CSRF attacks in Phoenix, you can use a combination of techniques including CSRF tokens and security headers.

To implement CSRF prevention in Phoenix, follow these steps:

1. Enable CSRF protection in your Phoenix application by adding the Plug.CSRFProtection plug to your router:

defmodule MyAppWeb.Router do
  use MyAppWeb, :router
  
  pipeline :browser do
    # ...
    plug Plug.Session, store: :cookie
    plug Plug.CSRFProtection
    # ...
  end
  
  # ...
end

2. Include a CSRF token in your forms to protect against CSRF attacks. You can use the get_csrf_token/1 helper function provided by Phoenix:

 %>
  
  # ...

3. Verify the CSRF token on the server-side by adding the Plug.CSRFProtection plug to the relevant routes:

pipeline :api do
  # ...
  plug Plug.CSRFProtection, csrf_token: true
  # ...
end

These steps will help protect your Phoenix application from CSRF attacks by validating the authenticity of requests using CSRF tokens.

What Are Security Headers and How Do They Enhance Security?

Security headers are HTTP headers that provide additional security measures for web applications. They can help protect against various types of attacks, such as XSS (Cross-Site Scripting), clickjacking, and MIME type sniffing.

Phoenix provides built-in support for adding security headers to HTTP responses. You can configure these headers in your Phoenix application's endpoint module:

defmodule MyAppWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_app
  
  # ...
  
  plug Plug.SSL
  plug Plug.SecurityHeaders, [
    # List of security headers and their configurations
    {:content_security_policy, "default-src 'self'; script-src 'self' 'unsafe-inline' cdn.example.com"},
    :strict_transport_security,
    :x_xss_protection,
    :x_content_type_options
  ]
  
  # ...
end

In the example above, we have added the content_security_policy, strict_transport_security, x_xss_protection, and x_content_type_options headers to our Phoenix application.

These security headers enhance the security of your Phoenix application by enforcing stricter policies for content loading, enabling SSL/TLS, protecting against XSS attacks, and setting the content type options.

How Can I Secure Real-time Functionalities in Phoenix Channels?

Phoenix Channels provide a useful mechanism for building real-time functionalities in web applications. However, it's essential to ensure the security of these functionalities to prevent unauthorized access and data breaches.

To secure real-time functionalities in Phoenix Channels, consider the following measures:

1. Authorize channel connections: Implement a custom channel authorization function that verifies the user's credentials and permissions before allowing them to connect to a channel. You can use Guardian or any other authentication library to authenticate and authorize users.

2. Use channel-specific authorization: In addition to authorizing channel connections, you can also implement channel-specific authorization to control access to specific channels or channel topics. This ensures that only authorized users can subscribe to and receive data from specific channels.

3. Validate and sanitize user input: Real-time functionalities often involve user input, such as chat messages or form submissions. It's crucial to validate and sanitize this input to prevent security vulnerabilities like cross-site scripting (XSS) or SQL injection attacks. Phoenix provides tools like Phoenix.HTML.Form and Phoenix.HTML.Sanitize to help with input validation and sanitization.

4. Implement rate limiting: Real-time functionalities can be susceptible to abuse, such as excessive message sending or connection attempts. Implementing rate limiting mechanisms helps prevent abuse and ensures fair usage of resources. You can use libraries like ex_redis_rate_limiter or throttle_ecto for rate limiting in Phoenix.

5. Encrypt sensitive data: If your real-time functionalities involve transmitting sensitive data, such as user credentials or personal information, ensure that this data is encrypted during transmission. Phoenix Channels use secure WebSocket connections by default, but you should also consider encrypting any sensitive data stored on the server.

What Are the Benefits of Using Phoenix Channels for Real-time Communication?

Phoenix Channels provide several benefits for real-time communication in web applications:

1. Scalability: Phoenix Channels are built on top of the Erlang virtual machine (BEAM) and leverage its concurrency model. This allows Phoenix applications to handle a large number of concurrent connections and scale horizontally with ease.

2. Fault tolerance: Phoenix Channels are designed to be fault-tolerant. If a connection or process fails, Phoenix can automatically reconnect and resume the session without losing any data. This ensures a smooth user experience even in the presence of network issues or server failures.

3. Bi-directional communication: Phoenix Channels enable bi-directional communication between the client and the server. This allows real-time updates, such as live chat, notifications, or collaborative editing, to be delivered instantly to all connected clients.

4. Presence tracking: Phoenix Channels provide built-in support for presence tracking, which allows you to track the presence (online/offline status) of users in real-time. This is useful for building features like user presence indicators or online user lists.

5. Efficient data transmission: Phoenix Channels use WebSockets for communication, which provides a low-latency, full-duplex connection between the client and the server. This allows for efficient data transmission and reduces the overhead associated with traditional HTTP request-response cycles.

Related Article: Integrating Phoenix Web Apps with Payment, Voice & Text

What Are the Common Security Considerations When Developing with Phoenix?

When developing with Phoenix, it's important to consider the following security aspects:

1. Authentication and authorization: Implement secure authentication and authorization mechanisms to ensure that only authorized users can access protected resources. Use token-based authentication with Guardian or other authentication libraries to handle user authentication securely.

2. Input validation and sanitization: Validate and sanitize all user input to prevent security vulnerabilities like cross-site scripting (XSS) or SQL injection attacks. Phoenix provides tools like Phoenix.HTML.Form and Phoenix.HTML.Sanitize to help with input validation and sanitization.

3. Secure session management: If you use session-based authentication, ensure that session data is stored securely and cannot be tampered with or hijacked. Use secure cookies and configure appropriate session expiration lengths to minimize the risk of session-related attacks.

4. Cross-Site Request Forgery (CSRF) prevention: Implement CSRF protection by including CSRF tokens in forms and verifying them on the server side. Use the Plug.CSRFProtection plug provided by Phoenix to add CSRF protection to your application.

5. Secure password storage: When storing user passwords, always use a secure password hashing algorithm like bcrypt or argon2. Never store passwords in plaintext or using weak hashing algorithms like MD5 or SHA-1.

6. Secure data transmission: Use HTTPS with SSL/TLS encryption to ensure secure data transmission between the client and the server. Phoenix provides built-in support for SSL/TLS with the Plug.SSL plug.

7. Use security headers: Configure security headers to enhance the security of your Phoenix application. These headers can help protect against various types of attacks, such as XSS, clickjacking, or MIME type sniffing. Phoenix provides the Plug.SecurityHeaders plug for adding security headers to HTTP responses.

8. Regularly update dependencies: Keep your Phoenix application and its dependencies up to date to benefit from the latest security patches and bug fixes. Regularly review and update your application's dependencies to ensure that you are not using outdated or vulnerable libraries.

How Can I Implement Secure Authentication in Phoenix?

To implement secure authentication in Phoenix, follow these best practices:

1. Use strong password hashing algorithms: When storing user passwords, always use a strong password hashing algorithm like bcrypt or argon2. These algorithms are designed to be slow and computationally expensive, making it difficult for attackers to crack passwords using brute-force or dictionary attacks.

2. Salt and hash passwords: Salt and hash user passwords before storing them in the database. Salting involves adding a random value (salt) to the password before hashing it. This adds an extra layer of security and prevents attackers from using precomputed rainbow tables to crack passwords.

3. Implement password policies: Enforce strong password policies to ensure that users choose secure passwords. This includes requiring a minimum password length, enforcing the use of uppercase and lowercase letters, numbers, and special characters. You can use libraries like comeonin or argon2_elixir to implement password policies and handle password hashing in Phoenix.

4. Use secure session management: If you use session-based authentication, ensure that session data is stored securely and cannot be tampered with or hijacked. Use secure cookies with the Plug.Session plug to store session data and configure appropriate session expiration lengths to minimize the risk of session-related attacks.

5. Implement multi-factor authentication (MFA): Consider implementing multi-factor authentication to add an extra layer of security to user accounts. MFA requires users to provide additional verification, such as a temporary code sent to their mobile device, in addition to their password.

6. Protect against account enumeration: Implement measures to protect against account enumeration attacks, where attackers can determine whether a user account exists on your application by exploiting differences in error messages or response times. Ensure that error messages and response times are consistent, regardless of whether a user account exists or not.

7. Monitor and log authentication events: Implement logging and monitoring for authentication events to detect and respond to suspicious activities. Log failed login attempts, successful logins, and other relevant events to identify potential security threats.

Additional Resources



- Commonly Used Security Headers in Phoenix

You May Also Like

Optimizing Database Queries with Elixir & Phoenix

This tutorial delves into the importance of database interactions and application loads in Elixir. Learn how Ecto optimizes database interactions, ex… read more

Exploring Phoenix: Umbrella Project Structures,Ecto & More

The article "Exploring Phoenix: Umbrella Project Structures, Ecto & More" is a deep dive into best practices for umbrella project structures, Liv… read more

Implementing Enterprise Features with Phoenix & Elixir

Implementing Enterprise Features with Phoenix & Elixir offers insights into integrating Single Sign-On, LDAP authentication, and audit trails in … read more

Deployment Strategies and Scaling for Phoenix Apps

Shipping, scaling, and monitoring Phoenix applications can be a complex task. This article provides proven techniques and best practices for deployin… read more

Building Real-Time Apps with Phoenix Channels & WebSockets

Building real-time applications can be a complex task, but with Phoenix Channels and WebSockets in Elixir, it becomes much easier. This article explo… read more

Phoenix Core Advanced: Contexts, Plugs & Telemetry

Delve into advanced Phoenix Core concepts with this article, which explores plug constructs, domain-driven design contexts, and custom telemetry even… read more

Phoenix Design Patterns: Actor Model, Repositories, and Events

Design patterns are essential for building robust and scalable applications. In this article, we will explore various design patterns in Phoenix, suc… read more