文章目錄
- Using Forms Authentication
- Windows Authentication
- Examining <allow> and <deny>
- Role-Based Authorization
- Limiting Access to Files and Folders
- Checking Users and Roles Programmatically
- Connecting Without Passwords
- SQL Permissions
- Using Views
- SQL Express User Instances
- Drawbacks of the VS Built-in Web Server
- Dynamic SQL Stored Procedures
- Using SQL Encryption
- Making Static Files Secure
- Making a File Downloadable and Setting Its Name
- Adding Further Checks to File Access
- Accessing Files on a Remote System
- Well-Formed XML
- Valid XML
- XML Parsers
- Avoid XPath Injection
- Encrypting XML Documents
- Signing XML Documents
PART II: Securing Common ASP.NET TasksChapter 7: Adding usernames and passwordsAuthentication and AuthorizationDiscovering Your Own IdentityAdding Authentication in ASP.NETUsing Forms AuthenticationConfiguration Forms AuthenticationUsing SQL as a Membership StoreCreating UsersExamining How Users Are StoredConfiguring the Membership SettingsCreating Users ProgrammaticallySupporting Password Changes and ResetsWindows AuthenticationConfiguring IIS for Windows AuthenticationImpersonation with Windows Authentication
If you want to pass the user’s identity to another system then you need to wrap the call in the follow code:
using (((WindowsIdentity)HttpContext.Current.User.Identity).Impersonate()){ // Perform database or network access here}
Authorization in ASP.NETExamining <allow> and <deny>Role-Based AuthorizationConfiguring Roles with Forms-Based AuthenticationUsing the Configuration Tools to Manage RolesManaging Roles ProgrammaticallyManaging Role Members ProgrammaticallyLimiting Access to Files and FoldersChecking Users and Roles ProgrammaticallySecuring Object ReferencesA checklist for Authentication and Authorization
- Do not roll your own unless you have to.—There are some cases where you may wish to develop own authentication and authorization functions, but doing so is fraught with potential mistakes. If you have an existing user database, then consider implementing the membership and roles provider models. This will enable you to use the standard methods to control access.
- Encourage your uses to logout.—Persistent authentication can lead to CSRF attacks. For high-value systems encourage users to log out by providing a visible and consistent logout button and do not provide “Remember Me” functionality.
- Always start with a deny access role.—Being specific in who you allow to access resources is safer than specifying who does not have access.
- Be aware of the difference between ASP.NET and IIS authorization rules.—IIS authorization rules run against every resource. ASP.NET authorization rules will only protect resources mapped to a managed code handler.
- If you use programmatic authorization checks to hide or display control, ensure those authorization checks run during execution of the underlying code.—If you show or hode user elements such as buttons based on roles or usernames, check again in any method bound to those buttons such as an OnClick() event.
- If resources belong to a user check the current user before serving them.—If a resource such as a message is for a particular user then check the current user has access to that resource.
Chapter 8: Securely accessing databasesWriting Bad Code: Demonstrating SQL InjectionFixing the VulnerabilityMore Security for SQL ServerConnecting Without PasswordsSQL PermissionsAdding a User to a Database
CREATE USER Olle FOR LOGIN Olle;
Managing SQL Permissions
GRANT SELECT ON Example TO PUCK\GuestDENY SELECT ON Example TO OlleREVOKE SELECT ON Example TO OlleGRANT EXECUTE ON GetLogins TO Olle
Groups and Roles
CREATE ROLE auditors AUTHORIZATION db_owner; EXEC sp_addrolemember 'auditors', 'PhilHa' GRANT EXECUTE ON ReadAuditLogin TO auditors
Least Privilege AccountsUsing ViewsSQL Express User InstancesDrawbacks of the VS Built-in Web ServerDynamic SQL Stored Procedures
Wrong dynamic SQL stored procedures:
CREATE PROCEDURE search_orders @custId nchar(5) = NULL, @shipTo nvarchar(40) = NULL ASDECLARE @sql nvarchar(4000)SELECT @sql = ' SELECT OrderID, OrderDate, CustomerID, ShipTo ' + ' FROM dbo.Orders WHERE 1 = 1 'IF @custid IS NOT NULL SELECT @sql = @sql + ' AND custid LIKE ''' + @customerID + '''' IF @shipTo IS NOT NULL SELECT @sql = @sql + ' AND ShipTo LIKE ''' + @shipTo + ''''EXEC(@sql)
Correct one:
CREATE PROCEDURE search_orders @custId nchar(5) = NULL, @shipTo nvarchar(40) = NULL ASDECLARE @sql nvarchar(4000)SELECT @sql = ' SELECT OrderID, OrderDate, CustomerID, ShipName ' + ' FROM dbo.Orders WHERE 1 = 1 'IF @custid IS NOT NULL SELECT @sql = @sql + ' AND CustomerID LIKE @custId 'IF @shipTo IS NOT NULL SELECT @sql = @sql + ' AND ShipName LIKE @shipTo 'EXEC sp_executesql @sql, N'@custid nchar(5), @shipTo nvarchar(40)', @custid, @shipTo
Using SQL EncryptionEncrypting by Pass PhraseSQL Symmetric EntryptionSQL Asymmetric EncryptionCalculting Hashes and HMACs in SQLA Checklist for securely Accessing Databases
- Never dynamically build SQL queries.—Dynamic queries are a vector for SQL injection.
- Always use SQL parameters.—SQL parameters will automatically escape dangerous characters and help you void SQL injection.
- Control access to your data.—If you can use stored procedures and SQL permissions to limit access to the underlying database. If you cannot use stored procedures, use updatable view to limit access to the underlying database. Stored procedures are not a panacea because they can, in turn, contain dynamic SQL themselves.
Chapter 9: Using the file systemAccessing Existing Files SafelyMaking Static Files Secure
use Server.MapPath:
using System;using System.IO;public partial class getfile : System.Web.UI.Page{ protected void Page_Load(object sender, EventArgs e) { Response.Clear(); string filename = Path.GetFileName(Request.QueryString["filename"]); FileInfo file = new FileInfo( Server.MapPath( Path.Combine("documents", filename))); Response.AddHeader("Content - Length", file.Length.ToString()); Response.ContentType = "text/plain"; Response.WriteFile(file.FullName); Response.End(); }
Checking That Your Application Can Access Files
using System;using System.IO;using System.Security;using System.Security.Permissions;public partial class getfile : System.Web.UI.Page{ protected void Page_Load(object sender, EventArgs e) { Response.Clear(); string filename = Path.GetFileName(Request.QueryString["filename"]); string fullPath = Server.MapPath( Path.Combine(@"c:\inetpub\wroxStaticFiles\", ?lename)); FileIOPermission accessPermission = new FileIOPermission(FileIOPermissionAccess.Read, fullPath); try { accessPermission.Demand(); FileInfo file = new FileInfo( Server.MapPath( Path.Combine("documents", filename))); Response.AddHeader("Content-Length", file.Length.ToString()); Response.WriteFile(file.FullName); } catch (SecurityException) { Response.Write("Access Denied"); } Response.End(); }}
Making a File Downloadable and Setting Its Name
You can use the Response headers to set the filename of the file being served by using the Content-Disposition header like so:
Response.AddHeader("Content-Disposition", "filename=" & filename);
Furthermore, you can use Content-Disposition to cause a download rather than rendering in the browser:
Response.AddHeader("Content-Disposition", "attachment; filename=" & filename);
Adding Further Checks to File AccessAdding Role ChecksAnti-Leeching Checks
if (Request.UrlReferrer.Host != "mysite.example" && Request.UrlReferrer.Host != "www.mysite.example"){ Response.End();}else{ // Continue}
Remember that, like anything from the HTTPRequest, the referrer is untrusted input and could be be faked by an attacker. Futhermore, some privacy software strips the referrer from a request, and a direct access to a file via its full URI will also mean the referrer is blank. You should always serve the file if the referrer is blank.
Accessing Files on a Remote SystemCreating Files Safely
Path.GetTempFileName() will generate a filename with a .TMP extension located in the system temporary directory, and create a zero length file. However, the number of temporary file is limited to 65535 files, so old files must be cleaned as quickly as possible.
User Path.GetRandomFileName() to generates a cryptographically strong random value that you can create a file or directory.
A quick and easy scheduler using the ASP.NET cache:
<%@ Application Language="C#" %> <script runat="server"> private const string CleanUpTask = "appCleanFiles"; private static CacheItemRemovedCallback OnCacheRemove = null; void Application_Start(object sender, EventArgs e) { // code that runs on application startup AddTask(CleanUpTask, 60); } private void AddTask(string taskName, int frequency) { OnCacheRemove = this.CacheItemRemoved; HttpRuntime.Cache.Insert(taskName, frequency, null, DateTime.Now.AddSeconds(frequency), Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, OnCacheRemove); } public void CacheItemRemoved(string key, object value, CacheItemRemovedReason reason) { switch (key) { case CleanUpTask: // Perform our cleanup here break; } // re-add our task so it recurs AddTask(key, Convert.ToInt32(value)); } </script>
Handling User UploadsUsing the File Upload Control
For safety, you should ignore any filename sent with the upload request. Instead, you should create a random filename and save the original filename against it. You can then use the original filename as part of a ContentDisposition header when you serve the file back to users.
Change the maximum request size:
<?xml version="1.0"?><configuration > .... <system.web> <httpRuntime executionTimeout = "90" maxRequestLength="4096" /> .... </system.web> ....</configuration>
WARNING: Increasing the maxRequestLength and executionTimeout properties may expose your application to a DOS attack. Experiment with your server configuration to discover the maximum values your hardware will support.
A Checklist for Securely Accessing Files
- Truncate user specific file name and extract just the file name from any potential path.—Use the Path.GetFileName() to safely remove all directory information.
- Serve content from a directory outside of your Web application.—If you must serve content from within your application path, then use Server.MapPath() to resolve directories. Server.MapPath() will stop any directory resolution from escaping out of the root of your application.
- Use code access Secruity demands to ensure your application has the ability to access the file system.—Remember that .NET applications have their own permissions in addition to underlying file system permissions.
- Create your own file names.—Use Path.GetRandomFileName() to generate filenames and directory names when you create files. This will avoid the overwriting of existing files, or any traps with reserved names and directory transversal attacks.
- Limit the maximum request size and execution timeout for your application to prevent DOS attacks.—Never trust any input from file uploads. Generate your own filenames and use indirect object references to retrieve these files.
Chapter 10: Securing XMLValidating XMLWell-Formed XMLValid XMLXML ParsersQuerying XMLAvoid XPath Injection
string xPath = "string(//Account[UserName/text()=$username"+ " and " + "Password/text()=$password]/UserName/text())"; // This could be performed in a constructor to avoid// the CPU hit needed each time an expression is compiled.XPathNavigator navigator = xmlDocument.CreateNavigator();XPathExpression expression = DynamicContext.Compile(xPath); DynamicContext ctx = new DynamicContext();ctx.AddVariable("username", username);ctx.AddVariable("password", password);expression.SetContext(ctx); string account = Convert.ToString(navigator.Evaluate(expression));
Securing XML DocumentsEncrypting XML DocumentsUsing a Symmetric Encryption Key with XMLUsing an Asymmetric Key Pair to Encrypt and Decrypt XMLUsing an X509 Certificate to Encrypt and Decrypt XMLSigning XML DocumentsA Checklist for XML
- XML should be validated before trusting and using it.—Remember that the well-formed status of an XML document is not a guarantee of its validity.
- Validate all XML against a strict schema.—Use local copies of XML schemas whenever possible. If you need to use external schemas, consider caching them with a caching resolver.
- Choose and appropriate encryption method for your situation.—Generally, if your application needs to encrypt and decrypt the same data, choose a symmetric algorithm. If your application talks to an external system, choose an asymmetric algorithm.
- Always use digital signatures if you need to ensure data has not changed.—Encryption is not enough when you cannot detect changes in data. Even unencrypted data may need a mechanism for detecting changes. Use digital signing to provide a security mechanism against unauthorized modification.