To ensure secure password resets and prevent attackers from gaining control over user accounts.
defmodule MyAppWeb.PasswordResetController do
use MyAppWeb, :controller
def create(conn, %{'email' => email}) do
user = Accounts.get_user_by_email(email)
Accounts.deliver_password_reset_instructions(user, conn)
send_resp(conn, :ok, "")
end
end
defmodule MyApp.Accounts do
def deliver_password_reset_instructions(%User{} = user, conn) do
url = MyAppWeb.Router.Helpers.password_reset_url(conn, :edit, user.reset_password_token)
MyApp.Mailer.deliver_password_reset_instructions(user.email, url)
end
end
The code generates a password reset link using the host provided in the request headers. An attacker can manipulate the request headers to provide a host that they control, resulting in the application generating a reset link that points to the attacker's host.
defmodule MyAppWeb.PasswordResetController do
use MyAppWeb, :controller
def create(conn, %{'email' => email}) do
user = Accounts.get_user_by_email(email)
Accounts.deliver_password_reset_instructions(user)
send_resp(conn, :ok, "")
end
end
defmodule MyApp.Accounts do
def deliver_password_reset_instructions(%User{} = user) do
host = Application.get_env(:my_app, MyAppWeb.Endpoint)[:url][:host]
url = MyAppWeb.Router.Helpers.password_reset_url(MyAppWeb.Endpoint, :edit, user.reset_password_token, host: host)
MyApp.Mailer.deliver_password_reset_instructions(user.email, url)
end
end
The code now generates the password reset URL using a trusted host value from the application's configuration, instead of the host provided in the request headers. This prevents an attacker from manipulating the reset URL to point to their own host.