Arduino безопасно потребляет .net Core API? - PullRequest
0 голосов
/ 27 августа 2018

Мне удалось настроить мой .Net core 2 API так, чтобы пользователь запрашивал логин, когда он / она хочет его использовать. Код для этого находится в Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>()
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings
        options.Password.RequireDigit = true;
        options.Password.RequiredLength = 8;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = true;
        options.Password.RequireLowercase = false;
        options.Password.RequiredUniqueChars = 6;

        // Lockout settings
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(60);
        options.Lockout.MaxFailedAccessAttempts = 10;
        options.Lockout.AllowedForNewUsers = true;

        // User settings
        options.User.RequireUniqueEmail = true;
    });

    services.ConfigureApplicationCookie(options =>
    {
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
        options.LoginPath = "/Account/Login";
        options.AccessDeniedPath = "/Account/AccessDenied";
        options.SlidingExpiration = true;
    });

    services.AddApplicationServices(Configuration);
    services.AddRouting();
    services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
    services.AddSwagger(Configuration);
}

Теперь я размещаю это на Azure, и, похоже, оно работает. Однако мне нужно спросить, как бы я это реализовал, если я хочу, чтобы мой Arduino использовал этот API? Должен ли я просто создать учетную запись пользователя Arduino и позволить arduino опубликовать учетные данные этой учетной записи и сохранить куки? Это безопасный подход?

1 Ответ

0 голосов
/ 28 августа 2018

Вместо JWT или Cookies, используйте предварительный секрет и некоторые базовые крипто. Используйте их для установки пользовательских заголовков в ваших HTTP-запросах к API Azure из Arduino.

Обязательно храните свой предварительно общий секретный ключ в базе данных и / или переменных среды на компьютере сборки через хранилище ключей Azure - не включайте их в исходный код.

Используйте такой декоратор для защиты ваших контроллеров API:

[UserKeyAuthorize]
public class MyController : Controller
{
    // ...
}

Вот реализация атрибута + фильтра:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Delegate)]
public class UserKeyAuthorizeAttribute : TypeFilterAttribute
{
    public UserKeyAuthorizeAttribute() : base(typeof(UserKeyFilter))
    {
    }

    public class UserKeyFilter : IAsyncActionFilter
    {
        public static string CalculateHash(string stringToHash)
        {
            using (var sha = SHA256.Create())
            {
                var computedHash = sha.ComputeHash(Encoding.Unicode.GetBytes(stringToHash));
                return Convert.ToBase64String(computedHash);
            }
        }


        private const string Hash = "signature";

        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // check key
            if (!IsUserKeyValid(context.HttpContext.Request))
            {
                context.Result = new UnauthorizedResult();
            }
            else
            {
                await next();
            }
        }

        public static bool IsUserKeyValid(HttpRequest request)
        {
            var map = new Dictionary<string, string>();
            foreach (var header in request.Headers)
            {
                if (header.Key.StartsWith("x-"))
                {
                    map.Add(header.Key, header.Value);
                }
            }

            var headerSig = "";

            foreach (var key in map.OrderBy(x=> x.Key))
            {
                headerSig = headerSig + "x-" + map[key.Key] + "-";
            }
            var nonce = request.Headers["nonce"].FirstOrDefault();   
            headerSig = headerSig + nonce + "-" + Environment.GetEnvironmentVariable("ApiKey");
            var hash = CalculateHash(headerSig);
            var signature = request.Headers[Hash].FirstOrDefault<string>();                                

            return hash == signature;
        }
    }       
}

На вашем Arduino вы можете затем установить заголовки в ваших запросах API, хешировать их с помощью общего ключа и отправить хеш в качестве дополнительного заголовка, чтобы сервер мог вас проверить. https://github.com/simonratner/Arduino-SHA-256 и https://techtutorialsx.com/2016/07/21/esp8266-post-requests/ должны помочь вам с кодом клиента, вот что работает для меня в C #:

    public static async Task<HttpResponseMessage> SignedGetRequest(this HttpClient client, string url, Dictionary<string, string> data)
    {   
        var request = new HttpRequestMessage()
        {
            RequestUri = new Uri(url),
            Method = HttpMethod.Get
        };

        var keys = data.Keys.OrderBy(x => x);
        var headerSig = "";
        foreach (var key in keys)
        {
            headerSig = headerSig + "x-" + data[key] + "-";
            request.Headers.Add("x-" + key, data[key]);
        }
        // TODO: Time based nonce hash
        var nonce = String.Format("{0:r}", DateTime.Now);
        headerSig = headerSig + nonce + "-" + Environment.GetEnvironmentVariable("ApiKey");
        var hash = Security.CalculateHash(headerSig);
        request.Headers.Add("signature", hash);
        request.Headers.Add("nonce", nonce);

        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        return  await client.SendAsync(request);
    }

Вы можете установить любую информацию уровня пользователя или приложения в заголовках, например, например. «x-user-id» для идентификации отдельных клиентов на сервере.

Сведения о настройке ключа в Azure для разрешения Environment.GetEnvironmentVariable("ApiKey") см. https://stackoverflow.com/a/34622196

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