Asp.net Security : A case using WebInspect





Recently a client contacted me and showed me an application developed in asp.net and Sql server. They explained me that the temporary site they implemented on a hosting service was tested for vulnerabilities using webinspect , ( a web security scanner  from Hp ) and the results were alarming.  The final user, a  well known local bank , outsourced the vulnerabilities detection service from a Canadian company which in turn submitted a report  from the tool after a 4-day scan.







Web security is a concern, specially for a bank. In an interesting study ,WhiteHat Security  Inc, a US company based on Santa Clara CA, found that "the average website had nearly 13 serious vulnerabilities"  and  "Banking, Insurance, and Healthcare industries performed the best overall regarding the average number of serious vulnerabilities having 5, 6, and 8 respectively. The worst were the IT, Retail, and Education sectors with an average of 24, 17, and 17." This study was conducted on near 2000 websites and 350 US companies. You can read the report from http://img.en25.com/Web/WhiteHatSecurityInc/WPstats_fall10_10th.pdf ( they ask for registration ).





WebInspect outputs a very complete report, with comprehensive description of the vulnerabilites found and the procedures recommended for fixing them.



Here is a summary of the findings:




The Vulnerabilities and their solutions

1 Critical - SQL Injection 

2 High -  Logins Sent Over Unencrypted Connection

3 High - Unencrypted Login Form

4 High   ASP.NET Viewstate Cross-Site Scripting

5 Medium -  ASP.NET Unhashed Viewstate Agent

6  Medium - Cross-Site Request Forgery

7 Low - Microsoft ASP.NET Debugging Enabled

8 Low - CGI and Scripting-Related Directories

9 Low - Common Web Site Structure Directories

10 Low Foreign Language Miscellaneous Directories

11 Low - IIS/Microsoft Directories

Additionally there were many informational and "best practice" messages. They are not considered as vulnerabilities but are mentioned  just to show that the tool is aware of some issues that can supossedly develop in a vulnerability. It is logical that the tool try to bas strict as possible, because what is not vulnerable now can be vulnerable in a future.  It all depends if the security hole is discovered or not. It is important to mention here that  a  tool can issue false positives and you have to be aware of that. Obviously it is better for the tool  to issue a false alarm than miss a real one. It is a responsability of the developer or the Sytem Administrator take the right measures to prevent security problems.


1 - SQL Injection ( Critical  ) 

WebInspect gives an extensive explanation an recommended fixes to resolve the issue found. 

Here is a sample message which is explanatory of the concepts involved. 

Critical SQL Injection vulnerabilities have been identified in the web application. SQL injection is a method of attack where an attacker can exploit vulnerable code and the type of data an application will accept, and can be exploited in any application parameter that influences a database query. Examples include parameters within the url itself, post data, or cookie values. If successful, SQL Injection can give an attacker access to backend database contents, the ability to remotely execute system commands, or in some circumstances the means to take control of the server hosting the database. Recommendations include employing a layered approach to security that includes utilizing parameterized queries when accepting user input, ensuring that only expected data is accepted by an application, and hardening the database server to prevent data from being accessed inappropriately.

Execution:

Consider a login form for a web application. If the user input from the form is directly utilized to build a dynamic SQL statement, then there has been no input validation conducted, giving control to an attacker who wants access to the  database. Basically, an attacker can use an input box to send their own request to the server, and then utilize the results in a malicious manner. This is a very typical scenario considering that HTML pages often use the POST command to send parameters to another ASP page. The number in bold might be supplied by the client in an HTTP GET or POST parameter, like in the following URL:
http://www.example.com/GetItemPrice?ItemNumber=12345
In the example above, the client-supplied value, 12345, is simply used as a numeric expression to indicate the item that the user wants to obtain the price of an item. The web application takes this value and inserts it into the SQL statement in between the single quotes in the WHERE clause. However, consider the following URL: 


http://www.example.com/GetItemPrice?ItemPrice?ItemNumber=0' UNION SELECT CreditCardNumber FROM Customers WHERE '1'='1

In this case, the client-supplied value has actually modified the SQL statement itself and 'injected' a statement of his or her choosing. Instead of the price of an item, this statement will retrieve a customer's credit card number

Implication:

Fundamentally, SQL Injection is an attack upon the web application, not the web server or the operating system itself. As the name implies, SQL Injection is the act of adding an unexpected SQL commands to a query, thereby manipulating the database in ways unintended by the database administrator or developer. When successful, data can be extracted, modified, inserted or deleted from database servers that are used by vulnerable web applications. In certain circumstances, SQL Injection can be utilized to take complete control of a system.

Fix:

Each method of preventing SQL injection has its own limitations. Therefore, it is wise to employ a layered approach to preventing SQL injection, and implement several measures to prevent unauthorized access to your backend database. The
following are recommended courses of action to take to prevent SQL Injection and Blind SQL Injection vulnerabilities from being exploited in your web application.

For Development:
Use the following recommendations to code web applications that are not susceptible to SQL Injection attacks.
· Parameterized Queries: SQL Injection arises from an attacker’s manipulation of query data to modify query logic. The best method of preventing SQL Injection attacks is thereby to separate the logic of a query from its data. This will prevent commands inserted from user input from being executed. The downside of this approach is that it can have an impact on performance, albeit slight, and that each query on the site must be structured in this method for it to be completely effective. If one query is inadvertently bypassed, that could be enough to leave the application vulnerable to SQL Injection. The following code shows a sample SQL statement that is SQL injectable.
sSql = "SELECT LocationName FROM Locations ";
sSql = sSql + " WHERE LocationID = " + Request["LocationID"];
oCmd.CommandText = sSql;
The following example utilizes parameterized queries, and is safe from SQL Injection attacks.
sSql = "SELECT * FROM Locations ";
sSql = sSql + " WHERE LocationID = @LocationID";
oCmd.CommandText = sSql;
oCmd.Parameters.Add("@LocationID", Request["LocationID"]);
The application will send the SQL statement to the server without including the user’s input. Instead, a parameter-
@LocationID- is used as a placeholder for that input. In this way, user input never becomes part of the command that
SQL executes. Any input that an attacker inserts will be effectively negated. An error would still be generated, but it would be a simple data-type conversion error, and not something which a hacker could exploit.
The following code samples show a product ID being obtained from an HTTP query string, and used in a SQL query. Note how the string containing the “SELECT” statement passed to SqlCommand is simply a static string, and is not concatenated from input. Also note how the input parameter is passed using a SqlParameter object, whose name (“@pid”) matches the name used within the SQL query.
C# sample:
string connString = WebConfigurationManager.ConnectionStrings["myConn"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT Count(*) FROM Products WHERE ProdID=@pid", conn);
SqlParameter prm = new SqlParameter("@pid", SqlDbType.VarChar, 50);
prm.Value = Request.QueryString["pid"];
cmd.Parameters.Add(prm);
int recCount = (int)cmd.ExecuteScalar();
}
VB.NET sample:
Dim connString As String = WebConfigurationManager.ConnectionStrings("myConn").ConnectionString
Using conn As New SqlConnection(connString)
conn.Open()
Dim cmd As SqlCommand = New SqlCommand("SELECT Count(*) FROM Products WHERE ProdID=@pid", conn)
Dim prm As SqlParameter = New SqlParameter("@pid", SqlDbType.VarChar, 50)
prm.Value = Request.QueryString("pid")
cmd.Parameters.Add(prm)
Dim recCount As Integer = cmd.ExecuteScalar()
End Using



Validate input: The vast majority of SQL Injection checks can be prevented by properly validating user input for both type and format. The best method of doing this is via “white listing”. This is defined as only accepting specific account numbers or specific account types for those relevant fields, or only accepting integers or letters of the English alphabet for others. Many developers will try to validate input by “black listing” characters, or “escaping” them. Basically, this entails rejecting known bad data, such as a single quotation mark, by placing an “escape” character in front of it so that the item that follows will be treated as a literal value. This approach is not as effective as white listing because it is impossible to know all forms of bad data ahead of time.


For Security Operations:
Use the following recommendations to help prevent SQL Injection attacks upon your web applications.
· Restrict Application Privileges: Limit user credentials so that only those rights the application needs to function are utilized. Any successful SQL Injection attack would run in the context of the user’s credential. While limiting privileges will not prevent SQL Injection attacks outright, it will make them significantly harder to enact.
· Strong SA Password Policy: Often, an attacker will need the functionality of the administrator account to utilize specific SQL commands. It is much easier to “brute force” the SA password when it is weak, and will increase the likelihood of a successful SQL Injection attack. Another option is not to use the SA account at all, and instead create specific accounts for specific purposes.
· Consistent Error Messaging Scheme: Ensure that you provide as little information to the user as possible when a database error occurs. Don’t reveal the entire error message. Error messages need to be dealt with on both the web and application server. When a web server encounters a processing error it should respond with a generic web page, or redirect the user to a standard location. Debug information, or other details that could be useful to a potential attacker, should never be revealed. Application servers, like WebSphere, often install with error messages or debug settings enabled by default. Consult your application server’s documentation for information on suppressing those error messages.
· Stored Procedures: If unused, delete SQL stored procedures such as master..Xp_cmdshell, xp_startmail, xp_sendmail, and sp_makewebtask.
In our particular case the problem was  an input parameter , specifically a text variable used to enter a character string to search for   records in the database. The solution was simply to implement a regular expresion to prevent Sql injection.

ValidationExpression="^[a-zA-Z'.\s | \d | \- | \/ | \$ | \£| \€ | \( | \) | \ | \! | \% | \+ | \& | \, | \! $]{1,200}$"

2 - Medium - Logins Sent Over Unencrypted Connection



Webinspect states :

Any area of a web application that possibly contains sensitive information or access to privileged functionality such as remote site administration functionality should utilize SSL or another form of encryption to prevent login information from being sniffed or otherwise intercepted or stolen. http://www.myprojectSite.com:80/login.aspx has failed this policy. Recommendations include ensuring that sensitive areas of your web application have proper encryption protocols in place to prevent login information and other data that could be helpful to an attacker from being intercepted. 


SSL was implemented on the site but it lacked of a mecanism for automatic redirection.  When SSL is implemented , all secure requests are directed  to port 443. This occurs when the user enters  https://www.myprojectSite.com  instead of http://www.myprojectSite.com, but the user has to explicity begin the http request  by typing https://..., if the user writes http://... he will use the port 80 thus sending the request through an unsecure port.



The solution implies the implementation of WebPageSecurity.dll in \bin and the addition of the following lines in web.config



<section name="secureWebPagestype="Ventaur.Web.Security.Configuration.SecureWebPageSettings,

WebPageSecurity"/>

….

<secureWebPages mode="RemoteOnlyignoreHandlers="WithStandardExtensions">

<files>

<add path="Login.aspx" />

</files>

<directories>

<add path="/recurse="True" /> </directories>

</secureWebPages>



<httpModules>

<add name="WebPageSecurity" type="Ventaur.Web.Security.Secure WebPageModule, WebPageSecurity"

/>



This fix can take less than 5 minutes to implement if you read previously  the excelent article :

http://www.codeproject.com/KB/aspnet/WebPageSecurity.aspx?fid=29017&df=90&mpp=25&noise=3&sort=Position&view=Quick&fr=126



3 High - Unencrypted Login Form.



The login form  should utilize SSL or another form of encryption to prevent login information from being sniffed or otherwise intercepted or stolen. If the login form is being served over SSL, the page that the form is being submitted to MUST be accessed over SSL. Every link/URL present on that page (not just the form action) needs to be served over HTTPS
Our application use a login form, so the information submitted  by using that form could be stolen because of an inapropiate implementation of SSL. The solution was implemented in item 2.



4 High   ASP.NET Viewstate Cross-Site Scripting

Microsoft ASP.NET pages are vulnerable to content modification attacks including Cross-Site Scripting when view state signing is disabled (EnableViewStateMac=false). An attacker can craft a malicious view state value which will overwrite properties on server-based controls, several of which can cause arbitrary HTML to be written to the output. To fix this issue, enable view state signing, or disable view state usage entirely.
It was necesary to change the web.config file :





<pages viewStateEncryptionMode="Always" enableViewStateMac="true" >



<machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey="AutoGenerate,Isola

teApps" validation="AES" decryption="Auto" />





A complete listing of web.config :





<?xml version="1.0"?>

<!--



-->

<configuration>



<configSections>

<section name="secureWebPages" type="Ventaur.Web.Security.Configuration.SecureWebPageSettings, WebPageSecurity"/>

<!--csrfSettings />-->

<section name="csrfSettings"  type="Idunno.AntiCsrf.Configuration.CsrfSettings, Idunno.AntiCsrf" />







<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">

<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">

<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>

<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">

<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>

<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>

<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>

<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>

</sectionGroup>

</sectionGroup>

</sectionGroup>

</configSections>

<secureWebPages mode="RemoteOnly" ignoreHandlers="WithStandardExtensions">

<files>

<add path="Login.aspx" />



</files>

<directories>

<!--<add path="/" recurse="True" />-->

<add path="/" recurse="True" />



</directories>

</secureWebPages>

<!--csrfSettings />-->



<csrfSettings cookieName="__CSRFCOOKIE" formFieldName="__CSRFTOKEN" detectionResult="RaiseException" errorPage="" />







<appSettings/>

<connectionStrings/>

<system.web>





<!--

            Establezca debug="true" en la compilación para insertar símbolos

            de depuración en la página compilada. Dado que este

            proceso afecta al rendimiento, debe establecer este valor como true

            durante la depuración.

        -->

<compilation debug="FALSE">

<assemblies>

<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>

<add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

<add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>

<add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>

<add assembly="System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>

<add assembly="System.Web.Extensions.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

<add assembly="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/></assemblies>

</compilation>

<!--

            La sección <authentication> habilita la configuración

            del modo de autenticación de seguridad utilizado por

            ASP.NET para identificar a un usuario entrante.

        -->

<authentication mode="Windows"/>



<customErrors mode="Off"/>



<!--



 <customErrors mode="RemoteOnly" defaultRedirect="AppErrors.aspx">

<error statusCode="404" redirect="AppErrors.aspx"/>

<error statusCode="403" redirect="AppErrors.aspx"/>

<error statusCode="500" redirect="AppErrors.aspx"/>

</customErrors>

-->

<pages viewStateEncryptionMode="Always"  enableViewStateMac="true" >

<controls>

<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

<add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

</controls>

</pages>

  <machineKey  validationKey="AutoGenerate,IsolateApps"     decryptionKey="AutoGenerate,IsolateApps"     validation="AES"     decryption="Auto" />



<httpHandlers>

<remove verb="*" path="*.asmx"/>

<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>

</httpHandlers>

<httpModules>

<!--csrfSettings />-->

<add name="AntiCSRF" type="Idunno.AntiCsrf.AntiCsrfModule, Idunno.AntiCsrf"/>



<add name="WebPageSecurity" type="Ventaur.Web.Security.SecureWebPageModule, WebPageSecurity" />

<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

</httpModules>

</system.web>

<system.codedom>

<compilers>

<compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">

<providerOption name="CompilerVersion" value="v3.5"/>

<providerOption name="WarnAsError" value="false"/>

</compiler>

<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" warningLevel="4" type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">

<providerOption name="CompilerVersion" value="v3.5"/>

<providerOption name="OptionInfer" value="true"/>

<providerOption name="WarnAsError" value="false"/>

</compiler>

</compilers>

</system.codedom>

<!--

        La sección system.webServer es necesaria para ejecutar ASPN.NET AJAX en Internet

        Information Services 7.0. No es necesaria para la versión anterior de IIS.

    -->

<system.webServer>

<validation validateIntegratedModeConfiguration="false"/>

<modules>

<remove name="ScriptModule"/>

<add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

</modules>

<handlers>

<remove name="WebServiceHandlerFactory-Integrated"/>

<remove name="ScriptHandlerFactory"/>

<remove name="ScriptHandlerFactoryAppServices"/>

<remove name="ScriptResource"/>

<add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

<add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

<add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

</handlers>

</system.webServer>

<runtime>

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

<dependentAssembly>

<assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>

<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>

</dependentAssembly>

<dependentAssembly>

<assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>

<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>

</dependentAssembly>

</assemblyBinding>

</runtime>

</configuration>





End of web.config







Comments

Popular posts from this blog

Crystal Reports Error 1606 could not access network location v4.030319\ASP.NETClientFiles\

Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached."