Поделиться через


Обычная проверка подлинности в веб-API ASP.NET

Майк Уассон

Обычная проверка подлинности определяется в RFC 2617, проверка подлинности HTTP: обычная и дайджест-проверка подлинности.

Недостатки

  • Учетные данные пользователя отправляются в запросе.
  • Учетные данные отправляются в виде открытого текста.
  • Учетные данные отправляются с каждым запросом.
  • Выходить из системы можно только путем завершения сеанса браузера.
  • Уязвимы для межсайтовых подделки запросов (CSRF); требуются меры по борьбе с CSRF.

Преимущества

  • Интернет стандартный.
  • Поддерживается всеми основными браузерами.
  • Относительно простой протокол.

Обычная проверка подлинности работает следующим образом:

  1. Если запрос требует проверки подлинности, сервер возвращает 401 (не авторизовано). Ответ содержит заголовок WWW-Authenticate, указывающий, что сервер поддерживает обычную проверку подлинности.
  2. Клиент отправляет другой запрос с учетными данными клиента в заголовке авторизации. Учетные данные форматируются как строка "name:password" в кодировке base64. Учетные данные не шифруются.

Обычная проверка подлинности выполняется в контексте "области". Сервер включает имя области в заголовок WWW-Authenticate. Учетные данные пользователя действительны в пределах этой области. Точный область области определяется сервером. Например, можно определить несколько областей для секционирования ресурсов.

Схема обычной проверки подлинности

Так как учетные данные отправляются незашифрованными, обычная проверка подлинности защищена только по протоколу HTTPS. См . раздел Работа с SSL в веб-API.

Обычная проверка подлинности также уязвима для атак CSRF. Когда пользователь вводит учетные данные, браузер автоматически отправляет их при последующих запросах в тот же домен в течение сеанса. Сюда входят запросы AJAX. См. статью Предотвращение атак с подделкой межсайтовых запросов (CSRF).

Обычная проверка подлинности с помощью IIS

СЛУЖБЫ IIS поддерживают обычную проверку подлинности, но есть предостережение: пользователь проходит проверку подлинности с использованием учетных данных Windows. Это означает, что у пользователя должна быть учетная запись в домене сервера. Для общедоступного веб-сайта обычно требуется пройти проверку подлинности у поставщика членства ASP.NET.

Чтобы включить обычную проверку подлинности с помощью IIS, задайте режим проверки подлинности "Windows" в Web.config проекта ASP.NET:

<system.web>
    <authentication mode="Windows" />
</system.web>

В этом режиме службы IIS используют учетные данные Windows для проверки подлинности. Кроме того, необходимо включить обычную проверку подлинности в СЛУЖБАх IIS. В диспетчере IIS перейдите в представление функций, выберите Проверка подлинности и включите обычную проверку подлинности.

Изображение панели мониторинга i I S Manager

В проекте веб-API добавьте [Authorize] атрибут для всех действий контроллера, требующих проверки подлинности.

Клиент выполняет проверку подлинности, задавая заголовок Authorization в запросе. Клиенты браузера выполняют этот шаг автоматически. Клиентам, не требующим выполнения, необходимо задать заголовок.

Обычная проверка подлинности с пользовательским членством

Как уже упоминалось, обычная проверка подлинности, встроенная в IIS, использует учетные данные Windows. Это означает, что необходимо создать учетные записи для пользователей на сервере размещения. Но для интернет-приложения учетные записи пользователей обычно хранятся во внешней базе данных.

Ниже приведен код http-модуля, выполняющего обычную проверку подлинности. Вы можете легко подключить поставщик членства ASP.NET, заменив CheckPassword метод , который является фиктивным методом в этом примере.

В веб-API 2 следует рассмотреть возможность написания фильтра проверки подлинности или ПО промежуточного слоя OWIN вместо модуля HTTP.

namespace WebHostBasicAuth.Modules
{
    public class BasicAuthHttpModule : IHttpModule
    {
        private const string Realm = "My Realm";

        public void Init(HttpApplication context)
        {
            // Register event handlers
            context.AuthenticateRequest += OnApplicationAuthenticateRequest;
            context.EndRequest += OnApplicationEndRequest;
        }

        private static void SetPrincipal(IPrincipal principal)
        {
            Thread.CurrentPrincipal = principal;
            if (HttpContext.Current != null)
            {
                HttpContext.Current.User = principal;
            }
        }

        // TODO: Here is where you would validate the username and password.
        private static bool CheckPassword(string username, string password)
        {
            return username == "user" && password == "password";
        }

        private static void AuthenticateUser(string credentials)
        {
            try
            {
                var encoding = Encoding.GetEncoding("iso-8859-1");
                credentials = encoding.GetString(Convert.FromBase64String(credentials));

                int separator = credentials.IndexOf(':');
                string name = credentials.Substring(0, separator);
                string password = credentials.Substring(separator + 1);

                if (CheckPassword(name, password))
                {
                    var identity = new GenericIdentity(name);
                    SetPrincipal(new GenericPrincipal(identity, null));
                }
                else
                {
                    // Invalid username or password.
                    HttpContext.Current.Response.StatusCode = 401;
                }
            }
            catch (FormatException)
            {
                // Credentials were not formatted correctly.
                HttpContext.Current.Response.StatusCode = 401;
            }
        }

        private static void OnApplicationAuthenticateRequest(object sender, EventArgs e)
        {
            var request = HttpContext.Current.Request;
            var authHeader = request.Headers["Authorization"];
            if (authHeader != null)
            {
                var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);

                // RFC 2617 sec 1.2, "scheme" name is case-insensitive
                if (authHeaderVal.Scheme.Equals("basic",
                        StringComparison.OrdinalIgnoreCase) &&
                    authHeaderVal.Parameter != null)
                {
                    AuthenticateUser(authHeaderVal.Parameter);
                }
            }
        }

        // If the request was unauthorized, add the WWW-Authenticate header 
        // to the response.
        private static void OnApplicationEndRequest(object sender, EventArgs e)
        {
            var response = HttpContext.Current.Response;
            if (response.StatusCode == 401)
            {
                response.Headers.Add("WWW-Authenticate",
                    string.Format("Basic realm=\"{0}\"", Realm));
            }
        }

        public void Dispose() 
        {
        }
    }
}

Чтобы включить модуль HTTP, добавьте следующий код в файл web.config в разделе system.webServer :

<system.webServer>
    <modules>
      <add name="BasicAuthHttpModule" 
        type="WebHostBasicAuth.Modules.BasicAuthHttpModule, YourAssemblyName"/>
    </modules>

Замените YourAssemblyName именем сборки (не включая расширение dll).

Следует отключить другие схемы проверки подлинности, такие как forms или проверка подлинности Windows.