Sdílet prostřednictvím


Co nedělat v ASP.NET a jak to udělat správně

Toto téma popisuje několik běžných chyb, které lidé dělají v rámci ASP.NET webových projektů. Poskytuje doporučení, co byste měli dělat, abyste se těmto běžným chybám vyhnuli. Vychází z prezentaceDamiana Edwardse na norské vývojářské konferenci.

Disclaimer

Toto téma není zamýšleno jako úplný průvodce, jak zajistit, aby vaše aplikace byla zabezpečená a efektivní. Stále musíte dodržovat osvědčené postupy pro zabezpečení a výkon, které nejsou popsané v tomto tématu. Pouze navrhuje, jak se vyhnout běžným chybám souvisejícím s procesy a třídami .NET.

Přehled

Toto téma obsahuje následující oddíly:

Dodržování standardů

Ovládací adaptéry

Doporučení: Přestaňte používat řídicí adaptéry pro adaptivní vykreslování a místo toho používejte dotazy na média CSS a html kompatibilní se standardy.

Adaptéry ovládacích prvků byly zavedeny v .NET 2.0 k vykreslení prezentačního kódu přizpůsobeného pro různá zařízení a prostředí. Teď můžete toto adaptivní vykreslování provádět pomocí šablon stylů CSS a HTML. Měli byste přestat používat ovládací adaptéry a převést všechny existující adaptéry na šablony stylů CSS a HTML.

Další informace najdete v tématech Dotazy na média a Postupy: Přidání mobilních stránek do aplikace ASP.NET Web Forms / MVC.

Vlastnosti stylu u ovládacích prvků

Doporučení: Ukončete nastavování hodnot stylů v ovládacím kódu a místo toho nastavte hodnoty formátování v šablonách stylů CSS.

Ovládací prvky webového serveru obsahují desítky vlastností, které lze použít k nastavení vlastností stylu řádku. Například ForeColor vlastnost nastaví barvu textu ovládacího prvku. Stejného výsledku můžete dosáhnout efektivněji pomocí šablon stylů CSS. Šablony stylů umožňují centralizovat hodnoty stylů a vyhnout se nastavení těchto hodnot v celé aplikaci.

Následující příklad ukazuje třídu CSS, která nastaví text na červenou.

.CautionRow {
    color: red;
}

Následující příklad ukazuje, jak dynamicky použít třídu CSS.

protected void CustomersGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.Cells[2].Text == "Unconfirmed")
    {
        e.Row.CssClass = "CautionRow";
    }
}

Stránkovat a ovládat zpětná volání

Doporučení: Přestaňte používat zpětná volání stránky a řízení a místo toho použijte některou z následujících metod: AJAX, UpdatePanel, metody akcí MVC, Webové rozhraní API nebo SignalR.

V dřívějších verzích ASP.NET vám metody zpětného volání Page a Control umožňovaly aktualizovat část webové stránky bez aktualizace celé stránky. Částečné aktualizace stránek teď můžete provádět prostřednictvím FUNKCÍ AJAX, UpdatePanel, MVC, Webového rozhraní API nebo SignalR. Metody zpětného volání byste měli přestat používat, protože můžou způsobovat problémy s popisnými adresami URL a směrováním. Ve výchozím nastavení ovládací prvky nepovolují metody zpětného volání, ale pokud jste tuto funkci v ovládacím prvku povolili, měli byste ji zakázat.

Detekce schopností prohlížeče

Doporučení: Přestaňte používat detekci funkcí statického prohlížeče a místo toho použijte dynamickou detekci funkcí.

V dřívějších verzích ASP.NET byly podporované funkce pro každý prohlížeč uložené v souboru XML. Zjišťování podpory funkcí prostřednictvím statického vyhledávání není nejlepší přístup. Teď můžete dynamicky rozpoznat podporované funkce prohlížeče pomocí architektury detekce funkcí, jako je Například Modernizr. Detekce funkcí určuje podporu tak, že se pokusí použít metodu nebo vlastnost a pak zkontroluje, jestli prohlížeč vytvořil požadovaný výsledek. Ve výchozím nastavení je modernizátor součástí šablon webových aplikací.

Zabezpečení

Žádost o ověření

Doporučení: Ověřte vstup uživatele a kódujte výstup od uživatelů.

Ověření požadavku je funkce ASP.NET, která kontroluje každý požadavek a zastaví ho, pokud se najde vnímaná hrozba. Při zabezpečení aplikace před skriptovacími útoky mezi weby nespoléhejte na ověření požadavků. Místo toho ověřte všechny vstupy od uživatelů a zakódujte výstup. V některých omezených případech můžete k ověření vstupu použít regulární výrazy, ale ve složitějších případech byste měli uživatelský vstup ověřit pomocí tříd .NET, které určují, jestli hodnota odpovídá povoleným hodnotám.

Následující příklad ukazuje, jak použít statickou metodu ve třídě Uri k určení, zda je identifikátor URI poskytnutý uživatelem platný.

var isValidUri = Uri.IsWellFormedUriString(passedUri, UriKind.Absolute);

Pokud ale chcete identifikátor URI dostatečně ověřit, měli byste také zkontrolovat, jestli určuje http nebo https. Následující příklad používá metody instance k ověření platnosti identifikátoru URI.

var uriToVerify = new Uri(passedUri);
var isValidUri = uriToVerify.IsWellFormedOriginalString();
var isValidScheme = uriToVerify.Scheme == "http" || uriToVerify.Scheme == "https";

Před vykreslením uživatelského vstupu ve formátu HTML nebo zahrnutím uživatelského vstupu do dotazu SQL zakódujte hodnoty, abyste zajistili, že nebude zahrnutý škodlivý kód.

Hodnotu ve značkách můžete zakódovat pomocí <syntaxe %: %> , jak je znázorněno níže.

<span><%: userInput %></span>

Nebo v syntaxi Razor můžete kódovat HTML pomocí @, jak je znázorněno níže.

<span>@userInput</span>

Další příklad ukazuje, jak kódovat hodnotu html v kódu na pozadí.

var encodedInput = Server.HtmlEncode(userInput);

Chcete-li bezpečně kódovat hodnotu pro příkazy SQL, použijte parametry příkazu, jako je SqlParameter.

Ověřování a relace formulářů bez souborů cookie

Doporučení: Vyžadovat soubory cookie.

Předávání ověřovacích informací v řetězci dotazu není bezpečné. Proto vyžadovat soubory cookie, pokud vaše aplikace zahrnuje ověřování. Pokud váš soubor cookie ukládá citlivé informace, zvažte vyžádání SSL pro soubor cookie.

Následující příklad ukazuje, jak v souboru Web.config určit, že ověřování pomocí formulářů vyžaduje soubor cookie, který se přenáší přes protokol SSL.

<authentication mode="Forms">
  <forms loginUrl="member_login.aspx"
    cookieless="UseCookies"
    requireSSL="true"
    path="/MyApplication" />
</authentication>

Enableviewstatemac

Doporučení: Nikdy nenastavujte hodnotu false.

Ve výchozím nastavení je Vlastnost EnableViewStateMac nastavená na hodnotu true. I když vaše aplikace nepoužívá stav zobrazení, nenastavujte EnableViewStateMac na false. Nastavení této hodnoty na false způsobí, že vaše aplikace bude zranitelná vůči skriptování mezi weby.

Počínaje ASP.NET 4.5.2 modul runtime vynucuje EnableViewStateMac=true. I když ho nastavíte na false, modul runtime tuto hodnotu ignoruje a pokračuje s hodnotou nastavenou na true. Další informace najdete v tématech ASP.NET 4.5.2 a EnableViewStateMac.

Následující příklad ukazuje, jak nastavit EnableViewStateMac na true. Tuto hodnotu nemusíte ve skutečnosti nastavovat na true, protože je ve výchozím nastavení true. Pokud jste ale na libovolné stránce aplikace nastavili hodnotu false, musíte tuto hodnotu okamžitě opravit.

<%@ Page language="C#" EnableViewStateMac="true" %>

Střední vztah důvěryhodnosti

Doporučení: Nespoléhejte na střední vztah důvěryhodnosti (ani na žádné jiné úrovni důvěryhodnosti) jako na hranici zabezpečení.

Částečná důvěryhodnost dostatečně nechrání vaši aplikaci a neměla by se používat. Místo toho použijte úplný vztah důvěryhodnosti a izolujte nedůvěryhodné aplikace v samostatných fondech aplikací. Každý fond aplikací také spusťte pod jedinečnou identitou. Další informace najdete v tématu ASP.NET částečná důvěryhodnost nezaručuje izolaci aplikace.

<Appsettings>

Doporučení: Nezakazujte nastavení zabezpečení v <elementu appSettings> .

Element appSettings obsahuje mnoho hodnot, které se vyžadují pro aktualizace zabezpečení. Tyto hodnoty byste neměli měnit ani zakazovat. Pokud musíte tyto hodnoty při nasazování aktualizace zakázat, po dokončení nasazení je okamžitě znovu povolte.

Podrobnosti najdete v tématu ASP.NET appSettings – element.

UrlPathEncode

Doporučení: Místo toho použijte UrlEncode .

Metoda UrlPathEncode byla přidána do rozhraní .NET Framework za účelem vyřešení velmi specifického problému s kompatibilitou prohlížeče. Nezakóduje adekvátně adresu URL a nechrání vaši aplikaci před skriptováním mezi weby. V aplikaci byste ho nikdy neměli používat. Místo toho použijte UrlEncode.

Následující příklad ukazuje, jak předat kódovanou adresu URL jako parametr řetězce dotazu pro ovládací prvek hypertextového odkazu.

string destinationURL = "http://www.contoso.com/default.aspx?user=test";
NextPage.NavigateUrl = "~/Finish?url=" + Server.UrlEncode(destinationURL);

Spolehlivost a výkon

PreSendRequestHeaders a PreSendRequestContent

Doporučení: Nepoužívejte tyto události se spravovanými moduly. Místo toho napište nativní modul služby IIS, který provede požadovanou úlohu. Viz Vytváření Native-Code modulů HTTP.

Události PreSendRequestHeaders a PreSendRequestContent můžete použít s nativními moduly služby IIS.

Upozornění

Nepoužívejte PreSendRequestHeaders a PreSendRequestContent se spravovanými moduly, které implementují IHttpModule. Nastavení těchto vlastností může způsobit problémy s asynchronními požadavky. Kombinace směrování požadovaných aplikací (ARR) a websocketů může vést k výjimkám narušení přístupu, které můžou způsobit chybové ukončení w3wp. Například iiscore! W3_CONTEXT_BASE::GetIsLastNotification+68 v iiscore.dll způsobil výjimku porušení přístupu (0xC0000005).

Asynchronní události stránky s webovými formuláři

Doporučení: V Web Forms nepoužívejte asynchronní metody void pro události životního cyklu stránky a místo toho pro asynchronní kód používejte Page.RegisterAsyncTask.

Když označíte událost stránky pomocí asynchronních a void, nemůžete určit, kdy byl asynchronní kód dokončen. Místo toho použijte Page.RegisterAsyncTask ke spuštění asynchronního kódu způsobem, který vám umožní sledovat jeho dokončení.

Následující příklad ukazuje obslužnou rutinu kliknutí na tlačítko, která obsahuje asynchronní kód. Tento příklad zahrnuje čtení řetězcové hodnoty asynchronně, které je poskytováno pouze jako zjednodušený příklad asynchronní úlohy, a nikoli jako doporučený postup.

protected void StartAsync_Click(object sender, EventArgs e)
{
    Page.RegisterAsyncTask(new PageAsyncTask(async() =>
    {
        string stringToRead = "Long text value";

        using (StringReader reader = new StringReader(stringToRead))
        {
            string readText = await reader.ReadToEndAsync();
            Result.Text = readText;
        }
    }));
}

Pokud používáte asynchronní úlohy, nastavte cílovou architekturu modulu runtime HTTP na 4.5 (nebo novější) v souboru Web.config. Nastavení cílové architektury na 4.5 zapne nový kontext synchronizace přidaný v .NET 4.5. Tato hodnota je nastavena ve výchozím nastavení v nových projektech v sadě Visual Studio, ale není nastavena, pokud pracujete s existujícím projektem.

<system.web>
    <httpRuntime TargetFramework="4.5" />
</system.web>

Zapomeňte na práci

Doporučení: Při zpracování požadavku v rámci ASP.NET se vyhněte spuštění fire-and-forget (například volání metody ThreadPool.QueueUserWorkItem nebo vytvoření časovače, který opakovaně volá delegáta).

Pokud je vaše aplikace spuštěná v rámci ASP.NET, může se nesynchronizovat. Doménu aplikace je možné kdykoliv zničit, což znamená, že probíhající proces už nemusí odpovídat aktuálnímu stavu aplikace.

Tento typ práce byste měli přesunout mimo ASP.NET. Pomocí webových úloh, služby Windows nebo role pracovního procesu v Azure můžete provádět probíhající práci a spouštět tento kód z jiného procesu.

Pokud musíte tuto práci provést v rámci ASP.NET, můžete ke spuštění kódu přidat balíček NuGet s názvem WebBackgrounder .

Tělo entity žádosti

Doporučení: Vyhněte se čtení Request.Form nebo Request.InputStream před událostí spuštění obslužné rutiny.

Nejdříve byste měli číst z Request.Form nebo Request.InputStream během události spuštění obslužné rutiny. V MVC je kontroler obslužnou rutinou a událost spuštění nastane při spuštění metody action. V Web Forms je page obslužnou rutinou a událost spuštění nastane, když se aktivuje událost Page.Init. Pokud si přečtete tělo entity požadavku dříve než událost spuštění, zasahujete do zpracování požadavku.

Pokud před událostí spuštění potřebujete přečíst tělo entity požadavku, použijte request.GetBufferlessInputStream nebo Request.GetBufferedInputStream. Když použijete GetBufferlessInputStream, získáte nezpracovaný datový proud z požadavku a převezmete odpovědnost za zpracování celého požadavku. Po volání GetBufferlessInputStream nejsou Request.Form a Request.InputStream k dispozici, protože nebyly naplněny ASP.NET. Když použijete GetBufferedInputStream, získáte kopii streamu z požadavku. Request.Form a Request.InputStream jsou stále k dispozici později v požadavku, protože ASP.NET naplní druhou kopii.

Response.Redirect a Response.End

Doporučení: Mějte na paměti rozdíly ve způsobu zpracování vlákna po volání Response.Redirect(String).

Metoda Response.Redirect(String) volá metodu Response.End. V synchronním procesu volání Request.Redirect způsobí okamžité přerušení aktuálního vlákna. V asynchronním procesu však volání Response.Redirect nepřestane aktuální vlákno, takže provádění kódu pokračuje pro požadavek. V asynchronním procesu je nutné vrátit úlohu z metody zastavit provádění kódu.

V projektu MVC byste neměli volat Response.Redirect. Místo toho vraťte Hodnotu RedirectResult.

EnableViewState a ViewStateMode

Doporučení: Použijte ViewStateMode místo EnableViewState k poskytnutí podrobné kontroly nad tím, které ovládací prvky používají stav zobrazení.

Pokud v direktivě Page nastavíte EnableViewState na hodnotu false, stav zobrazení je zakázán pro všechny ovládací prvky v rámci stránky a nelze ho povolit. Pokud chcete povolit stav zobrazení jenom pro určité ovládací prvky na stránce, nastavte ViewStateMode pro stránku na Zakázáno.

<%@ Page ViewStateMode="Disabled" . . . %>

Pak nastavte ViewStateMode na Povoleno pouze u ovládacích prvků, které skutečně potřebují stav zobrazení.

<asp:GridView ViewStateMode="Enabled" runat="server">

Když povolíte stav zobrazení jenom pro ovládací prvky, které ho potřebují, můžete zmenšit velikost stavu zobrazení webových stránek.

Sqlmembershipprovider

Doporučení: Použijte univerzální zprostředkovatele.

V aktuálních šablonách projektů byl SqlMembershipProvider nahrazen ASP.NET Universal Providers, který je k dispozici jako balíček NuGet. Pokud používáte SqlMembershipProvider v projektu, který byl sestaven pomocí dřívější verze šablon, měli byste přepnout na Univerzální poskytovatele. Univerzální zprostředkovatelé pracují se všemi databázemi, které jsou podporovány rozhraním Entity Framework.

Další informace najdete v tématu Představení univerzálních poskytovatelů ASP.NET.

Dlouhotrvající požadavky (>110 sekund)

Doporučení: Pro připojené klienty používejte protokoly WebSocket nebo SignalR a asynchronní vstupně-výstupní operace.

Dlouhotrvající požadavky můžou způsobit nepředvídatelné výsledky a nízký výkon webové aplikace. Výchozí časový limit požadavku je 110 sekund. Pokud používáte stav relace s dlouhotrvajícím požadavkem, ASP.NET uvolní zámek objektu Relace po 110 sekundách. Aplikace však může být uprostřed operace s objektem Relace, když je zámek uvolněn a operace nemusí být úspěšně dokončena. Pokud je druhý požadavek od uživatele zablokovaný, zatímco je první požadavek spuštěný, druhý požadavek může přistupovat k objektu Relace v nekonzistentním stavu.

Pokud vaše aplikace zahrnuje blokující (nebo synchronní) vstupně-výstupní operace, aplikace přestane reagovat.

Pokud chcete zvýšit výkon, použijte asynchronní vstupně-výstupní operace v rozhraní .NET Framework. Pro připojení klientů k serveru také použijte protokol WebSocket nebo SignalR. Tyto funkce jsou navržené tak, aby efektivně zpracovávaly dlouhotrvající požadavky.