Логинимся на сайт под чужим аккаунтом не имея пароля

в 19:51, , рубрики: .net, ASP, asp.net mvc, support, Веб-разработка, метки: , ,

Логинимся на сайт под чужим аккаунтом не имея пароля

День добрый, сегодня я расскажу про довольно полезный прием при поддержке крупный сайтов-сервисов. А именно, возможность зайти на ваш сайт под обычным пользователем, не имея пароля (вы же не храните пароли в БД открытым текстом верно?).

Введение

Предположим у вас уже есть готовый, запущенный, живущий бурной жизнью веб сайт. И так уж сложилось что для разных пользователей он выглядит и работает по разному, например в зависимости от ролей, политик безопасности и прочей бизнес логики. Для простоты примера пусть будет интернет банкинг.

Задача

Дать возможность администраторам или, например операторам тех-поддержки как-бы войти под учетной записью конкретного пользователя. Например, намного проще объяснить дозвонившемуся в колл-центр пользователю решение проблемы видя сайт его глазами.

Решение

Для примера я возьму ASP.NET MVC, хотя поклонникам долларовых знаков и драгоценных камней реализовать что-то похожее на любимом фреймворке не должно составить труда. За код сильно не бейте – писался четыре года назад как пример.

В мире Windows Authentication есть уже готовое красивое решение под названием Impersonation. Т.е. когда один пользователь как-бы прикидывается другим, получая его права и возможность выполнять действия от его имени.

Все хорошо, вот только наш гипотетический интернет банкинг использует Forms Authentication т.е. в web.config написано что-то типа <authentication mode=«Forms» (для не посвященных: вход реализован как обычная веб форма для ввода логина и пароля). В таком режиме воспользоваться плюшками Impersonation не получиться, но мы можем попробовать реализовать что-то подобное.

Для начала добавим в AccountController.cs (создается с дефолтным проектом ASP.NET MVC) следующий метод:

public ActionResult Impersonate(string user)
{
    //checking if user is logged in
    if (Request.IsAuthenticated)
    {
        //and checking if he is a super user
        if (Roles.IsUserInRole("admins"))
        {
            //saving current auth cookie for a later use
            HttpCookie cookie = Request.Cookies[FormsAuthentication.FormsCookieName];
            cookie.Name = "__" + cookie.Name;
            Response.Cookies.Add(cookie);

            //logging out for current user
            FormsAuthentication.SignOut();

            //making new user logged in without a password
            FormsAuthentication.SetAuthCookie(user, true);

            //redirect to some page here
            return RedirectToAction("Index", "Home");
        }
    }

    //currently logged user has no rights to impersonate
    throw new SecurityException();
}

Вся суть в том, что бы залогинить текущего пользователя под другим именем, проверив входит ли он в роль Администраторов (или например «Операторы тех-поддержки 3го уровня») не проверяя правильность ввода пароля. Для этого мы выдадим ему новую соответствующую печеньку авторизации, а старую переименуем, что бы можно было вернуться в режим администратора без повторного ввода пароля.

По шагам это будет выглядеть так:

  1. Проверяем залогинен ли пользователь
  2. Проверяем достаточно ли у него прав, что бы прикинуться кем-то другим
  3. Сохраняем текущую печенюху сессии под другим именем
  4. Создаем новую для другого пользователя, точно также как мы это делаем при обычном логине после проверки пароля (т.е. после Membership.ValidateUser())
  5. Редиректимся например на главную страницу

В итоге браузер получит уже две печеньки а сервер будет воспринимать текущую сессию как сессию обычного пользователя.

Для того что бы вызвать только что созданный метод нам понадобиться новая страница со списком пользователей. Полный листинг приводить не буду, думаю, идея понятна:

using (Html.BeginForm("Impersonate", "Account"))
{
   var users = Membership.GetAllUsers(); // or Roles.GetUsersInRole(...)
   foreach (var userName in users)
   {
      ...
   }
}

Итого после выбора необходимого пользователя администратор временно превращается в обычного пользователя.

Осталось только добавить возможность вернуться в режим администратора. Внешний вид сайта изменять мы не хотим добавляя новые элементы, поэтому воспользуемся уже существующей кнопкой выхода. Для этого немного расширим существующий метод выхода (в AccountController.cs):

public ActionResult LogOff()
{
    //checking if we impersonated already
    HttpCookie cookie = Request.Cookies["__" + FormsAuthentication.FormsCookieName];
    if (null != cookie)
    {
        //logging off
        FormsAuthentication.SignOut();

        //restoring original auth cookie of a super user
        cookie.Name = FormsAuthentication.FormsCookieName;
        Response.Cookies.Add(cookie);

        //removing previosly saved auth cookie of a super user so we can logout normaly next time
        HttpCookie expired = new HttpCookie("__" + FormsAuthentication.FormsCookieName);
        expired.Expires = DateTime.Now.AddDays(-1);
        Response.Cookies.Add(expired);
    }
    else
    {
        //logging off in a ordinary fashion
        FormsAuth.SignOut();
    }

    //redirecting
    return RedirectToAction("Index", "Home");
}

В начале проверяем существует ли заранее сохраненная печенька администратора. Если да то переименовываем ее обратно, а про обычного пользователя забываем. В итоге при нажатии кнопки выхода, администратор вернется в свое привычное окружение. А нажав на кнопку выхода еще раз, он разлогиниться полностью.

Конечный результат

Логинимся на сайт администратором
Логинимся на сайт под чужим аккаунтом не имея пароля

Выбираем пользователя, которым мы хотим стать
Логинимся на сайт под чужим аккаунтом не имея пароля

Сайт думает что мы обычный пользователь
Логинимся на сайт под чужим аккаунтом не имея пароля

Если посмотреть на сохраненные печеньки в браузере, можно увидеть оба токена
Логинимся на сайт под чужим аккаунтом не имея пароля

После нажатия на Log Off, возвращаемся к режиму администратора
Логинимся на сайт под чужим аккаунтом не имея пароля

Соответственно печеньки восстановлены
Логинимся на сайт под чужим аккаунтом не имея пароля

Если нажать Log Off еще раз, выходим полностью
Логинимся на сайт под чужим аккаунтом не имея пароля

Заключение

Итого с минимум изменений мы добавили возможность на время прикинуться другим пользователем. Причем, даже не меняя внешний вид и логику сайта. Для остальной части нашего веб-приложения такие манипуляции абсолютно прозрачны и не должны ничего поломать.

Осталось только продумать логику кому кем можно прикидываться. Что бы, например оператор тех поддержки не мог войти как администратор.

Интересно узнать, может кто-то уже реализовывал похожую схему другими способами? Отписывайтесь в комментариях.

Автор: berlox

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js