FormsAuthentication.SignOut () не выходит из системы

143

Слишком долго разбивал мою голову от этого. Как запретить пользователю просматривать страницы сайта после того, как они вышли из системы с помощью FormsAuthentication.SignOut? Я ожидаю, что это сделать это:

FormsAuthentication.SignOut();
Session.Abandon();
FormsAuthentication.RedirectToLoginPage();

Но это не так. Если я введу URL-адрес напрямую, я все равно смогу перейти на страницу. Я давно не пользуюсь защитой по собственной инициативе, поэтому я забываю, почему это не работает.

Джейсон
источник
Этот код в порядке ... нажатие в браузере не возвращает страницу на сервер, а просто перезагружает локальную кэшированную версию страницы. Все приведенные ниже решения, кажется, игнорируют этот факт и на самом деле не делают ничего больше, чем вы делаете здесь. Короче говоря, нет ответа на этот вопрос, который решит пользователя, смотрящего на его кеш, поскольку на сегодняшний день я не верю, что есть способ очистить кеш, скажем, в js или с помощью инструкции на стороне сервера.
Война
Этот ответ предлагает несколько способов проверки, особенно если ваш сайт не проходит тесты PEN: stackoverflow.com/questions/31565632/…
Тайлер С. Лопер

Ответы:

211

Пользователи по-прежнему могут просматривать ваш веб-сайт, потому что куки не очищаются при вашем звонке, FormsAuthentication.SignOut()и они аутентифицируются при каждом новом запросе. В документации MS написано, что cookie будет очищен, но это не так, ошибка? Это точно так же, какSession.Abandon() cookie все еще там.

Вы должны изменить свой код на это:

FormsAuthentication.SignOut();
Session.Abandon();

// clear authentication cookie
HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie1.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie1);

// clear session cookie (not necessary for your current problem but i would recommend you do it anyway)
SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState");
HttpCookie cookie2 = new HttpCookie(sessionStateSection.CookieName, "");
cookie2.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie2);

FormsAuthentication.RedirectToLoginPage();

HttpCookieнаходится в System.Webпространстве имен. Справочник MSDN .

Игорь Еросимич
источник
18
Это работает для меня. Однако стоит отметить, что если свойство Domain было установлено в cookie-файле FormsAuthentication при входе в систему, его также необходимо будет установить при истечении срока действия cookie-файла при выходе из системы
Фил Хейл,
8
Также не забудьте cookie1.HttpOnly = true;
Дмитрий Заец
6
Это кажется мне лучшим решением: Response.Cookies [FormsAuthentication.FormsCookieName] .Expires = DateTime.Now.AddDays (-1);
Рэнди Х.
7
@RandyH. Переопределение существующего файла cookie FormsAuthentication новым пустым файлом cookie гарантирует, что даже если клиент выключит свои системные часы, он все равно не сможет получить какие-либо пользовательские данные из файла cookie.
Tri Q Tran
9
Может ли кто-нибудь объединить все эти комментарии в ответ?
Дэвид
22

Использование двух из приведенных выше сообщений x64igor и Phil Haselden решило это:

1. x64igor привел пример для выхода из системы:

  • Сначала вам нужно очистить куки-файлы аутентификации и куки- файлы сеанса , передав обратно пустые куки-файлы в ответе на выход из системы.

    public ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        Session.Clear();  // This may not be needed -- but can't hurt
        Session.Abandon();
    
        // Clear authentication cookie
        HttpCookie rFormsCookie = new HttpCookie( FormsAuthentication.FormsCookieName, "" );
        rFormsCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rFormsCookie );
    
        // Clear session cookie 
        HttpCookie rSessionCookie = new HttpCookie( "ASP.NET_SessionId", "" );
        rSessionCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rSessionCookie );

2. Фил Хаселден привел приведенный выше пример того, как предотвратить кэширование после выхода из системы:

  • Вам необходимо аннулировать кэш на стороне клиента через ответ .

        // Invalidate the Cache on the Client Side
        Response.Cache.SetCacheability( HttpCacheability.NoCache );
        Response.Cache.SetNoStore();
    
        // Redirect to the Home Page (that should be intercepted and redirected to the Login Page first)
        return RedirectToAction( "Index", "Home" ); 
    }
justdan23
источник
1
Потраченный впустую целый день на работе, чтобы решить эту проблему. После входа в систему кнопка выхода из системы стала вызывать неправильное действие в контроллере (вход в систему не выход из системы). Спасибо, это решило проблему. Среда разработки: ASP.NET 4.51 MVC 5.1
Ako
1
Хороший ответ! Humble предложение: Используйте Форму для очистки сессионных куков x64igor используется: SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState"); HttpCookie sessionCookie = new HttpCookie(sessionStateSection.CookieName, "");. Как правило, имя файла cookie сеанса отсутствует "ASP.NET_SessionId".
Seebiscuit
20

Похоже, у вас не правильно настроен раздел авторизации web.config. Смотрите ниже пример.

<authentication mode="Forms">
  <forms name="MyCookie" loginUrl="Login.aspx" protection="All" timeout="90" slidingExpiration="true"></forms>
</authentication>
<authorization>
  <deny users="?" />
</authorization>
jwalkerjr
источник
Это гораздо более простое решение, я бы отметил это как ответ. Поскольку я получил одну версию кода, работающую на разных серверах на одном, мне не нужно было устанавливать дополнительные свойства, которые вы добавили здесь, а на других я сделал. Таким образом, изменение кода не должно быть правильным решением, изменение конфигурации лучше.
Владимир Божич
По умолчанию для параметра slideExpiration установлено значение true ( msdn.microsoft.com/library/1d3t3c61(v=vs.100).aspx ). И, наконец, это приведет к тому, что cookie станет недействительным через x минут, как установлено в тайм-ауте, а не когда пользователь выйдет из системы через SignOut (). Таким образом, это не приведет к желаемому поведению для выхода пользователя из системы с помощью FormsAuthentication. Пожалуйста, поправьте меня, если я ошибаюсь.
OlafW
12

Ключевым моментом здесь является то, что вы говорите: «Если я введу URL-адрес напрямую ...».

По умолчанию при проверке подлинности форм браузер кэширует страницы для пользователя. Таким образом, выбрав URL-адрес непосредственно из раскрывающегося списка адресов браузера или введя его, МОЖЕТ получить страницу из кэша браузера и никогда не возвращаться на сервер для проверки подлинности / авторизации. Решение этой проблемы состоит в том, чтобы предотвратить кэширование на стороне клиента в событии Page_Load каждой страницы или в OnLoad () вашей базовой страницы:

Response.Cache.SetCacheability(HttpCacheability.NoCache);

Вы также можете позвонить:

Response.Cache.SetNoStore();
Фил Хаселден
источник
11

Я тоже боролся с этим раньше.

Вот аналогия с тем, что, кажется, происходит ... Новый посетитель, Джо, заходит на сайт и входит через страницу входа в систему с помощью FormsAuthentication. ASP.NET генерирует новую личность для Джо и дает ему cookie. Это печенье похоже на ключ от дома, и пока Джо возвращается с этим ключом, он может открыть замок. Каждому посетителю предоставляется новый ключ и новый замок для использования.

Когда FormsAuthentication.SignOut()вызывается, система говорит Джо потерять ключ. Обычно это работает, так как у Джо больше нет ключа, он не может войти.

Однако, если Джо когда-нибудь вернется и у него будет этот потерянный ключ, его снова впустят!

Из того, что я могу сказать, нет способа заставить ASP.NET изменить замок на двери!

Я могу жить с этим, вспоминая имя Джо в переменной Session. Когда он выходит из системы, я покидаю сессию, поэтому у меня больше нет его имени. Позже, чтобы проверить, разрешен ли ему вход, я просто сравниваю его Identity.Name с тем, что имеет текущий сеанс, и если они не совпадают, он не является действительным посетителем.

Короче говоря, для веб-сайта НЕ полагайтесь User.Identity.IsAuthenticatedбез проверки ваших переменных сеанса!

Глен Литтл
источник
8
+1, я думаю, что это называется «атака воспроизведения cookie». Есть статья об ограничениях FormsAuthentication.SignOut: support.microsoft.com/kb/900111
Дмитрий
3
Для тех, кто хочет перейти по ссылке выше, он мертв. Вы можете попробовать использовать WaybackMachine, чтобы получить копию этой страницы здесь, но она НЕМЕДЛЕННО пытается перенаправить пользователя. web.archive.org/web/20171128133421/https://...
Killa байт
7

После долгих поисков, наконец, это сработало для меня. Я надеюсь, что это помогает.

public ActionResult LogOff()
{
    AuthenticationManager.SignOut();
    HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
    return RedirectToAction("Index", "Home");
}

<li class="page-scroll">@Html.ActionLink("Log off", "LogOff", "Account")</li>
Khosro.Pakmanesh
источник
Я много лет занимался разработкой веб-приложений на PHP. Так что я новичок в MVC ... Признаюсь, мне это нравится, НО кто бы мог подумать, что что-то такое простое, как отписаться от кого-то, будет так сложно? Я пробовал все остальные сценарии на этой странице, и это единственный работающий сценарий. Спасибо за публикацию!
Энтони Григгс
6

Это работает для меня

public virtual ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        foreach (var cookie in Request.Cookies.AllKeys)
        {
            Request.Cookies.Remove(cookie);
        }
        foreach (var cookie in Response.Cookies.AllKeys)
        {
            Response.Cookies.Remove(cookie);
        }
        return RedirectToAction(MVC.Home.Index());
    }
Korayem
источник
3

Выложенный код выглядит так, как будто он должен корректно удалить токен проверки подлинности форм, поэтому вполне возможно, что рассматриваемые папки / страницы фактически не защищены.

Подтвердили ли вы, что доступ к страницам невозможен до того, как произойдет вход в систему?

Можете ли вы опубликовать настройки web.config и код входа, который вы используете?

Абрам Симон
источник
3

Я писал базовый класс для всех своих страниц, и я пришел к той же проблеме. У меня был код, подобный следующему, и он не работал. При трассировке управление переходит от оператора RedirectToLoginPage () к следующей строке без перенаправления.

if (_requiresAuthentication)
{
    if (!User.Identity.IsAuthenticated)
        FormsAuthentication.RedirectToLoginPage();

    // check authorization for restricted pages only
    if (_isRestrictedPage) AuthorizePageAndButtons();
}

Я узнал, что есть два решения. Либо изменить FormsAuthentication.RedirectToLoginPage (); быть

if (!User.Identity.IsAuthenticated)
    Response.Redirect(FormsAuthentication.LoginUrl);

ИЛИ изменить web.config, добавив

<authorization>
  <deny users="?" />
</authorization>

Во втором случае при отслеживании управление не достигло запрошенной страницы. Он был сразу же перенаправлен на URL входа в систему, прежде чем достигнуть точки останова. Следовательно, метод SignOut () не проблема, а метод перенаправления.

Я надеюсь, что это может помочь кому-то

С уважением

Вахид Шалалы
источник
2
Также вы можете вызвать Response.End () сразу после вызова FormsAuthentication.RedirectToLoginPage ()
murki
Я думаю, что есть некоторая недопонимание со стороны MS. Вы должны заблокировать людей, если вы хотите, чтобы они вернулись на страницу входа. В противном случае фреймворк будет рад предоставить вам доступ. Таким образом, вы должны сказать решение № 2 в этом посте.
Джош Робинсон
3

Я только что попробовал некоторые из предложенных здесь советов, и, хотя я смог использовать кнопку «Назад» в браузере, когда я щелкнул по выбору меню, токен [Authorize] для этого [ActionResult] вернул меня обратно на экран входа в систему.

Вот мой код выхода из системы:

        FormsAuthentication.SignOut();
        Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
        Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
        HttpCookie cookie = HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (cookie != null)
        {
            cookie.Expires = DateTime.Now.AddDays(-1);
            Response.Cookies.Add(cookie);
        }

Хотя функция возврата в браузере вернула меня назад и отобразила защищенное меню (я все еще над этим работаю), я не смог сделать ничего, что было защищено в приложении.

Надеюсь это поможет

DonH
источник
Спасибо. Это решение, которое работало для меня (нет необходимости <deny users="?" />в web.config)
Алексей
3

Я пробовал большинство ответов в этой теме, не повезло. Закончилось этим:

protected void btnLogout_Click(object sender, EventArgs e)
{
    FormsAuthentication.Initialize();
    var fat = new FormsAuthenticationTicket(1, "", DateTime.Now, DateTime.Now.AddMinutes(-30), false, string.Empty, FormsAuthentication.FormsCookiePath);
    Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(fat)));
    FormsAuthentication.RedirectToLoginPage();
}

Нашел здесь: http://forums.asp.net/t/1306526.aspx/1

stoffen
источник
3

Этот ответ технически идентичен Хосро.Пакманеш. Я публикую его, чтобы уточнить, чем его ответ отличается от других ответов в этой теме, и в каком случае его можно использовать.

В общем, очистить пользовательский сеанс, делая

HttpContext.Session.Abandon();
FormsAuthentication.SignOut();

эффективно выйдет из системы Однако , если в том же запросе вам нужно проверить Request.isAuthenticated(как это часто бывает в фильтре авторизации, например), вы обнаружите, что

Request.isAuthenticated == true

даже после того, как вы сделали HttpContext.Session.Abandon()и FormsAuthentication.SignOut().

Единственное, что работало, делало

AuthenticationManager.SignOut();
HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);

Это эффективно устанавливает Request.isAuthenticated = false.

seebiscuit
источник
2

Это начало происходить со мной, когда я установил свойство аутентификации> формы> Путь в Web.config. Удаление, которое FormsAuthentication.SignOut();устранило проблему, и простое снова удалило cookie.

BPM
источник
1

Возможно, вы входите из одного субдомена (sub1.domain.com), а затем пытаетесь выйти из другого субдомена (www.domain.com).

jorsh1
источник
1

У меня была та же проблема, когда SignOut (), похоже, не смог правильно удалить тикет. Но только в конкретном случае, когда какая-то другая логика вызвала перенаправление. После того, как я удалил этот второй редирект (заменил его сообщением об ошибке), проблема ушла.

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

Педер Скоу
источник
1

У меня сейчас похожая проблема, и я считаю, что проблема в моем случае, как и в оригинальном постере, связана с перенаправлением. По умолчанию Response.Redirect вызывает исключение, которое немедленно всплывает, пока не будет перехвачено, и перенаправление немедленно выполнено, я предполагаю, что это предотвращает передачу измененной коллекции файлов cookie клиенту. Если вы измените свой код для использования:

Response.Redirect("url", false);

Это предотвращает исключение и, по-видимому, позволяет правильно отправлять cookie клиенту.

lostatredrock
источник
1

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

if(Session["UserID"] == null || Session["UserID"] == "")
{
    Response.Redirect("Login.aspx");
}
Devrishi
источник
1

Для меня работает следующий подход. Я думаю, что если после оператора «FormsAuthentication.SignOut ()» возникнет ошибка, SingOut не будет работать.

public ActionResult SignOut()
    {
        if (Request.IsAuthenticated)
        {
            FormsAuthentication.SignOut();

            return Redirect("~/");
        }
        return View();
     }
Аджи
источник
0

Вы тестируете / видите это поведение с помощью IE? Вполне возможно, что IE обслуживает эти страницы из кэша. Общеизвестно, что IE трудно очистить кэш, и во многих случаях, даже после выхода из системы, при вводе URL-адреса одной из «защищенных» страниц показывалось кэшированное содержимое ранее.

(Я видел такое поведение, даже когда вы входите в систему как другой пользователь, и IE отображает строку «Добро пожаловать» в верхней части вашей страницы со старым именем пользователя. В настоящее время обычно перезагрузка обновляет его, но если он сохраняется , это все еще может быть проблемой кеширования.)

Stobor
источник
0

Выполнение Session.abandon () и уничтожение cookie-файлов работает довольно хорошо. Я использую mvc3 и похоже, что проблема возникает, если вы заходите на защищенную страницу, выходите из системы и просматриваете историю браузера. Ничего страшного, но все равно раздражает.

Попытка пролистать ссылки в моем веб-приложении работает правильно, хотя.

Возможно, стоит отключить кеширование в браузере.

Джеймс
источник
0

Для MVC это работает для меня:

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            return Redirect(FormsAuthentication.GetRedirectUrl(User.Identity.Name, true));
        }
ANOVO
источник
0

Я хотел добавить некоторую информацию, чтобы помочь понять проблему. Проверка подлинности с помощью форм позволяет хранить пользовательские данные либо в файле cookie, либо в строке запроса URL-адреса. Метод, поддерживаемый вашим сайтом, можно настроить в файле web.config.

По словам Microsoft :

Метод SignOut удаляет информацию о билете для проверки подлинности форм из файла cookie или URL-адреса. если CookiesSupported имеет значение false .

В то же время они говорят :

Одно из значений HttpCookieMode, которое указывает, настроено ли приложение для аутентификации форм без файлов cookie. умолчанию используется UseDeviceProfile .

Наконец, что касается UseDeviceProfile, они говорят :

Если для свойства CookieMode установлено значение UseDeviceProfile, свойство CookiesSupported вернет значение true, если в браузере для текущего запроса поддерживает как файлы cookie, так и перенаправление с файлами cookie ; в противном случае свойство CookiesSupported вернет false.

Соединяя все это вместе, в зависимости от браузера пользователя, конфигурация по умолчанию может привести к тому, что CookiesSupported будет true , что означает, что метод SignOut не удаляет билет из куки. Это кажется нелогичным, и я не знаю, почему это работает так - я бы ожидал, что SignOut действительно выписывает пользователя при любых обстоятельствах.

Один из способов заставить SignOut работать самостоятельно - изменить режим файлов cookie на «UseCookies» (т. Е. Файлы cookie требуются) в файле web.config:

<authentication mode="Forms">
  <forms loginUrl="~/Account/SignIn" cookieless="UseCookies"/>
</authentication>

Согласно моим тестам, выполнение этого заставляет SignOut работать само по себе за счет вашего сайта, требующего, чтобы файлы cookie функционировали должным образом.

RogerMKE
источник
Я думаю, что вы читаете это неправильно. Что касается SignOut (), я уверен, что они имеют в виду, что он будет очищен от URL, если CookiesSupported имеет значение false, в противном случае из cookie. Т.е. они должны были написать «Метод SignOut удаляет информацию о билете для проверки подлинности форм из cookie или, если CookiesSupported имеет значение false, из URL».
Оскар Берггрен
-1

Имейте в виду, что WIF отказывается указывать браузеру очистить файлы cookie, если сообщение wsignoutcleanup от STS не соответствует URL-адресу с именем приложения из IIS, и я имею в виду CASE SENSITIVE . WIF отвечает зеленой проверкой OK, но не отправляет команду на удаление куки в браузер.

Итак, вам нужно обратить внимание на чувствительность к регистру ваших URL.

Например, ThinkTecture Identity Server сохраняет URL-адреса посещающих RP в одном файле cookie, но делает их все строчными. WIF получит сообщение wsignoutcleanup в нижнем регистре и сравнит его с именем приложения в IIS. Если он не совпадает, он не удаляет куки, но сообщит браузеру «ОК». Итак, для этого Identity Server мне нужно было написать все URL-адреса в web.config и имена всех приложений в IIS в нижнем регистре, чтобы избежать таких проблем.

Также не забудьте разрешить сторонние куки в браузере, если у вас есть приложения за пределами поддоменов STS, иначе браузер не удалит куки, даже если WIF скажет ему об этом.

Стефан
источник
1
WIF? STS? ThinkTecture Identity Server? Что это за вещи и как они относятся к этому вопросу?
Оскар Берггрен