To prevent denial of service by repeatedly changing user's password.
defmodule MyAppWeb.AccountController do
use MyAppWeb, :controller
def change_password(conn, %{"password" => password, "user_id" => id}) do
user = Repo.get!(User, id)
changeset = User.changeset(user, %{password: password})
Repo.update!(changeset)
render(conn, "password_changed.html")
end
end
This code is vulnerable because it allows a user's password to be changed without any limit on the number of requests. If an attacker is able to authenticate as a user, they can lock the user out of their account by repeatedly changing the password.
defmodule MyAppWeb.AccountController do
use MyAppWeb, :controller
def change_password(conn, %{"password" => password, "user_id" => id}) do
user = Repo.get!(User, id)
if can_change_password?(user) do
changeset = User.changeset(user, %{password: password})
Repo.update!(changeset)
update_last_password_change(user)
render(conn, "password_changed.html")
else
render(conn, "password_change_limit.html")
end
end
defp can_change_password?(user) do
# check if user can change password
end
defp update_last_password_change(user) do
# update the time of last password change
end
end
In this secure code example, the
can_change_password?
function checks if the user has already changed their password within the rate limit period. If they have, the password change request is rejected and a different view is rendered.