2013. augusztus havi bejegyzések

Tesztelés több Internet Explorer verzión

Gyakran visszatérő kérdés, hogy hogyan lehet kényelmesen több Internet Explorer verzión tesztelni a weboldalunkat. A kérdés azért izgalmas, mert míg más böngészőket lehet  több verzióban egymás mellé telepíteni, addig az Internet Explorernek – mivel össze van nőve az operációs rendszerrel – csak egy verziója lehet a gépünkön.

Korábban írtam már erről, azonban azóta a lehetőségeink csökkentek:

Az IETester ugyan támogatja a Windows 8-at és az IE10-et, de elég lassan fejlődik, és a hiányosságai továbbra is megmaradnak.

A Microsoft Expression SuperPreview-t az egész termékcsaláddal együtt süllyesztőbe küldte a cég, nem fejlesztik tovább.

Az Internet Explorer Developer Toolbar jelentősen megváltozik. A Windows 8.1-ben megjelenő IE11-ben egy teljesen újraírt F12 DevToolst találunk, megújult dizájnnal és számos új funkcióval, azonban pont  a kompatibilitási tesztek terén van egy fájó pont. Korábban ugyanis – egészen az IE10-ig – volt egy remek funkció a Dev Toolbaron a korábbi böngészők emulálására:

IE10 Developer Toolbar: Browser Mode emulation

Ez egyszerűnek és jónak tűnik, csakhogy van vele pár gyakorlati probléma. Először is van mellette egy Document Mode választó is, nagyon hasonló opciókkal:

IE10 Developer Toolbar: Document Mode emulation

IQ-ból nem egyszerű rájönni, hogy melyik mire szolgál, de az IEBlog Testing Sites with Browser Mode vs. Doc Mode című cikkében értelmesen le van írva. Persze ez csak azoknak segít, akik elolvassák.

A másik probléma ezzel a megoldással, hogy sajnos nem teljesen tökéletes. A legtöbb kompatibilitási problémát valóban meg lehet találni vele, de mi is futottunk bele számtalan olyan esetbe, amikor a Dev Toolbaros verzió váltással nem találtunk meg egy hibát, ami viszont egy IE8-on előjött. Magyarul a módszer nem megbízható.

Mindezeket átgondolva az IE csapat úgy döntött, hogy az IE11-ben ezt a funkciót megszűntetik. Így néz ki az új Dev Toolbar Emulation füle:

IE11 Developer Toolbar Emulation tab

Látható, hogy számos új funkció megjelent, van például Desktop és Windows Phone Profile, már nem csak felbontást, hanem tájolást is lehet állítani, sőt van GPS szimuláció is. A böngésző verziókkal kapcsolatban van ugyan egy User Agent string kamuzó opció, sőt van egy Document mode is (ami lehet Edge vagy Default), de nem tudjuk átkapcsolni a böngésző motorját a korábbi verzió motorjára.

Mi a megoldás?

Az információ ikonra kattintva a modern.IE webhelyre juthatunk, azon belül is a Test Across Browsers oldalra, ahol két megoldás találhatunk:

1. A Microsoft a http://www.browserstack.com szolgáltatásait ajánlja, amihez 3 hónapos ingyenes hozzáférést biztosít. Ez az ajánlat 2014. január 10-ig érvényes!

2. Letölthetünk egy rakás előre konfigurált virtuális gépet. Csak a hoszt operációs rendszert és a virtualizációs platformot kell kiválasztanunk és máris tölthetjük lefelé az adott IE verzióval konfigurált Windowsos virtuális gépet:

modern-ie-vms

A Microsoft aktuális álláspontja szerint a virtuális gépek használata a legmegbízhatóbb megoldás a webes megoldások korábbi IE verziókban történő tesztelésére, és ehhez most minden eszközt meg is kapunk.

Ami pedig a jövőben megjelenő verziókat illeti, a javaslat természetesen a szabványok követése. Kezdetnek érdemes elolvasni Rey Bango és Dave Methvin 20 tippjét a modern weboldalak kontra régi IE verziók témában.

 

Technorati-címkék: ,,

WSUS telepítése Windows Server 2012-re

A Windows Server Update Services telepítése Windows Server 2012-n alapvetően nem nehéz feladat: mivel az operációs rendszer része, még letölteni sem kell, csak végig kell nyomkodni a varázslót a Server Managerben. Aki nem hiszi el, nézze végig ezt a képes step-by-step útmutatót.

Ennek ellenére nekem nagyon nehezen jött össze. Ha telepítőkről van szó, én kétségkívül vonzom a hibákat, azonban a neten talált hihetetlen mennyiségű fórum bejegyzés arra utal, hogy nem vagyok egyedül. Íme a tapasztalatok.

Megjegyzés:
Az alábbi módszerek nálam működtek, de egyes esetekben magam sem tudom, hogy miért ez a megoldás a problémára. Csak saját felelősségedre próbáld ki!

A környezetről annyit, hogy egy frissen telepített WS 2012 tartományvezérlőről van szó, amin szépen megférne a WSUS. Nem találtam olyan leírást, ami arra utalna, hogy ez nem támogatott környezet.

NTFS jogok

A Prepare for Your WSUS Deployment című TechNet cikk szerint az NT Authority\Network Service felhasználónak Full Control jogokat kell adni az alábbi mappákra, különben a WSUS Administration snap-in nem fog jól működni:

  • %windir%\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files
  • %windir%\Temp

Ebben az a szép, hogy – ahogy a dokumentáció is írja – az első útvonal nem biztos, hogy létezik, ha még nincs a gépen IIS. A WSUS-nak kell az IIS, és szerencsére elég okos a telepítő ahhoz, hogy szükség esetén a Web Role szerepkört is telepítse. Ráadásul még azt is tudja, hogy az IIS-nek mely komponensei kellenek a WSUS-hoz, tehát a “minimal install” elv jegyében érdemes a WSUS telepítővel telepíteni az IIS-t is. De akkor hogy adjunk jogot még a telepítés előtt a nem létező mappára?

Role Services

A telepítő varázslóban egyszer csak elérkezünk a Role services lépéshez:

WSUS install wizard: Select role services

Ha esetleg lelkesen bepipálnánk az összes komponenst, akkor készüljünk fel az alábbi hibaüzenetre:

The following features cannot be installed on the same server: Database, WID Database.

The following features cannot be installed on the same server: Database, WID Database.

Egy kis magyarázat, mert az elnevezések nem nyilvánvalóak:

  • A WID Database (ami alapértelmezés szerint be van pipálva) azt jelenti, hogy a telepítő feltelepíti a Windows Internal Database-t, ami egy mini SQL Server. Sok megkötése van, de a célra tökéletesen megfelel, különösen egygépes környezetben.
  • A Database (ami alapértelmezés szerint nincs bepipálva) azt jelenti, hogy a telepítő egy létező SQL Serveren hozza létre a WSUS adatbázisát. Ez lehet akár másik gépen is. Ha így döntünk, akkor mindenképpen olvassuk el a WSUS database requirements részt a dokumentációban, mert rengeteg megkötés van.

A lényeg: az alapbeállítás jó, nem kell mindent bepipálni.

Újraindítás eredmény nélkül

Előfordulhat, hogy a telepítő fut egy darabig, majd azt mondja, hogy:

The request to add or remove features on the specified server failed.

The operation cannot be completed, because the server that you specified requires a restart.

The operation cannot be completed, because the server that you specified requires restart.

Persze az ember újraindítja, de utána sem lesz jobb a helyzet, lehet elölről kezdeni a WSUS telepítést, aminek ugyanez lesz az eredménye.

A megoldás – és erre különösen nehéz ráhibázni – egy kis csoportházirend módosítás. Meg kell nyitni a Group Policy Management konzolt, és módosítani kell a Default Domain Controllers Policy-t. A Computer Configuration –> Policies –> Windows Settings –> Security Settings –> Local Policies –> User Rights Assignment ágban található Log on as a service beállításnál fel kell venni az alábbi fiókokat: IIS_WPG, NETWORK, NETWORK SERVICE, SERVICE.

A házirend módosítása után természetesen a szokásos frissítés is szükséges admin parancssorból:

gpupdate /target:computer

Telepítés utáni feladatok

Miután a telepítő varázsló végigfut, találunk egy nem túl feltűnő Launch Post-Installation tasks linket, amire rá kell kattintanunk. Ez gyakorlatilag folytatja a telepítést.

Amíg el nem akad:

Configuration failed. A log file was created at C:\Users\felhasználónév\AppData\Local\Temp\tmpXXXX.tmp

wsus-post-installation

Bátran nézzünk bele a log fájlba, mert elég beszédes, és egyértelműen kiderül a probléma. Például:

Config file did not contain a value "ContentDirectory"
Microsoft.UpdateServices.Administration.CommandException: 
A required configuration value was not found in the system.

Ez azért különösen szép, mert egy olyan értéket hiányol, amit a grafikus telepítő megkérdezett, és amit természetesen meg is adtunk neki. A parancssor kedvelőinek jegyzem meg, hogy a C:\Program Files\Update Services\Tools mappában található egy wsusutil.exe segédprogram, amivel parancssorból el lehet intézni sok mindent, de itt nem segít.

Megsúgom, hogy a telepítő a C:\Windows\System32\ServerManager\ComponentConfiguration\UpdateServices-Services.xml fájlból hiányolja az értéket. Megnyitva a fájlt, ezt találjuk benne (áttördeltem az olvashatóság érdekében):

<?xml version="1.0" encoding="utf-16"?>
<INSTANCE CLASSNAME="ServerComponent_UpdateServices_Services">
<PROPERTY NAME="ContentDirectory" TYPE="string">
</PROPERTY>
<PROPERTY NAME="ContentLocal" TYPE="boolean">
<VALUE>true</VALUE>
</PROPERTY>
</INSTANCE>

Akinek van szeme az XML-hez rögtön észreveszi, hogy a ContentDirectory érték valóban üres. Semmi gond, egészítsük ki:

<?xml version="1.0" encoding="utf-16"?>
<INSTANCE CLASSNAME="ServerComponent_UpdateServices_Services">
<PROPERTY NAME="ContentDirectory" TYPE="string">

<VALUE>C:\WSUS</VALUE>
</PROPERTY>
<PROPERTY NAME="ContentLocal" TYPE="boolean">
<VALUE>true</VALUE>
</PROPERTY>
</INSTANCE>

Rendszergazdai jogosultságokkal indítva a Notepadet el is fogjuk tudni menteni a módosításokat. Így már tovább fut a telepítő.

Adatbázis

Nálam is tovább futott a telepítő, de nem végig. Ugyanígy készült egy naplófájl a Temp mappában, ezúttal ezzel a hibaüzenettel:

Fatal Error: SqlException (0x80131904): Invalid object name ‘SUSDB.dbo.tbSchemaVersion’.

Ez arra utal, hogy nem jó az adatbázis. A logból kiderül az is, hogy az adatbázis létezik, lehet hozzá kapcsolódni, csak épp a keresett tábla nem létezik.

Körülnéztem a C:\Windows\WID\Data mappában, ahol valóban megtaláltam a SUSDB.mdf és SUSDB_log.ldf fájlokat, tehát az adatbázis valóban megvolt, biztos egy korábbi telepítő már létrehozta. Az viszont gyanús volt, hogy ugyanúgy 2112 KB volt a mérete, mint a model.mdf fájlnak, amiből arra tippeltem, hogy ez az adatbázis bizony még szűz.

Ötlet: töröljük le az adatbázist, hátha a telepítő újra létrehozza, ezúttal jól. Érdekes módon a fájlt simán lehetett törölni, de a szolgáltatás újraindítása után a Log mappában lévő error.log fájlban egyértelműen látszott, hogy hiányolja.

Ez a módszer tehát nem jött be, korrekt módon kellene törölni az adatbázist, amihez viszont be kellene jelentkeznünk az adatbázis szerverre. Ha nem akarunk SQL Server Management Studiot telepíteni a szerverre, megcsinálhatjuk parancssorból SQLCMD-vel, ami önállóan is letölthető innen: Microsoft Command Line Utilities 11 for SQL Server

A System Requirements szekcióban megtaláljuk – vagy ha nem, akkor majd az MSI elindítása után közli velünk, – hogy ennek bizony kell az ODBC Driver 11 for SQL Server is.

A két telepítő MSI fájl letöltése önmagában sem egyszerű a szerveren lévő Internet Explorerből. Egyrészt mert a böngésző agresszívan blokkolja a felugró ablakokat, másrészt mert:

Security Alert: Your current security settings do not allow this file to be downloaded.

wsus-ie-msi-download-error

Ha a böngészővel nem akarunk harcolni, az összesen 7MB-nyi tartalmat letölthetjük másik gépen is, és átvihetjük például Remote Desktopon keresztül.

Ha már van SQLCMD, akkor jó lenne azt is tudni, hogy hol. Itt:

C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\110\Tools\Binn

A kapcsolódáshoz kelleni fog egy connection string, ami WID esetén így néz ki:

np:\\.\pipe\MICROSOFT##WID\tsql\query

Én az SQLCMD-t mindig úgy használom, hogy a parancsokat fájlba írom, mert úgy a legkönnyebb újra futtatni őket. Például egy wsus.sql fájlba:

select name from sys.sysdatabases
drop database susdb
select name from sys.sysdatabases

Futtassuk le:

sqlcmd -S np:\\.\pipe\MICROSOFT##WID\tsql\query -i c:\temp\wsus.sql

Adatbázis eldobva, így már a telepítő sikeresen létrehozza, sőt a megfelelő táblákat is megalkotja benne. Elindul a WSUS Administration konzol, a konfigurációs varázsló és megy is minden szépen.

Nem maradt más hátra, mint a kliensek beállítása és az SSL konfigurálása.

 

Technorati-címkék: ,,

Védekezés DoS támadás ellen ASP.NET alkalmazásokban

Egy elterjedt vélekedés szerint a (Distributed) Denial of Service támadások ellen nem lehet hatékonyan védekezni. Pont.

Ebben sok igazság van, különösen akkor, ha a támadás lényege a sok kis kérés küldése. De úgy is neki lehet esni egy szervernek, hogy óriási méretű HTTP kéréseket küldünk neki, ami memória problémákat okozhat. Ez egyébként előfordulhat akkor is, ha nem rosszindulatú, hanem figyelmetlen felhasználóink vannak (azok elég gyakran vannak), akiknek hiába írjuk ki, hogy maximum milyen méretű avatart töltsenek fel, gátlástalanul jön a gigaméretű panorámakép.

A HTTP kérés méretének korlátozásával csökkenthetjük ezt a veszélyt. Azonban nem elég a saját ASP.NET kódunkban vizsgálni a kérés méretét, hiszen addigra az már beérkezett a szerverre, de szerencsére a feldolgozási csővezeték korábbi részeiben mind az ASP.NET, mind pedig az IIS biztosít erre lehetőséget.

Az ASP.NET számára a web.config fájlban a maxRequestLength tulajdonság beállításával szabályozhatjuk a kérés maximális méretét. Itt az értéket kilóbájtban kell megadni és az alapértelmezett érték 4096.

<httpRuntime targetFramework="4.5" maxRequestLength="4096"/>

Azaz a 4MB-nál nagyobb kéréseket az ASP.NET eldobja, méghozzá egy HttpException formájában:

HttpException (0x80004005): Maximum request length exceeded.

Sajnos ez a kivétel nagyon általános, semmi olyan nincs benne, ami specifikusan erre a jelenségre utal (a lokalizált hibaüzenettől eltekintve), így csak az alábbi kóddal tudjuk kezelni:

void Application_Error( object sender, EventArgs e )
{
  HttpRuntimeSection section = ConfigurationManager.GetSection( 
"system.web/httpRuntime" ) as
HttpRuntimeSection; int maxRequestLength = section.MaxRequestLength; if( this.Request.ContentLength > maxRequestLength * 1024 ) { this.Server.ClearError(); // Naplózás, átirányítás stb. } }

Az IIS-ben található Request Filtering modul (sok egyéb funkció mellett) szintén ad lehetőséget a maximális kérés méret beállítására a maxAllowedContentLength tulajdonságban. Itt az alapérték 30000000, ami 28.6MB-nak felel meg, itt ugyanis bájtban kell megadni a limitet:

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="30000000" />
    </requestFiltering>
  </security>
</system.webServer>

Ha a kérés ennél nagyobb, akkor a szerver 404.13 hibát dob:

iis-error-404-13

Ha a standard hibaoldal helyett egy barátságosabbat szeretnénk megjeleníteni, vagy épp naplózni szeretnénk a hibát, akkor ezt szintén a global.asax fájlban tehetjük meg, de az Application_EndRequest eseménykezelőben:

protected void Application_EndRequest( object sender, EventArgs e )
{
  if( this.Response.StatusCode == 404 && this.Response.SubStatusCode == 13 )
  {
    // Naplózás, átirányítás stb.
  }
}

 

Technorati-címkék: ,,,,,

Feltöltött fájl típusának meghatározása

Nem ritka feladat, hogy egy weboldalon szeretnénk megszűrni, hogy a felhasználók milyen típusú fájlokat tölthetnek fel. Fontos, hogy a felhasználó fényképe valóban egy böngészőben megjeleníthető képfájl legyen, vagy hogy a feltöltött XLSX fájl valóban Excel fájl legyen, hiszen különben nem tudjuk belőle kiolvasni az adatokat.

Erre a problémára a klasszikus megoldás a feltöltött állomány kiterjesztésének vizsgálata. Azonban egy percre se felejtsük el, hogy a vizsgált fájlnevet és vele együtt a kiterjesztést is a HTTP kérés részeként kapja meg a weboldal, azaz pontosan annyira bízhatunk meg benne, mint a HTTP kérés bármelyik más részében. Semennyire.

Egy kicsivel jobb megoldás, ha beleolvasunk a fájlba, és megkeressük azokat a bájtokat, amik egy adott fájltípusra jellemzőek. Ezeket a bájtokat hívják “file (type) signature”-nek. Gary Kessler weboldalán elég sok ilyen signature-t összegyűjtött, sőt ott további weboldalakra, adatbázisokra, eszközökre is találhatók hivatkozások.

Számunkra most az a nagy kérdés, hogy .NET kódból hogyan valósítjuk meg ezt a legegyszerűbben. Nyilván tökéletesen működő megoldás, ha beletekerünk a fájlba a megadott offsetig, onnan kiolvassuk a szükséges számú bájtot, majd összehasonlítjuk őket a várt bájtokkal. Ez jól fog működni addig, amíg valamelyik gyártó nem változtat egy kicsit a fájl formátumon, akkor ugyanis frissítetünk kell a kódunkat.

Egy kicsit “hivatalosabb” megoldás, ha a FindMimeFromData Win32 API függvényt használjuk, amit egyébként az Internet Explorer használ MIME type sniffingre. Némi P/Invoke trükközéssel meg is hívhatjuk .NET-ből, ahogy az alábbi példa is mutatja:

using System;
using System.Runtime.InteropServices;

/// <summary>
/// Helper class to detect the MIME type based on the file header signature.
/// </summary>
public static class MimeSniffer
{
    /// <summary>
    /// Internet Explorer 9. Returns image/png and image/jpeg instead of 
///
image/x-png and image/pjpeg. /// </summary> private const uint FMFD_RETURNUPDATEDIMGMIMES = 0x20; /// <summary> /// The zero (0) value for Reserved parameters. /// </summary> private const uint RESERVED = 0; /// <summary> /// The value that is returned when the MIME type cannot be recognized. /// </summary> private const string UNKNOWN = "unknown/unknown"; /// <summary> /// The return value which indicates that the operation completed successfully. /// </summary> private const uint S_OK = 0; /// <summary> /// Determines the MIME type from the data provided. /// </summary> /// <param name="pBC">A pointer to the IBindCtx interface. Can be set to NULL.</param> /// <param name="pwzUrl">A pointer to a string value that contains the URL of the data. Can be set to NULL if <paramref name="pBuffer"/> contains the data to be sniffed.</param> /// <param name="pBuffer">A pointer to the buffer that contains the data to be sniffed. Can be set to NULL if <paramref name="pwzUrl"/> contains a valid URL.</param> /// <param name="cbSize">An unsigned long integer value that contains the size of the buffer.</param> /// <param name="pwzMimeProposed">A pointer to a string value that contains the proposed MIME type. This value is authoritative if type cannot be determined from the data. If the proposed type contains a semi-colon (;) it is removed. This parameter can be set to NULL.</param> /// <param name="dwMimeFlags">The flags which modifies the behavior of the function.</param> /// <param name="ppwzMimeOut">The address of a string value that receives the suggested MIME type.</param> /// <param name="dwReserverd">Reserved. Must be set to 0.</param> /// <returns>S_OK, E_FAIL, E_INVALIDARG or E_OUTOFMEMORY.</returns> /// <remarks> /// Read more: http://msdn.microsoft.com/en-us/library/ms775107(v=vs.85).aspx /// </remarks> [DllImport( @"urlmon.dll", CharSet = CharSet.Auto )] private extern static uint FindMimeFromData( uint pBC, [MarshalAs( UnmanagedType.LPStr )] string pwzUrl, [MarshalAs( UnmanagedType.LPArray )] byte[] pBuffer, uint cbSize, [MarshalAs( UnmanagedType.LPStr )] string pwzMimeProposed, uint dwMimeFlags, out uint ppwzMimeOut, uint dwReserverd ); /// <summary> /// Returns the MIME type for the specified file header. /// </summary> /// <param name="header">The header to examine.</param> /// <returns>The MIME type or "unknown/unknown" if the type cannot be recognized.</returns> /// <remarks> /// NOTE: This method recognizes only 26 types used by IE. /// http://msdn.microsoft.com/en-us/library/ms775147(VS.85).aspx#Known_MimeTypes /// </remarks> public static string GetMime( byte[] header ) { try { uint mimetype; uint result = FindMimeFromData( 0,
null,
header,
(
uint) header.Length,
null,
FMFD_RETURNUPDATEDIMGMIMES,
out mimetype,
RESERVED );
if( result != S_OK ) { return UNKNOWN; } IntPtr mimeTypePtr = new IntPtr( mimetype ); string mime = Marshal.PtrToStringUni( mimeTypePtr ); Marshal.FreeCoTaskMem( mimeTypePtr ); return mime; } catch { return UNKNOWN; } } }

Ez a függvény megbízhatóan működik, azonban csak a 26 leggyakoribb fájltípust képes felismerni. Ami egyébként nem kevés, a fájó pont inkább az, hogy az Office formátumokat ZIP-ként ismeri fel.

Némi keresgéléssel ráakadhatunk a neten más projektekre is, például a CodePlexen található FileTypeDetective kevesebb fájl típust ismer, de pontosabban azonosítja az Office állományokat. Ráadásul a lényeget megtaláljuk a forráskódjában.

Akármelyik módszert választjuk is, ne felejtsük el, hogy bevezettünk egy külső függést a rendszerbe, ráadásul azt sem tudhatjuk, hogy mennyire időtálló a megoldásunk.

 

Technorati-címkék: ,,