# فعال‌سازی HSTS در ASP.NET Core

برای فعال‌سازی SSL در ASP.NET Core می‌توانیم از ویژگی RequireHttps برای کنترلرها و همچنین اکشن‌متدها استفاده کنیم: [RequireHttps] public class AccountController : Controller { public IActionResult Log

- Published: 2017-12-28
- Language: fa
- Tags: DNTips
- Canonical: https://sirwan.info/blog/fa/dntips-2798

---

> این نوشته نخستین بار در [دات‌نت تیپس](https://www.dntips.ir/post/2798) منتشر شده است.

<div class="postBody">برای فعال‌سازی SSL در ASP.NET Core می‌توانیم از ویژگی RequireHttps برای کنترلرها و همچنین اکشن‌متدها استفاده کنیم:<br/> <div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">[RequireHttps]&#10;public class AccountController : Controller&#10;{&#10;    public IActionResult Login()&#10;    {&#10;        return Content("Login Page");&#10;    }&#10;}</pre>
 </div>
اکنون اگر پروژه را اجرا کنید، خروجی به صورت زیر خواهد بود و اگر به آدرس Account/Login مراجعه کنید، چیزی در خروجی نمایش داده نخواهد شد:</div> <div> <p style="margin-left: auto; margin-right: auto;"> <img src="/img/dntips/1ee871c1ff3d012455e5.png" style="display: block; margin-left: auto; margin-right: auto; cursor: default; height: 398px;"/> </p> </div> <div> <p style="margin-left: auto; margin-right: auto;"> </p> </div> <div>علت آن نیز این است که اگر درخواست HTTPS نباشد، یک Permanent Redirect به همان URL خواهیم داشت؛ زیرا بر روی پورتی که پروژه توسط آن اجرا شده‌است، هیچ certificateی نصب نشده‌است. در ادامه می‌خواهیم یک <a href="https://en.wikipedia.org/wiki/Self-signed_certificate">self-signed certificate</a> تستی را برای اجرای پروژه ایجاد کنیم.  </div> <div>توسط دستورات زیر می‌توانید یک certificate را برای localhost ایجاد کنید:</div> <div> <div align="left" dir="ltr" style="direction: ltr;"> </div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">$ openssl genrsa -out key.pem 2048&#10;$ openssl req -new -sha256 -key key.pem -out csr.csr&#10;$ openssl req -x509 -sha256 -days 365 -key key.pem -in csr.csr -out certificate.pem&#10;openssl pkcs12 -export -out localhost.pfx -inkey key.pem -in certificate.pem</pre>
 </div>
با اجرای دستورات فوق، باید یک فایل جدید با نام localhost.pfx و تعدادی فایل دیگر، درون پروژه ایجاد شده باشند:</div> <div> <p style="margin-left: auto; margin-right: auto;"> <img src="/img/dntips/e2572e130205a3ed3259.png" style="display: block; margin-left: auto; margin-right: auto; cursor: default; height: 378px;"/> </p> </div> <div>اکنون باید Kestrel را از وجود این certificate مطلع کنیم. برای انجام اینکار ابتدا باید پکیج زیر را نصب کنیم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">$ dotnet add package Microsoft.AspNetCore.Server.Kestrel.Https</pre>
 </div>
سپس فایل Program.cs را به صورت زیر تغییر دهیم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">namespace testingSSL&#10;{&#10;    public class Program&#10;    {&#10;        public static void Main(string[] args)&#10;        {&#10;            BuildWebHost(args).Run();&#10;        }&#10;&#10;        public static IWebHost BuildWebHost(string[] args) =&gt;&#10;            WebHost.CreateDefaultBuilder(args)&#10;                .UseKestrel(options =&gt;&#10;                {&#10;                    options.Listen(IPAddress.Any, 8080);&#10;                    options.Listen(IPAddress.Any, 443,&#10;                        listenOptions =&gt; listenOptions.UseHttps("localhost.pfx", "qwe123456"));&#10;                })&#10;                .UseStartup&lt;Startup&gt;()&#10;                .Build();&#10;    }&#10;}</pre>
 </div>
در این حالت با مراجعه به کنترلری که با ویژگی RequireHttps مزین شده‌است، به صورت خودکار، درخواست به نسخه HTTPS هدایت خواهد شد:</div> <div> <p style="margin-left: auto; margin-right: auto;"> <img src="/img/dntips/0decf6ec4d63ab9a25fb.png" style="display: block; margin-left: auto; margin-right: auto; cursor: default; height: 413px;"/> </p> <p style="margin-left: auto; margin-right: auto;">البته تا اینجا، هدف بررسی ویژگی RequireHttps بود؛ طبیعتاً به SSL در حین Development نیازی نخواهید داشت. در نتیجه می‌توانیم به صورت Global تمامی کنترلرها را در زمان Production به ویژگی گفته شده مزین کنیم:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">private readonly IHostingEnvironment _env;&#10;public Startup(IConfiguration configuration,&#10;IHostingEnvironment env)&#10;{&#10;    Configuration = configuration;&#10;    _env = env;&#10;}&#10;&#10;public IConfiguration Configuration { get; }&#10;&#10;// This method gets called by the runtime. Use this method to add services to the container.&#10;public void ConfigureServices(IServiceCollection services)&#10;{&#10;    services.AddMvc();&#10;&#10;    if (!_env.IsDevelopment())&#10;        services.Configure&lt;MvcOptions&gt;(o =&gt; o.Filters.Add(new RequireHttpsAttribute()));&#10;}</pre>
 </div> <p style="margin-left: auto; margin-right: auto;"> <br/> </p> <p style="margin-left: auto; margin-right: auto;"> <b>(Http Strict Transport Security (HSTS</b> </p> <p style="margin-left: auto; margin-right: auto;">هدایت کردن خودکار درخواست از حالت HTTP به HTTPS، توسط خیلی از سایت‌ها انجام می‌شود:</p> <p style="margin-left: auto; margin-right: auto;"> <img src="/img/dntips/2f5170dceebd860780d9.png" style="display: block; margin-left: auto; margin-right: auto; cursor: default; height: 522px;"/> </p> <p style="margin-left: auto; margin-right: auto;">البته این روش بهتر از استفاده نکردن از SSL است؛ اما در نظر داشته باشید که همیشه اولین درخواست به صورت رمزنگاری نشده ارسال خواهد شد. فرض کنید در یک محیط پابلیک از طریق WiFi به اینترنت متصل شده‌ایم. شخصی (هکر) که بر روی مودم کنترل دارد، طوری WiFi را پیکربندی کرده‌است که به جای آدرس اصلی که در تصویر مشاهده می‌کنید، یک نسخه جعلی از سایت باز شود؛ به طوریکه URL همانند URL اصلی باشد. در این‌حالت کاربر به جای اینکه نام‌کاربری و کلمه‌عبور را وارد سایت اصلی کند، آن را درون سایت جعلی وارد خواهد کرد. برای حل این مشکل می‌توانیم وب‌سایت‌مان را طوری تنظیم کنیم که هدر Strict-Transport-Security را به هدر اولین responseی که توسط مرورگر دریافت می‌شود اضافه کند:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">Strict-Transport-Security: max-age=31536000</pre>
 </div> <p style="margin-left: auto; margin-right: auto;">بنابراین مرورگر وب‌سایت را درون یک لیست internal به مدت یکسال (مقدار max-age) نگهداری خواهد کرد؛ در طول این زمان به هیچ درخواست ناامنی اجازه داده نخواهد شد. به این قابلیت HSTS گفته می‌شود. البته ASP.NET Core به صورت توکار روشی را جهت اضافه کردن این هدر ارائه نداده است؛ اما می‌توانیم خودمان یک Middleware سفارشی را به pipeline اضافه کنیم تا اینکار را برایمان انجام دهد:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">namespace testingSSL.Middleware&#10;{&#10;    public class HstsMiddleware&#10;    {&#10;        private readonly RequestDelegate _next;&#10;        public HstsMiddleware(RequestDelegate next)&#10;        {&#10;            _next = next;&#10;        }&#10;&#10;        public Task Invoke(HttpContext context)&#10;        {&#10;            if (!context.Request.IsHttps)&#10;                return _next(context);&#10;&#10;            if (IsLocalhost(context))&#10;                return _next(context);&#10;&#10;            context.Response.Headers.Add("Strict-Transport-Security", "max-age=31536000");&#10;&#10;            return _next(context);&#10;        }&#10;        private bool IsLocalhost(HttpContext context)&#10;        {&#10;            return string.Equals(context.Request.Host.Host, "localhost", StringComparison.OrdinalIgnoreCase);&#10;        }&#10;    }&#10;}</pre>
 </div> <p style="margin-left: auto; margin-right: auto;"> <br/> </p> <p style="margin-left: auto; margin-right: auto;">یا اینکه می‌توانیم از کتابخانه NWebSec استفاده کنیم:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">$ dotnet add package NWebsec.AspNetCore.Middleware</pre>
 </div> <p style="margin-left: auto; margin-right: auto;">برای استفاده از آن نیز خواهیم داشت:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">app.UseHsts(h =&gt; h.MaxAge(days: 365));</pre>
 </div> <p style="margin-left: auto; margin-right: auto;"> <br/> </p> <p style="margin-left: auto; margin-right: auto;">اما هنوز یک مشکل وجود دارد؛ هنوز مشکل اولین درخواست را برطرف نکرده‌ایم. زیرا مرورگر برای دریافت این هدر نیاز به مراجعه به سایت دارد. برای حل این مشکل می‌توانید آدرس وب‌سایت خود را در سایت <a href="https://hstspreload.org/">hstspreload</a> ثبت کنید، سپس متد PreLoad را به کد فوق اضافه کنید:</p> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">app.UseHsts(h =&gt; h.MaxAge(days: 365).Preload());</pre>
 </div> <p style="margin-left: auto; margin-right: auto;">در اینحالت حتی اگر کسی به وب‌سایت شما مراجعه نکند، مرورگر می‌داند که باید از HTTPS استفاده کند. زیرا این لیست به صورت توکار درون مرورگر تعبیه شده‌است. بنابراین در این‌حالت مطمئن خواهیم شد که تمامی connectionها به سایت‌مان امن می‌باشند.</p> <p style="margin-left: auto; margin-right: auto;"> <br/> </p> <p style="margin-left: auto; margin-right: auto;"> <b>دریافت کدهای مطلب جاری (<a href="https://github.com/SirwanAfifi/SSL-in-ASP.NET-Core">+</a>)</b> </p> <p style="margin-left: auto; margin-right: auto;"> </p> </div> </div></div>
