Rules
Injection
References
OWASP: Top 10 2013-A1-Injection
SCS0001 - Command Injection
The dynamic value passed to the command execution should be validated.
Risk
If a malicious user controls either the FileName or Arguments, he might be able to execute unwanted commands or add unwanted argument. This behavior would not be possible if input parameter are validate against a white-list of characters.
Vulnerable Code
var p = new Process();
p.StartInfo.FileName = "exportLegacy.exe";
p.StartInfo.Arguments = " -user " + input + " -role user";
p.Start();
Solution
Regex rgx = new Regex(@"^[a-zA-Z0-9]+$");
if(rgx.IsMatch(input))
{
var p = new Process();
p.StartInfo.FileName = "exportLegacy.exe";
p.StartInfo.Arguments = " -user " + input + " -role user";
p.Start();
}
References
CWE-78: Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’)
OWASP: Command Injection
OWASP: Top 10 2013-A1-Injection
SCS0002 - SQL Injection
SQL injection flaws are introduced when software developers create dynamic database queries that include user supplied input.
Risk
Malicious user might get direct read and/or write access to the database. If the database is poorly configured the attacker might even get Remote Code Execution (RCE) on the machine running the database.
Vulnerable Code
var cmd = "SELECT * FROM Users WHERE username = '" + input + "' and role='user'";
ctx.Database.ExecuteSqlCommand(
cmd);
Solution
Use parametrized queries to mitigate SQL injection.
var cmd = "SELECT * FROM Users WHERE username = @username and role='user'";
ctx.Database.ExecuteSqlCommand(
cmd,
new SqlParameter("@username", input));
References
CWE-89: Improper Neutralization of Special Elements used in an SQL Command (‘SQL Injection’)
WASC-19: SQL Injection
OWASP: SQL Injection Prevention Cheat Sheet
OWASP: Query Parameterization Cheat Sheet
CAPEC-66: SQL Injection
Bobby Tables: A guide to preventing SQL injection
SCS0003 - XPath Injection
The dynamic value passed to the XPath query should be validated.
Risk
If the user input is not properly filtered, a malicious user could extend the XPath query.
Vulnerable Code
var doc = new XmlDocument {XmlResolver = null};
doc.Load("/config.xml");
var results = doc.SelectNodes("/Config/Devices/Device[id='" + input + "']");
Solution
Regex rgx = new Regex(@"^[a-zA-Z0-9]+$");
if(rgx.IsMatch(input)) //Additional validation
{
XmlDocument doc = new XmlDocument {XmlResolver = null};
doc.Load("/config.xml");
var results = doc.SelectNodes("/Config/Devices/Device[id='" + input + "']");
}
References
CWE-643: Improper Neutralization of Data within XPath Expressions (‘XPath Injection’)
WASC-39: XPath Injection
OWASP: XPATH Injection
Black Hat Europe 2012: Hacking XPath 2.0
SCS0007 - XML eXternal Entity Injection (XXE)
The XML parser is configured incorrectly. The operation could be vulnerable to XML eXternal Entity (XXE) processing.
Risk
Vulnerable Code
Prior to .NET 4.5.2
// DTD expansion is enabled by default
XmlReaderSettings settings = new XmlReaderSettings();
XmlReader reader = XmlReader.Create(inputXml, settings);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(pathToXmlFile);
Console.WriteLine(xmlDoc.InnerText);
Solution
Prior to .NET 4.5.2
var settings = new XmlReaderSettings();
// Prior to .NET 4.0
settings.ProhibitDtd = true; // default is false!
// .NET 4.0 - .NET 4.5.2
settings.DtdProcessing = DtdProcessing.Prohibit; // default is DtdProcessing.Parse!
XmlReader reader = XmlReader.Create(inputXml, settings);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.XmlResolver = null; // Setting this to NULL disables DTDs - Its NOT null by default.
xmlDoc.Load(pathToXmlFile);
Console.WriteLine(xmlDoc.InnerText);
.NET 4.5.2 and later
In .NET Framework versions 4.5.2 and up, XmlTextReader’s internal XmlResolver is set to null by default, making the XmlTextReader ignore DTDs by default. The XmlTextReader can become unsafe if if you create your own non-null XmlResolver with default or unsafe settings.
References
CWE-611: Improper Restriction of XML External Entity Reference (‘XXE’)
OWASP.org: XML External Entity (XXE) Prevention Cheat Sheet (.NET)
CERT: IDS10-J. Prevent XML external entity attacks
OWASP.org: XML External Entity (XXE) Processing
WS-Attacks.org: XML Entity Expansion
WS-Attacks.org: XML External Entity DOS
WS-Attacks.org: XML Entity Reference Attack
Identifying Xml eXternal Entity vulnerability (XXE)
SCS0018 - Path Traversal
A path traversal attack (also known as directory traversal) aims to access files and directories that are stored outside the expected directory.By manipulating variables that reference files with “dot-dot-slash (../)” sequences and its variations or by using absolute file paths, it may be possible to access arbitrary files and directories stored on file system including application source code or configuration and critical system files.
Risk
With a malicious relative path, an attacker could reach a secret file.
Vulnerable Code
[RedirectingAction]
public ActionResult Download(string fileName)
{
byte[] fileBytes = System.IO.File.ReadAllBytes(Server.MapPath("~/ClientDocument/") + fileName);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
The following request downloads a file of the attacker choice: http://www.address.com/Home/Download?fileName=../../somefile.txt
Solution
Do not try to strip invalid characters. Fail if any unexpected character is detected.
private static readonly char[] InvalidFilenameChars = Path.GetInvalidFileNameChars();
[RedirectingAction]
public ActionResult Download(string fileName)
{
if (fileName.IndexOfAny(InvalidFilenameChars) >= 0)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
byte[] fileBytes = System.IO.File.ReadAllBytes(Server.MapPath("~/ClientDocument/") + fileName);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
If the input is not supplied by user or a validation is in place the warning can be suppressed.
References
CWE-22: Improper Limitation of a Pathname to a Restricted Directory (‘Path Traversal’)
OWASP: Path Traversal
OS Command Injection, Path Traversal & Local File Inclusion Vulnerability - Notes
SCS0029 - Cross-Site Scripting (XSS)
A potential XSS was found. The endpoint returns a variable from the client input that has not been encoded. To protect against stored XSS attacks, make sure any dynamic content coming from user or data store cannot be used to inject JavaScript on a page. Most modern frameworks will escape dynamic content by default automatically (Razor for example) or by using special syntax (<%: content %>
, <%= HttpUtility.HtmlEncode(content) %>
).
Risk
XSS could be used to execute unwanted JavaScript in a client’s browser. XSS can be used to steal the cookie containing the user’s session ID. There is rarely a good reason to read or manipulate cookies in client-side JavaScript, so consider marking cookies as HTTP-only, meaning that cookies will be received, stored, and sent by the browser, but cannot be modified or read by JavaScript.
Vulnerable Code
public class TestController : Controller
{
[HttpGet(""{myParam}"")]
public string Get(string myParam)
{
return "value " + myParam;
}
}
Solution
public class TestController : Controller
{
[HttpGet(""{myParam}"")]
public string Get(string myParam)
{
return "value " + HttpUtility.HtmlEncode(myParam);
}
}
References
CWE-79: Improper Neutralization of Input During Web Page Generation (‘Cross-site Scripting’)
WASC-8: Cross Site Scripting
OWASP: XSS Prevention Cheat Sheet
OWASP: Top 10 2013-A3: Cross-Site Scripting (XSS)
SCS0026 - LDAP Distinguished Name Injection
The dynamic value passed to the LDAP query should be validated.
Risk
If the user input is not properly filtered, a malicious user could extend the LDAP query.
Vulnerable Code
var dir = new DirectoryEntry();
dir.Path = $"GC://DC={input},DC=com";
Solution
Use proper encoder (LdapFilterEncode
or LdapDistinguishedNameEncode
) from AntiXSS library:
var dir = new DirectoryEntry();
dir.Path = $"GC://DC={Encoder.LdapDistinguishedNameEncode(input)},DC=com";
References
CWE-90: Improper Neutralization of Special Elements used in an LDAP Query (‘LDAP Injection’)
WASC-29: LDAP Injection
OWASP: LDAP Injection
OWASP: LDAP Injection Prevention Cheat Sheet
MSDN Blog - Security Tools: LDAP Injection and mitigation
SCS0031 - LDAP Filter Injection
The dynamic value passed to the LDAP query should be validated.
Risk
If the user input is not properly filtered, a malicious user could extend the LDAP query.
Vulnerable Code
var searcher = new DirectorySearcher();
searcher.Filter = "(cn=" + input + ")";
Solution
Use proper encoder (LdapFilterEncode
or LdapDistinguishedNameEncode
) from AntiXSS library:
var searcher = new DirectorySearcher();
searcher.Filter = "(cn=" + Encoder.LdapFilterEncode(input) + ")";
References
CWE-90: Improper Neutralization of Special Elements used in an LDAP Query (‘LDAP Injection’)
WASC-29: LDAP Injection
OWASP: LDAP Injection
OWASP: LDAP Injection Prevention Cheat Sheet
MSDN Blog - Security Tools: LDAP Injection and mitigation
Cryptography
SCS0004 - Certificate Validation Disabled
Certificate Validation has been disabled. The communication could be intercepted.
Risk
Disabling certificate validation is often used to connect easily to a host that is not signed by a root certificate authority. As a consequence, this is vulnerable to Man-in-the-middle attacks since the client will trust any certificate.
Vulnerable Code
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
Solution
- Make sure the validation is disabled only in testing environment or
- Use certificate pinning for development or
- Use properly signed certificates for development
#if DEBUG
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
#endif
References
CWE-295: Improper Certificate Validation
WASC-04: Insufficient Transport Layer Protection
SCS0005 - Weak Random Number Generator
The random numbers generated could be predicted.
Risk
The use of a predictable random value can lead to vulnerabilities when used in certain security critical contexts.
Vulnerable Code
var rnd = new Random();
byte[] buffer = new byte[16];
rnd.GetBytes(buffer);
return BitConverter.ToString(buffer);
Solution
using System.Security.Cryptography;
var rnd = RandomNumberGenerator.Create();
References
CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)
OWASP: Insecure Randomness
SCS0006 - Weak hashing function
MD5 or SHA1 have known collision weaknesses and are no longer considered strong hashing algorithms.
Vulnerable Code
var hashProvider = new SHA1CryptoServiceProvider();
var hash = hashProvider.ComputeHash(str);
Solution
Use SHA256 or SHA512. Notice, that hashing algorithms are designed to be fast and shouldn’t be used directly for hashing passwords. Use adaptive algorithms for the purpose.
var hashProvider = SHA256Managed.Create();
var hash = hashProvider.ComputeHash(str);
References
CWE-327: Use of a Broken or Risky Cryptographic Algorithm
MSDN: SHA256 Class documentation
Salted Password Hashing - Doing it Right
SCS0010 - Weak cipher algorithm
DES and 3DES are not considered a strong cipher for modern applications. Currently, NIST recommends the usage of AES block ciphers instead.
Risk
Broken or deprecated ciphers have typically known weakness. A attacker might be able to brute force the secret key use for the encryption. The confidentiality and integrity of the information encrypted is at risk.
Vulnerable Code
DES DESalg = DES.Create();
// Create a string to encrypt.
byte[] encrypted;
ICryptoTransform encryptor = DESalg.CreateEncryptor(key, zeroIV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt,
encryptor,
CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(Data);
}
encrypted = msEncrypt.ToArray();
return encrypted;
}
}
Solution
Use AES for symmetric encryption.
// Create a string to encrypt.
byte[] encrypted;
var encryptor = new AesManaged();
encryptor.Key = key;
encryptor.GenerateIV();
var iv = encryptor.IV;
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt,
encryptor.CreateEncryptor(),
CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(Data);
}
encrypted = msEncrypt.ToArray();
return encrypted;
}
}
Notice that AES itself doesn’t protect from encrypted data tampering. For an example of authenticated encryption see the Solution in Weak Cipher Mode
References
CWE-327: Use of a Broken or Risky Cryptographic Algorithm
NIST Withdraws Outdated Data Encryption Standard
StackOverflow: Authenticated encryption example
SCS0013 - Potential usage of weak CipherMode mode
The cipher text produced is susceptible to alteration by an adversary.
Risk
The cipher provides no way to detect that the data has been tampered with. If the cipher text can be controlled by an attacker, it could be altered without detection. The use of AES in CBC mode with a HMAC is recommended guaranteeing integrity and confidentiality.
Vulnerable Code
using (var aes = new AesManaged {
KeySize = KeyBitSize,
BlockSize = BlockBitSize,
Mode = CipherMode.OFB,
Padding = PaddingMode.PKCS7
})
{
using (var encrypter = aes.CreateEncryptor(cryptKey, new byte[16]))
using (var cipherStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cryptoStream))
{
//Encrypt Data
binaryWriter.Write(secretMessage);
}
cipherText = cipherStream.ToArray();
}
}
//Missing HMAC suffix to assure integrity
Solution
Using bouncy castle:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
public static readonly int BlockBitSize = 128;
public static readonly int KeyBitSize = 256;
public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] key)
{
//User Error Checks
if (key == null || key.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "key");
if (secretMessage == null || secretMessage.Length == 0)
throw new ArgumentException("Secret Message Required!", "secretMessage");
//Using random nonce large enough not to repeat
var nonce = new byte[NonceBitSize / 8];
Random.NextBytes(nonce, 0, nonce.Length);
var cipher = new GcmBlockCipher(new AesFastEngine());
var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, new byte[0]);
cipher.Init(true, parameters);
//Generate Cipher Text With Auth Tag
var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)];
var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0);
cipher.DoFinal(cipherText, len);
//Assemble Message
using (var combinedStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(combinedStream))
{
//Prepend Nonce
binaryWriter.Write(nonce);
//Write Cipher Text
binaryWriter.Write(cipherText);
}
return combinedStream.ToArray();
}
}
Custom implementation of Encrypt and HMAC:
using System.IO;
using System.Security.Cryptography;
public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null)
{
//User Error Checks
if (cryptKey == null || cryptKey.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "cryptKey");
if (authKey == null || authKey.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "authKey");
if (secretMessage == null || secretMessage.Length < 1)
throw new ArgumentException("Secret Message Required!", "secretMessage");
byte[] cipherText;
byte[] iv;
using (var aes = new AesManaged {
KeySize = KeyBitSize,
BlockSize = BlockBitSize,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
//Use random IV
aes.GenerateIV();
iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(cryptKey, iv))
using (var cipherStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cryptoStream))
{
//Encrypt Data
binaryWriter.Write(secretMessage);
}
cipherText = cipherStream.ToArray();
}
}
//Assemble encrypted message and add authentication
using (var hmac = new HMACSHA256(authKey))
using (var encryptedStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(encryptedStream))
{
//Prepend IV
binaryWriter.Write(iv);
//Write Ciphertext
binaryWriter.Write(cipherText);
binaryWriter.Flush();
//Authenticate all data
var tag = hmac.ComputeHash(encryptedStream.ToArray());
//Postpend tag
binaryWriter.Write(tag);
}
return encryptedStream.ToArray();
}
}
References
CWE-327: Use of a Broken or Risky Cryptographic Algorithm
Padding Oracles for the masses (by Matias Soler)
Wikipedia: Authenticated encryption
NIST: Authenticated Encryption Modes
CAPEC: Padding Oracle Crypto Attack
Wikipedia: ECB mode
Cookies
SCS0008 - Cookie Without SSL Flag
It is recommended to specify the Secure flag to new cookie.
Risk
The Secure flag is a directive to the browser to make sure that the cookie is not sent by unencrypted channel
Vulnerable Code
The requireSSL
value is explicitly set to false
or the default is left.
<httpCookies requireSSL="false" [..] />
// default is left
var cookie = new HttpCookie("test");
// or explicitly set to false
var cookie = new HttpCookie("test");
cookie.Secure = false;
Solution
<httpCookies requireSSL="true" [..] />
var cookie = new HttpCookie("test");
cookie.Secure = true; //Add this flag
cookie.HttpOnly = true;
References
CWE-614: Sensitive Cookie in HTTPS Session Without ‘Secure’ Attribute
OWASP: Secure Flag
Rapid7: Missing Secure Flag From SSL Cookie
SCS0009 - Cookie Without HttpOnly Flag
It is recommended to specify the HttpOnly flag to new cookie.
Risk
Cookies that doesn’t have the flag set are available to JavaScript running on the same domain. When a user is the target of a “Cross-Site Scripting”, the attacker would benefit greatly from getting the session id.
Vulnerable Code
The httpOnlyCookies
value is explicitly set to false
or the default is left.
<httpCookies httpOnlyCookies="false" [..] />
// default is left
var cookie = new HttpCookie("test");
// or explicitly set to false
var cookie = new HttpCookie("test");
cookie.HttpOnly = false;
Solution
<httpCookies httpOnlyCookies="true" [..] />
var cookie = new HttpCookie("test");
cookie.Secure = true;
cookie.HttpOnly = true; //Add this flag
References
CWE-1004: Sensitive Cookie Without ‘HttpOnly’ Flag
Coding Horror blog: Protecting Your Cookies: HttpOnly
OWASP: HttpOnly
Rapid7: Missing HttpOnly Flag From Cookie
View State
SCS0023 - View State Not Encrypted
The viewStateEncryptionMode
is not set to Always
in configuration file.
Risk
Web Forms controls use hidden base64 encoded fields to store state information. If sensitive information is stored there it may be leaked to the client side.
Vulnerable Code
The default value is Auto
:
<system.web>
...
<pages [..] viewStateEncryptionMode="Auto" [..]/>
...
</system.web>
or
<system.web>
...
<pages [..] viewStateEncryptionMode="Never" [..]/>
...
</system.web>
Solution
Explicitly set to Always
and encrypt with with the .NET machine key:
<system.web>
...
<pages [..] viewStateEncryptionMode="Always" [..]/>
...
</system.web>
References
CWE-554: ASP.NET Misconfiguration: Not Using Input Validation Framework
MSDN: pages Element (ASP.NET Settings Schema)
MSDN: ViewStateEncryptionMode Property
MSDN: machineKey Element (ASP.NET Settings Schema)
SCS0024 - View State MAC Disabled
The enableViewStateMac
is disabled in configuration file. (This feature cannot be disabled starting .NET 4.5.1)
Risk
The view state could be altered by an attacker.
Vulnerable Code
<system.web>
...
<pages [..] enableViewStateMac="false" [..]/>
...
</system.web>
Solution
The default value is secure - true
. Or set it explicitly:
<system.web>
...
<pages [..] enableViewStateMac="true" [..]/>
...
</system.web>
References
CWE-554: ASP.NET Misconfiguration: Not Using Input Validation Framework
MSDN: pages Element (ASP.NET Settings Schema)
Request Validation
SCS0017 - Request Validation Disabled (Attribute)
Request validation is disabled. Request validation allows the filtering of some XSS patterns submitted to the application.
Risk
Vulnerable Code
public class TestController
{
[HttpPost]
[ValidateInput(false)]
public ActionResult ControllerMethod(string input) {
return f(input);
}
}
Solution
Although it performs blacklisting (that is worse than whitelisting by definition) and you should not rely solely on it for XSS protection, it provides a first line of defense for your application. Do not disable the validation:
public class TestController
{
[HttpPost]
public ActionResult ControllerMethod(string input) {
return f(input);
}
}
Always user proper encoder (Html, Url, etc.) before displaying or using user supplied data (even if it is loaded from database).
References
CWE-554: ASP.NET Misconfiguration: Not Using Input Validation Framework
MSDN: Request Validation in ASP.NET
OWASP: ASP.NET Request Validation
See XSS references.
SCS0021 - Request Validation Disabled (Configuration File)
The validateRequest
which provides additional protection against XSS is disabled in configuration file.
Risk
Vulnerable Code
<system.web>
...
<pages [..] validateRequest="false" [..]/>
...
</system.web>
Solution
Although it performs blacklisting (that is worse than whitelisting by definition) and you should not rely solely on it for XSS protection, it provides a first line of defense for your application. Do not disable the validation: The default value is true
. Or set it explicitly:
<system.web>
...
<pages [..] validateRequest="true" [..]/>
...
</system.web>
References
CWE-554: ASP.NET Misconfiguration: Not Using Input Validation Framework
MSDN: pages Element (ASP.NET Settings Schema)
MSDN: Request Validation in ASP.NET
OWASP: ASP.NET Request Validation
See XSS references.
SCS0030 - Request validation is enabled only for pages (Configuration File)
The requestValidationMode
which provides additional protection against XSS is enabled only for pages, not for all HTTP requests in configuration file.
Risk
Vulnerable Code
<system.web>
...
<httpRuntime [..] requestValidationMode="2.0" [..]/>
...
</system.web>
Solution
<system.web>
...
<httpRuntime [..] requestValidationMode="4.5" [..]/>
...
</system.web>
References
CWE-554: ASP.NET Misconfiguration: Not Using Input Validation Framework
MSDN: pages Element (ASP.NET Settings Schema)
MSDN: Request Validation in ASP.NET
OWASP: ASP.NET Request Validation
MSDN: RequestValidationMode Property
See XSS references.
Password Management
SCS0015 - Hardcoded Password
The password configuration to this API appears to be hardcoded.
Risk
If hard-coded passwords are used, it is almost certain that malicious users will gain access through the account in question.
Vulnerable Code
config.setPassword("NotSoSecr3tP@ssword");
Solution
It is recommended to externalize configuration such as password to avoid leakage of secret information. The source code or its binary form is more likely to be accessible by an attacker than a production configuration. To be managed safely, passwords and secret keys should be stored encrypted in separate configuration files. The certificate for decryption should be installed as non-exportable on the server machine.
Configuration file :
<configuration>
<appSettings>
<add key="api_password" value="b3e521073ca276dc2b7caf6247b6ddc72d5e2d2d" />
</appSettings>
</configuration>
Code:
string apiPassword = ConfigurationManager.AppSettings["api_password"];
config.setPassword(apiPassword);
References
CWE-259: Use of Hard-coded Password
SCS0034 - Password RequiredLength Not Set
The RequiredLength property must be set with a minimum value of 8.
Risk
Weak password can be guessed or brute-forced.
Vulnerable Code
ASP.NET Identity default is 6.
PasswordValidator pwdv = new PasswordValidator();
Solution
See the solution for Password Complexity
References
CWE-521: Weak Password Requirements
MSDN: ASP.NET Identity PasswordValidator Class
SCS0032 - Password RequiredLength Too Small
The minimal length of a password is recommended to be set at least to 8.
Risk
Weak password can be guessed or brute-forced.
Vulnerable Code
PasswordValidator pwdv = new PasswordValidator
{
RequiredLength = 6,
};
Solution
See the solution for Password Complexity
References
CWE-521: Weak Password Requirements
MSDN: ASP.NET Identity PasswordValidator Class
SCS0033 - Password Complexity
PasswordValidator should have at least two requirements for better security (RequiredLength, RequireDigit, RequireLowercase, RequireUppercase and/or RequireNonLetterOrDigit).
Risk
Weak password can be guessed or brute-forced.
Vulnerable Code
PasswordValidator pwdv = new PasswordValidator
{
RequiredLength = 6,
};
Solution
PasswordValidator pwdv = new PasswordValidator
{
RequiredLength = 8,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
References
CWE-521: Weak Password Requirements
MSDN: ASP.NET Identity PasswordValidator Class
Other
SCS0011 - Unsafe XSLT setting used
XSLT scripting should be enabled only if you require script support and you are working in a fully trusted environment.
Risk
This issue may lead to Remote Code Execution (RCE) if the XML source is untrusted.
Vulnerable Code
XslCompiledTransform transform = new XslCompiledTransform();
XsltSettings settings = new XsltSettings() {EnableScript = true};
transform.Load(xslPath, settings, null);
// Execute the transformation.
transform.Transform(reader, writer);
References
CWE-611: Improper Restriction of XML External Entity Reference
XSLT Server Side Injection Attacks
XML Attack for C# Remote Code Execution
XsltSettings.EnableScript Property
SCS0012 - Controller method is potentially vulnerable to authorization bypass
Neither the annotation [Authorize], nor [AllowAnonymous] is present.
Risk
The endpoint is potentially accessible to not authorized users. If it contains sensitive information, like log files for example, it may lead to privilege escalation. The warning may be ignored/suppressed if the application is using other authorization checks. It is possible to customize the rule and register the additional attributes.
Vulnerable Code
public class AccountController : Controller
{
public ActionResult Login()
{
}
[Authorize]
public ActionResult Logout()
{
}
}
Solution
[Authorize]
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Login()
{
}
public ActionResult Logout()
{
}
}
References
CWE-284: Improper Access Control
Access control vulnerabilities and privilege escalation
Simple authorization in ASP.NET Core
SCS0016 - Cross-Site Request Forgery (CSRF)
Anti-forgery token is missing.
Risk
An attacker could send a link to the victim. By visiting the malicious link, a web page would trigger a POST request (because it is a blind attack - the attacker doesn’t see a response from triggered request and has no use from GET request and GET requests should not change a state on the server by definition) to the website. The victim would not be able to acknowledge that an action is made in the background, but his cookie would be automatically submitted if he is authenticated to the website. This attack does not require special interaction other than visiting a website.
Vulnerable Code
public class TestController
{
[HttpPost]
public ActionResult ControllerMethod(string input)
{
//Do an action in the context of the logged in user
}
}
Solution
In your view:
@Html.AntiForgeryToken()
In your controller:
public class TestController
{
[HttpPost]
[ValidateAntiForgeryToken] //Annotation added
public ActionResult ControllerMethod(string input)
{
//Do an action in the context of the logged in user
}
}
References
CWE-352: Cross-Site Request Forgery (CSRF)
OWASP: Cross-Site Request Forgery
OWASP: CSRF Prevention Cheat Sheet
SCS0019 - OutputCache Conflict
Caching conflicts with authorization.
Risk
Having the annotation [OutputCache] will disable the annotation [Authorize] for the requests following the first one.
Vulnerable Code
[Authorize]
public class AdminController : Controller
{
[OutputCache]
public ActionResult Index()
{
return View();
}
}
Solution
Remove the caching:
[Authorize]
public class AdminController : Controller
{
public ActionResult Index()
{
return View();
}
}
References
CWE-524: Use of Cache Containing Sensitive Information
Improving Performance with Output Caching
SCS0022 - Event Validation Disabled
The enableEventValidation
is disabled in configuration file.
Risk
This feature reduces the risk of unauthorized or malicious post-back requests and callbacks. It is strongly recommended that you do not disable event validation. When the EnableEventValidation property is set to true, ASP.NET validates that a control event originated from the user interface that was rendered by that control.
Vulnerable Code
<system.web>
...
<pages [..] enableEventValidation="false" [..]/>
...
</system.web>
Solution
The default value is secure - true
. Or set it explicitly:
<system.web>
...
<pages [..] enableEventValidation="true" [..]/>
...
</system.web>
References
CWE-554: ASP.NET Misconfiguration: Not Using Input Validation Framework
MSDN: pages Element (ASP.NET Settings Schema)
MSDN: Page.EnableEventValidation Property
SCS0027 - Open Redirect
The dynamic value passed to the Redirect
should be validated.
Risk
Your site may be used in phishing attacks. An attacker may craft a trustworthy looking link to your site redirecting a victim to a similar looking malicious site: https://www.yourdomain.com/loginpostback?redir=https://www.urdomain.com/login
Vulnerable Code
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Solution
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl)) // Make sure the url is relative, not absolute path
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
References
CWE-601: URL Redirection to Untrusted Site (‘Open Redirect’)
Microsoft: Preventing Open Redirection Attacks (C#)
OWASP: Unvalidated Redirects and Forwards Cheat Sheet
Hacksplaining: preventing malicious redirects
SCS0028 - Insecure Deserialization
Untrusted data passed for deserialization.
Risk
Arbitrary code execution, full application compromise or denial of service. An attacker may pass specially crafted serialized .NET object of specific class that will execute malicious code during the construction of the object.
Vulnerable Code
private void ConvertData(string json)
{
var mySerializer = new JavaScriptSerializer(new SimpleTypeResolver());
Object mything = mySerializer.Deserialize(json, typeof(SomeDataClass)/* the type doesn't matter */);
}
Solution
There is no simple fix. Do not deserialize untrusted data: user input, cookies or data that crosses trust boundaries.
In case it is unavoidable:
1) If serialization is done on the server side, then crosses trust boundary, but is not modified and is returned back (like cookie for example) - use signed cryptography (HMAC for instance) to ensure it wasn’t tampered.
2) Do not get the type to deserialize into from untrusted source: the serialized stream itself or other untrusted parameter. BinaryFormatter
for example reads type information from serialized stream itself and can’t be used with untrusted streams:
// DO NOT DO THIS!
var thing = (MyType)new BinaryFormatter().Deserialize(untrustedStream);
JavaScriptSerializer for instance without a JavaScriptTypeResolver is safe because it doesn’t resolve types at all:
private void ConvertData(string json)
{
var mySerializer = new JavaScriptSerializer(/* no resolver here */);
Object mything = mySerializer.Deserialize(json, typeof(SomeDataClass));
}
Pass the expected type (may be hardcoded) to the deserialization library. Some libraries like Json.Net, DataContractJsonSerializer and FSPickler validate expected object graph before deserialization. However the check is not bulletproof if the expected type contains field or property of System.Object
type somewhere nested in hierarchy.
// Json.net will inspect if the serialized data is the Expected type
var data = JsonConvert.DeserializeObject<Expected>(json, new
JsonSerializerSettings
{
// Type information is not used, only simple types like int, string, double, etc. will be resolved
TypeNameHandling = TypeNameHandling.None
});
// DO NOT DO THIS! The cast to MyType happens too late, when malicious code was already executed
var thing = (MyType)new BinaryFormatter().Deserialize(untrustedStream);
3) If the library supports implement a callback that verifies if the object and its properties are of expected type (don’t blacklist, use whitelist!):
class LimitedBinder : SerializationBinder
{
List<Type> allowedTypes = new List<Type>()
{
typeof(Exception),
typeof(List<Exception>),
};
public override Type BindToType(string assemblyName, string typeName)
{
var type = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName), true);
foreach(Type allowedType in allowedTypes)
{
if(type == allowedType)
return allowedType;
}
// Don’t return null for unexpected types –
// this makes some serializers fall back to a default binder, allowing exploits.
throw new Exception("Unexpected serialized type");
}
}
var formatter = new BinaryFormatter() { Binder = new LimitedBinder () };
var data = (List<Exception>)formatter.Deserialize (fs);
Determining which types are safe is quite difficult, and this approach is not recommended unless necessary. There are many types that might allow non Remote Code Execution exploits if they are deserialized from untrusted data. Denial of service is especially common. As an example, the System.Collections.HashTable class is not safe to deserialize from an untrusted stream – the stream can specify the size of the internal “bucket” array and cause an out of memory condition.
4) Serialize simple Data Transfer Objects (DTO) only. Do not serialize/deserialize type information. For example, use only TypeNameHandling.None
(the default) in Json.net:
class DataForStorage
{
public string Id;
public int Count;
}
var data = JsonConvert.SerializeObject<DataForStorage>(json, new
JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.None
});
will produce the following JSON without type information that is perfectly fine to deserialize back:
{
"Id": null,
"Count": 0
}
References
CWE-502: Deserialization of Untrusted Data
BlackHat USA 2017: Friday the 13th: JSON Attacks
BlueHat v17: Dangerous Contents - Securing .Net Deserialization
BlackHat USA 2012: Are you my type?
OWASP: Deserialization of untrusted data
Deserialization payload generator for a variety of .NET formatters
.NET Deserialization Passive Scanner
規則
注射
參考
SCS0001 - 指令注入
傳遞給命令執行的動態值應該被驗證。
風險
如果惡意使用者控制了檔案名稱或參數,他可能能夠執行不需要的命令或新增不需要的參數。如果輸入參數根據字元白名單進行驗證,則這種行為不可能發生。
易受攻擊的程式碼
var p = new Process();
p.StartInfo.FileName = "exportLegacy.exe";
p.StartInfo.Arguments = " -user " + input + " -role user";
p.Start();
解決方案
Regex rgx = new Regex(@"^[a-zA-Z0-9]+$");
if(rgx.IsMatch(input))
{
var p = new Process();
p.StartInfo.FileName = "exportLegacy.exe";
p.StartInfo.Arguments = " -user " + input + " -role user";
p.Start();
}
參考
CWE-78:作業系統指令中特殊元素的不當中和(「作業系統指令注入」)
OWASP:指令注入
OWASP:Top 10 2013-A1-注入
SCS0002-SQL注入
當軟體開發人員建立包含使用者提供的輸入的動態資料庫查詢時,就會引入 SQL 注入缺陷。
風險
惡意使用者可能會直接獲得資料庫的讀取和/或寫入權限。如果資料庫配置不當,攻擊者甚至可能在運行資料庫的機器上獲得遠端程式碼執行(RCE)。
易受攻擊的程式碼
var cmd = "SELECT * FROM Users WHERE username = '" + input + "' and role='user'";
ctx.Database.ExecuteSqlCommand(
cmd);
解決方案
使用參數化查詢來減輕 SQL 注入。
var cmd = "SELECT * FROM Users WHERE username = @username and role='user'";
ctx.Database.ExecuteSqlCommand(
cmd,
new SqlParameter("@username", input));
參考
CWE-89:SQL 指令中使用的特殊元素的不正確中和(「SQL 注入」)
WASC-19:SQL 注入
OWASP:SQL 注入預防備忘錄
OWASP:查詢參數化備忘單
CAPEC-66:SQL 注入
Bobby Tables:防止 SQL 注入的指南
SCS0003-XPath注入
傳遞給 XPath 查詢的動態值應該被驗證。
風險
如果使用者輸入沒有被正確過濾,惡意使用者可能會擴充 XPath 查詢。
易受攻擊的程式碼
var doc = new XmlDocument {XmlResolver = null};
doc.Load("/config.xml");
var results = doc.SelectNodes("/Config/Devices/Device[id='" + input + "']");
解決方案
Regex rgx = new Regex(@"^[a-zA-Z0-9]+$");
if(rgx.IsMatch(input)) //Additional validation
{
XmlDocument doc = new XmlDocument {XmlResolver = null};
doc.Load("/config.xml");
var results = doc.SelectNodes("/Config/Devices/Device[id='" + input + "']");
}
參考
CWE-643:XPath 表達式中的資料中和不當(「XPath 注入」)
WASC-39:XPath 注入
OWASP:XPATH 注入
Black Hat Europe 2012:駭客攻擊 XPath 2.0
SCS0007-XML 外部實體注入(XXE)
XML 解析器配置不正確。此操作可能容易受到 XML 外部實體 (XXE) 處理的攻擊。
風險
易受攻擊的程式碼
.NET 4.5.2 之前版本
// DTD expansion is enabled by default
XmlReaderSettings settings = new XmlReaderSettings();
XmlReader reader = XmlReader.Create(inputXml, settings);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(pathToXmlFile);
Console.WriteLine(xmlDoc.InnerText);
解決方案
.NET 4.5.2 之前版本
var settings = new XmlReaderSettings();
// Prior to .NET 4.0
settings.ProhibitDtd = true; // default is false!
// .NET 4.0 - .NET 4.5.2
settings.DtdProcessing = DtdProcessing.Prohibit; // default is DtdProcessing.Parse!
XmlReader reader = XmlReader.Create(inputXml, settings);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.XmlResolver = null; // Setting this to NULL disables DTDs - Its NOT null by default.
xmlDoc.Load(pathToXmlFile);
Console.WriteLine(xmlDoc.InnerText);
.NET 4.5.2 及更高版本
在 .NET Framework 4.5.2 及更高版本中,XmlTextReader 的內部 XmlResolver 預設為 null,使得 XmlTextReader 預設忽略 DTD。如果您使用預設或不安全的設定來建立自己的非空 XmlResolver,則 XmlTextReader 可能會變得不安全。
參考
CWE-611:XML 外部實體參考(‘XXE’)的不當限制
OWASP.org:XML 外部實體(XXE)預防備忘單(.NET)
CERT:IDS10-J。防止 XML 外部實體攻擊
OWASP.org:XML 外部實體 (XXE) 處理
WS-Attacks.org:XML 實體擴展
WS-Attacks.org:XML 外部實體 DOS
WS-Attacks.org:XML 實體引用攻擊
識別 XML 外部實體漏洞 (XXE)
SCS0018 - 路徑遍歷
路徑遍歷攻擊(也稱為目錄遍歷)旨在存取儲存在預期目錄之外的檔案和目錄。透過操縱引用帶有「點-點-斜線 (../)」序列及其變體的檔案的變數或使用絕對檔案路徑,可以存取儲存在檔案系統上的任意檔案和目錄,包括應用程式原始程式碼或設定和關鍵系統檔案。
風險
利用惡意相對路徑,攻擊者可以取得秘密檔案。
易受攻擊的程式碼
[RedirectingAction]
public ActionResult Download(string fileName)
{
byte[] fileBytes = System.IO.File.ReadAllBytes(Server.MapPath("~/ClientDocument/") + fileName);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
以下請求下載攻擊者選擇的檔案: http://www.address.com/Home/Download?fileName=../../somefile.txt
解決方案
不要嘗試刪除無效字元。如果偵測到任何意外字符,則失敗。
private static readonly char[] InvalidFilenameChars = Path.GetInvalidFileNameChars();
[RedirectingAction]
public ActionResult Download(string fileName)
{
if (fileName.IndexOfAny(InvalidFilenameChars) >= 0)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
byte[] fileBytes = System.IO.File.ReadAllBytes(Server.MapPath("~/ClientDocument/") + fileName);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
如果使用者未提供輸入或已進行驗證,則可以抑制警告。
參考
CWE-22:將路徑名稱不正確地限製到受限目錄(「路徑遍歷」)
OWASP:路徑遍歷
作業系統命令注入、路徑遍歷和本機檔案包含漏洞 - 註釋
SCS0029 - 跨站腳本 (XSS)
發現了潛在的 XSS。端點傳回來自客戶端輸入的尚未編碼的變數。為了防止儲存型 XSS 攻擊,請確保來自使用者或資料儲存的任何動態內容不能用於在頁面上註入 JavaScript。大多數現代框架將預設自動轉義動態內容(例如 Razor)或使用特殊語法(<%: content %>
,<%= HttpUtility.HtmlEncode(content) %>
)。
風險
XSS 可用於在客戶端的瀏覽器中執行不需要的 JavaScript。 XSS 可用於竊取包含使用者會話 ID 的 cookie。很少有充分的理由在客戶端 JavaScript 中讀取或操作 cookie,因此請考慮將 cookie 標記為HTTP-only,這意味著 cookie 將由瀏覽器接收、儲存和發送,但不能由 JavaScript 修改或讀取。
易受攻擊的程式碼
public class TestController : Controller
{
[HttpGet(""{myParam}"")]
public string Get(string myParam)
{
return "value " + myParam;
}
}
解決方案
public class TestController : Controller
{
[HttpGet(""{myParam}"")]
public string Get(string myParam)
{
return "value " + HttpUtility.HtmlEncode(myParam);
}
}
參考
CWE-79:網頁產生過程中輸入的不當中和(「跨站點腳本」)
WASC-8:跨站點腳本
OWASP:XSS 預防備忘單
OWASP:Top 10 2013-A3:跨站點腳本(XSS)
SCS0026 - LDAP 可分辨名稱注入
應該驗證傳遞給 LDAP 查詢的動態值。
風險
如果使用者輸入沒有被正確過濾,惡意使用者可能會擴展 LDAP 查詢。
易受攻擊的程式碼
var dir = new DirectoryEntry();
dir.Path = $"GC://DC={input},DC=com";
解決方案
使用AntiXSS 庫中的適當編碼器(LdapFilterEncode
或LdapDistinguishedNameEncode
):
var dir = new DirectoryEntry();
dir.Path = $"GC://DC={Encoder.LdapDistinguishedNameEncode(input)},DC=com";
參考
CWE-90:LDAP 查詢中使用的特殊元素的中和不當(「LDAP 注入」)
WASC-29:LDAP 注入
OWASP:LDAP 注入
OWASP:LDAP 注入預防備忘單
MSDN 部落格 - 安全工具:LDAP 注入和緩解
SCS0031 - LDAP 過濾器注入
應該驗證傳遞給 LDAP 查詢的動態值。
風險
如果使用者輸入沒有被正確過濾,惡意使用者可能會擴展 LDAP 查詢。
易受攻擊的程式碼
var searcher = new DirectorySearcher();
searcher.Filter = "(cn=" + input + ")";
解決方案
使用AntiXSS 庫中的適當編碼器(LdapFilterEncode
或LdapDistinguishedNameEncode
):
var searcher = new DirectorySearcher();
searcher.Filter = "(cn=" + Encoder.LdapFilterEncode(input) + ")";
參考
CWE-90:LDAP 查詢中使用的特殊元素的中和不當(「LDAP 注入」)
WASC-29:LDAP 注入
OWASP:LDAP 注入
OWASP:LDAP 注入預防備忘單
MSDN 部落格 - 安全工具:LDAP 注入和緩解
密碼學
SCS0004 - 憑證驗證已停用
證書驗證已被停用。通訊可能會被截取。
風險
停用憑證驗證通常用於輕鬆連接到未經根憑證授權單位簽署的主機。因此,由於客戶端會信任任何證書,因此很容易受到中間人攻擊。
易受攻擊的程式碼
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
解決方案
- 確保僅在測試環境中停用驗證,或
- 使用證書固定進行開發或
- 使用正確簽署的憑證進行開發
#if DEBUG
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
#endif
參考
SCS0005 - 弱隨機數產生器
產生的隨機數是可以預測的。
風險
在某些安全關鍵環境中使用可預測的隨機值可能會導致漏洞。
易受攻擊的程式碼
var rnd = new Random();
byte[] buffer = new byte[16];
rnd.GetBytes(buffer);
return BitConverter.ToString(buffer);
解決方案
using System.Security.Cryptography;
var rnd = RandomNumberGenerator.Create();
參考
CWE-338:使用加密弱偽隨機數產生器(PRNG)
OWASP:不安全的隨機性
SCS0006 - 弱雜湊函數
MD5 或 SHA1 具有已知的碰撞弱點,不再被視為強雜湊演算法。
易受攻擊的程式碼
var hashProvider = new SHA1CryptoServiceProvider();
var hash = hashProvider.ComputeHash(str);
解決方案
使用 SHA256 或 SHA512。請注意,雜湊演算法設計為快速的,不應直接用於雜湊密碼。使用自適應演算法來實現此目的。
var hashProvider = SHA256Managed.Create();
var hash = hashProvider.ComputeHash(str);
參考
CWE-327:使用損壞或有風險的加密演算法
MSDN:SHA256 類文件
加鹽密碼哈希 - 正確做法
SCS0010 - 弱密碼演算法
對於現代應用程式來說,DES 和 3DES 並不被視為強密碼。目前,NIST 建議使用 AES 分組密碼。
風險
被破解或棄用的密碼通常具有已知的弱點。攻擊者可能能夠強行取得用於加密的金鑰。加密資訊的機密性和完整性受到威脅。
易受攻擊的程式碼
DES DESalg = DES.Create();
// Create a string to encrypt.
byte[] encrypted;
ICryptoTransform encryptor = DESalg.CreateEncryptor(key, zeroIV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt,
encryptor,
CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(Data);
}
encrypted = msEncrypt.ToArray();
return encrypted;
}
}
解決方案
使用 AES 進行對稱加密。
// Create a string to encrypt.
byte[] encrypted;
var encryptor = new AesManaged();
encryptor.Key = key;
encryptor.GenerateIV();
var iv = encryptor.IV;
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt,
encryptor.CreateEncryptor(),
CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(Data);
}
encrypted = msEncrypt.ToArray();
return encrypted;
}
}
請注意,AES 本身並不能防止加密資料被竄改。有關認證加密的範例,請參閱弱密碼模式下的解決方案
參考
CWE-327:使用損壞或有風險的加密演算法
NIST 撤回過時的資料加密標準
StackOverflow:經過身份驗證的加密範例
SCS0013 - 可能使用弱 CipherMode 模式
產生的密文很容易被對手更改。
風險
該密碼無法偵測資料是否被竄改。如果攻擊者可以控制密文,那麼就可以在不被發現的情況下進行更改。建議使用 CBC 模式的 AES 和 HMAC 來保證完整性和機密性。
易受攻擊的程式碼
using (var aes = new AesManaged {
KeySize = KeyBitSize,
BlockSize = BlockBitSize,
Mode = CipherMode.OFB,
Padding = PaddingMode.PKCS7
})
{
using (var encrypter = aes.CreateEncryptor(cryptKey, new byte[16]))
using (var cipherStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cryptoStream))
{
//Encrypt Data
binaryWriter.Write(secretMessage);
}
cipherText = cipherStream.ToArray();
}
}
//Missing HMAC suffix to assure integrity
解決方案
使用充氣城堡:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
public static readonly int BlockBitSize = 128;
public static readonly int KeyBitSize = 256;
public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] key)
{
//User Error Checks
if (key == null || key.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "key");
if (secretMessage == null || secretMessage.Length == 0)
throw new ArgumentException("Secret Message Required!", "secretMessage");
//Using random nonce large enough not to repeat
var nonce = new byte[NonceBitSize / 8];
Random.NextBytes(nonce, 0, nonce.Length);
var cipher = new GcmBlockCipher(new AesFastEngine());
var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, new byte[0]);
cipher.Init(true, parameters);
//Generate Cipher Text With Auth Tag
var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)];
var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0);
cipher.DoFinal(cipherText, len);
//Assemble Message
using (var combinedStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(combinedStream))
{
//Prepend Nonce
binaryWriter.Write(nonce);
//Write Cipher Text
binaryWriter.Write(cipherText);
}
return combinedStream.ToArray();
}
}
加密和 HMAC 的自訂實作:
using System.IO;
using System.Security.Cryptography;
public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null)
{
//User Error Checks
if (cryptKey == null || cryptKey.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "cryptKey");
if (authKey == null || authKey.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "authKey");
if (secretMessage == null || secretMessage.Length < 1)
throw new ArgumentException("Secret Message Required!", "secretMessage");
byte[] cipherText;
byte[] iv;
using (var aes = new AesManaged {
KeySize = KeyBitSize,
BlockSize = BlockBitSize,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
//Use random IV
aes.GenerateIV();
iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(cryptKey, iv))
using (var cipherStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cryptoStream))
{
//Encrypt Data
binaryWriter.Write(secretMessage);
}
cipherText = cipherStream.ToArray();
}
}
//Assemble encrypted message and add authentication
using (var hmac = new HMACSHA256(authKey))
using (var encryptedStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(encryptedStream))
{
//Prepend IV
binaryWriter.Write(iv);
//Write Ciphertext
binaryWriter.Write(cipherText);
binaryWriter.Flush();
//Authenticate all data
var tag = hmac.ComputeHash(encryptedStream.ToArray());
//Postpend tag
binaryWriter.Write(tag);
}
return encryptedStream.ToArray();
}
}
參考
CWE-327:使用破損或有風險的加密演算法
填充 Oracles 供大眾使用(作者:Matias Soler)
維基百科:經過身份驗證的加密
NIST:經過身份驗證的加密模式
CAPEC:填充 Oracle 加密攻擊
維基百科:ECB 模式
餅乾
SCS0008 - 沒有 SSL 標誌的 Cookie
建議為新的 cookie 指定安全標誌。
風險
安全標誌是向瀏覽器發出的指令,以確保 cookie 不會透過未加密的通道傳送
易受攻擊的程式碼
該requireSSL
值明確設定為false
或保留預設值。
<httpCookies requireSSL="false" [..] />
// default is left
var cookie = new HttpCookie("test");
// or explicitly set to false
var cookie = new HttpCookie("test");
cookie.Secure = false;
解決方案
<httpCookies requireSSL="true" [..] />
var cookie = new HttpCookie("test");
cookie.Secure = true; //Add this flag
cookie.HttpOnly = true;
參考
CWE-614:HTTPS 會話中的敏感 Cookie 不具備「安全性」屬性
OWASP:安全標誌
Rapid7:SSL Cookie 中缺少安全標誌
SCS0009 - 沒有 HttpOnly 標誌的 Cookie
建議為新的 cookie 指定 HttpOnly 標誌。
風險
未設定標誌的 Cookie 可供在同一網域上執行的 JavaScript 使用。當使用者成為「跨站點腳本」的目標時,攻擊者可以透過獲取會話 ID 獲得巨大利益。
易受攻擊的程式碼
該httpOnlyCookies
值明確設定為false
或保留預設值。
<httpCookies httpOnlyCookies="false" [..] />
// default is left
var cookie = new HttpCookie("test");
// or explicitly set to false
var cookie = new HttpCookie("test");
cookie.HttpOnly = false;
解決方案
<httpCookies httpOnlyCookies="true" [..] />
var cookie = new HttpCookie("test");
cookie.Secure = true;
cookie.HttpOnly = true; //Add this flag
參考
CWE-1004:沒有「HttpOnly」標誌的敏感 Cookie
Coding Horror 部落格:保護您的 Cookie:HttpOnly
OWASP:HttpOnly
Rapid7:Cookie 中缺少 HttpOnly 標誌
視圖狀態
SCS0023 - 視圖狀態未加密
設定檔中viewStateEncryptionMode
未設定為。Always
風險
Web 窗體控制項使用隱藏的 base64 編碼欄位來儲存狀態資訊。如果敏感資訊儲存在那裡,則可能會洩漏給客戶端。
易受攻擊的程式碼
預設值為Auto
:
<system.web>
...
<pages [..] viewStateEncryptionMode="Auto" [..]/>
...
</system.web>
或者
<system.web>
...
<pages [..] viewStateEncryptionMode="Never" [..]/>
...
</system.web>
解決方案
明確設定為Always
並使用 .NET機器密鑰加密:
<system.web>
...
<pages [..] viewStateEncryptionMode="Always" [..]/>
...
</system.web>
參考
CWE-554:ASP.NET 設定錯誤:未使用輸入驗證框架
MSDN:pages 元素(ASP.NET 設定架構)
MSDN:ViewStateEncryptionMode 屬性
MSDN:machineKey 元素(ASP.NET 設定架構)
SCS0024 - 檢視狀態 MAC 已停用
在設定檔中被enableViewStateMac
停用。 (從 .NET 4.5.1 開始無法停用此功能)
風險
視圖狀態可能會被攻擊者改變。
易受攻擊的程式碼
<system.web>
...
<pages [..] enableViewStateMac="false" [..]/>
...
</system.web>
解決方案
預設值為 secure- true
。或明確設定:
<system.web>
...
<pages [..] enableViewStateMac="true" [..]/>
...
</system.web>
參考
CWE-554:ASP.NET 設定錯誤:未使用輸入驗證框架
MSDN:pages 元素(ASP.NET 設定架構)
請求驗證
SCS0017 - 請求驗證已停用(屬性)
請求驗證已停用。請求驗證允許過濾提交給應用程式的一些XSS模式。
風險
易受攻擊的程式碼
public class TestController
{
[HttpPost]
[ValidateInput(false)]
public ActionResult ControllerMethod(string input) {
return f(input);
}
}
解決方案
雖然它執行黑名單(從定義上來說比白名單更差)並且您不應該僅僅依靠它來進行 XSS 保護,但它為您的應用程式提供了第一道防線。不要停用驗證:
public class TestController
{
[HttpPost]
public ActionResult ControllerMethod(string input) {
return f(input);
}
}
在顯示或使用使用者提供的資料(即使是從資料庫載入的)之前,請務必使用適當的編碼器(Html、Url 等)。
參考
CWE-554:ASP.NET 設定錯誤:未使用輸入驗證框架
MSDN:ASP.NET 中的請求驗證
OWASP:ASP.NET 請求驗證
請參閱XSS參考。
SCS0021 - 請求驗證已停用(設定檔)
validateRequest
在設定檔中停用了提供針對XSS 的額外保護。
風險
易受攻擊的程式碼
<system.web>
...
<pages [..] validateRequest="false" [..]/>
...
</system.web>
解決方案
雖然它執行黑名單(從定義上來說比白名單更差)並且您不應該僅僅依靠它來進行 XSS 保護,但它為您的應用程式提供了第一道防線。不禁用驗證:預設值為true
。或明確設定:
<system.web>
...
<pages [..] validateRequest="true" [..]/>
...
</system.web>
參考
CWE-554:ASP.NET 設定錯誤:未使用輸入驗證框架
MSDN:pages 元素(ASP.NET 設定架構)
MSDN:ASP.NET 中的請求驗證
OWASP:ASP.NET 請求驗證
請參閱XSS參考。
SCS0030 - 僅對頁面啟用請求驗證(設定檔)
提供對XSS 的requestValidationMode
額外保護僅針對頁面啟用,而不是針對設定檔中的所有 HTTP 請求啟用。
風險
易受攻擊的程式碼
<system.web>
...
<httpRuntime [..] requestValidationMode="2.0" [..]/>
...
</system.web>
解決方案
<system.web>
...
<httpRuntime [..] requestValidationMode="4.5" [..]/>
...
</system.web>
參考
CWE-554:ASP.NET 設定錯誤:未使用輸入驗證框架
MSDN:pages 元素(ASP.NET 設定架構)
MSDN:ASP.NET 中的請求驗證
OWASP:ASP.NET 請求驗證
MSDN:RequestValidationMode 屬性
請參閱XSS參考。
密碼管理
SCS0015 - 硬編碼密碼
此 API 的密碼配置似乎是硬編碼的。
風險
如果使用硬編碼密碼,那麼惡意使用者幾乎肯定會透過相關帳戶獲得存取權限。
易受攻擊的程式碼
config.setPassword("NotSoSecr3tP@ssword");
解決方案
建議將密碼等配置外部化,避免機密資訊外洩。與生產配置相比,原始碼或其二進位形式更容易被攻擊者存取。為了安全管理,密碼和金鑰應以加密形式儲存在單獨的設定檔中。解密憑證應以不可匯出的形式安裝在伺服器電腦上。
設定檔:
<configuration>
<appSettings>
<add key="api_password" value="b3e521073ca276dc2b7caf6247b6ddc72d5e2d2d" />
</appSettings>
</configuration>
代碼:
string apiPassword = ConfigurationManager.AppSettings["api_password"];
config.setPassword(apiPassword);
參考
SCS0034 - 未設定密碼要求長度
RequiredLength 屬性必須設定為最小值 8。
風險
弱密碼可能會被猜測或暴力破解。
易受攻擊的程式碼
ASP.NET Identity 預設值為 6。
PasswordValidator pwdv = new PasswordValidator();
解決方案
查看密碼複雜性的解決方案
參考
CWE-521:弱密碼需求
MSDN:ASP.NET Identity PasswordValidator 類
SCS0032 - 密碼要求長度太小
建議密碼的最小長度至少設定為8位。
風險
弱密碼可能會被猜測或暴力破解。
易受攻擊的程式碼
PasswordValidator pwdv = new PasswordValidator
{
RequiredLength = 6,
};
解決方案
查看密碼複雜性的解決方案
參考
CWE-521:弱密碼需求
MSDN:ASP.NET Identity PasswordValidator 類
SCS0033 - 密碼複雜性
為了提高安全性,PasswordValidator 至少應該有兩個要求(RequiredLength、RequireDigit、RequireLowercase、RequireUppercase 和/或 RequireNonLetterOrDigit)。
風險
弱密碼可能會被猜測或暴力破解。
易受攻擊的程式碼
PasswordValidator pwdv = new PasswordValidator
{
RequiredLength = 6,
};
解決方案
PasswordValidator pwdv = new PasswordValidator
{
RequiredLength = 8,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
參考
CWE-521:弱密碼需求
MSDN:ASP.NET Identity PasswordValidator 類
其他
SCS0011 - 使用了不安全的 XSLT 設置
僅當您需要腳本支援並且在完全受信任的環境中工作時才應啟用 XSLT 腳本。
風險
如果 XML 來源不受信任,則此問題可能會導致遠端程式碼執行 (RCE)。
易受攻擊的程式碼
XslCompiledTransform transform = new XslCompiledTransform();
XsltSettings settings = new XsltSettings() {EnableScript = true};
transform.Load(xslPath, settings, null);
// Execute the transformation.
transform.Transform(reader, writer);
參考
CWE-611:XML 外部實體引用
XSLT 伺服器端注入攻擊
XML 攻擊 C# 遠端程式碼執行
XsltSettings.EnableScript 屬性的不當限制
SCS0012 - 控制器方法可能容易受到授權繞過的影響
註 [Authorize] 和 [AllowAnonymous] 均不存在。
風險
未經授權的使用者可能會存取該端點。如果它包含敏感資訊,例如日誌文件,則可能會導致權限提升。如果應用程式正在使用其他授權檢查,則可能會忽略/抑制該警告。可以自訂規則並註冊附加屬性。
易受攻擊的程式碼
public class AccountController : Controller
{
public ActionResult Login()
{
}
[Authorize]
public ActionResult Logout()
{
}
}
解決方案
[Authorize]
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Login()
{
}
public ActionResult Logout()
{
}
}
參考
CWE-284:不當存取控制
存取控制漏洞和權限提升
ASP.NET Core 中的簡單授權
SCS0016 - 跨站請求偽造 (CSRF)
防偽標記遺失。
風險
攻擊者可以向受害者發送連結。透過訪問惡意鏈接,網頁將觸發對網站的 POST 請求(因為這是一種盲目攻擊 - 攻擊者看不到觸發請求的回應,也不會使用 GET 請求,而且根據定義,GET 請求不應該改變伺服器上的狀態)。受害者無法確認後台執行的操作,但如果他透過網站驗證,他的 cookie 就會自動提交。除了訪問網站之外,此攻擊不需要任何特殊互動。
易受攻擊的程式碼
public class TestController
{
[HttpPost]
public ActionResult ControllerMethod(string input)
{
//Do an action in the context of the logged in user
}
}
解決方案
您認為:
@Html.AntiForgeryToken()
在您的控制器中:
public class TestController
{
[HttpPost]
[ValidateAntiForgeryToken] //Annotation added
public ActionResult ControllerMethod(string input)
{
//Do an action in the context of the logged in user
}
}
參考
CWE-352:跨站請求偽造 (CSRF)
OWASP:跨站請求偽造
OWASP:CSRF 預防備忘單
SCS0019 - 輸出快取衝突
快取與授權衝突。
風險
使用註解 [OutputCache] 將停用第一個請求之後的註解 [Authorize]。
易受攻擊的程式碼
[Authorize]
public class AdminController : Controller
{
[OutputCache]
public ActionResult Index()
{
return View();
}
}
解決方案
刪除快取:
[Authorize]
public class AdminController : Controller
{
public ActionResult Index()
{
return View();
}
}
參考
SCS0022 - 事件驗證已停用
在設定檔中被enableEventValidation
停用。
風險
此功能降低了未經授權或惡意的回發請求和回呼的風險。強烈建議您不要停用事件驗證。當 EnableEventValidation 屬性設定為 true 時,ASP.NET 會驗證控制項事件是否源自於該控制項呈現的使用者介面。
易受攻擊的程式碼
<system.web>
...
<pages [..] enableEventValidation="false" [..]/>
...
</system.web>
解決方案
預設值為 secure- true
。或明確設定:
<system.web>
...
<pages [..] enableEventValidation="true" [..]/>
...
</system.web>
參考
CWE-554:ASP.NET 設定錯誤:未使用輸入驗證框架
MSDN:pages 元素(ASP.NET 設定架構)
MSDN:Page.EnableEventValidation 屬性
SCS0027-開放重定向
傳遞給的動態值Redirect
應該被驗證。
風險
您的網站可能被用於網路釣魚攻擊。攻擊者可能會製作一個看似可信的連結指向您的網站,將受害者重新導向到類似的惡意網站:https://www.yourdomain.com/loginpostback?redir=https://www.urdomain.com/login
易受攻擊的程式碼
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
解決方案
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl)) // Make sure the url is relative, not absolute path
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
參考
CWE-601:URL 重定向至不受信任的網站(「開放重定向」)
Microsoft:防止開放重定向攻擊 (C#)
OWASP:未經驗證的重定向和轉送備忘單
Hacksplaining:防止惡意重定向
SCS0028 - 不安全的反序列化
傳遞了不受信任的資料以進行反序列化。
風險
任意程式碼執行、完全應用程式破壞或拒絕服務。攻擊者可能會傳遞特製的特定類別的序列化 .NET 對象,該物件將在物件建構期間執行惡意程式碼。
易受攻擊的程式碼
private void ConvertData(string json)
{
var mySerializer = new JavaScriptSerializer(new SimpleTypeResolver());
Object mything = mySerializer.Deserialize(json, typeof(SomeDataClass)/* the type doesn't matter */);
}
解決方案
沒有簡單的解決方法。不要反序列化不受信任的資料:使用者輸入、cookie 或跨越信任邊界的資料。
如果不可避免:
1)如果在伺服器端進行序列化,則跨越信任邊界,但未被修改並傳回(例如 cookie) - 使用簽章加密(例如 HMAC)來確保它沒有被竄改。
2)不要從不受信任的來源取得反序列化的類型:序列化流本身或其他不受信任的參數。BinaryFormatter
例如從序列化流本身讀取類型信息,不能與不受信任的流一起使用:
// DO NOT DO THIS!
var thing = (MyType)new BinaryFormatter().Deserialize(untrustedStream);
例如,沒有 JavaScriptTypeResolver 的 JavaScriptSerializer 是安全的,因為它根本不會解析類型:
private void ConvertData(string json)
{
var mySerializer = new JavaScriptSerializer(/* no resolver here */);
Object mything = mySerializer.Deserialize(json, typeof(SomeDataClass));
}
將預期類型(可能是硬編碼)傳遞給反序列化庫。一些函式庫(如 Json.Net、DataContractJsonSerializer 和 FSPickler)在反序列化之前驗證預期的物件圖。但是,如果預期類型包含System.Object
層次結構中嵌套的欄位或屬性,則檢查不是萬無一失的。
// Json.net will inspect if the serialized data is the Expected type
var data = JsonConvert.DeserializeObject<Expected>(json, new
JsonSerializerSettings
{
// Type information is not used, only simple types like int, string, double, etc. will be resolved
TypeNameHandling = TypeNameHandling.None
});
// DO NOT DO THIS! The cast to MyType happens too late, when malicious code was already executed
var thing = (MyType)new BinaryFormatter().Deserialize(untrustedStream);
3)如果庫支援實現回調,則驗證物件及其屬性是否屬於預期類型(不要黑名單,使用白名單!):
class LimitedBinder : SerializationBinder
{
List<Type> allowedTypes = new List<Type>()
{
typeof(Exception),
typeof(List<Exception>),
};
public override Type BindToType(string assemblyName, string typeName)
{
var type = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName), true);
foreach(Type allowedType in allowedTypes)
{
if(type == allowedType)
return allowedType;
}
// Don’t return null for unexpected types –
// this makes some serializers fall back to a default binder, allowing exploits.
throw new Exception("Unexpected serialized type");
}
}
var formatter = new BinaryFormatter() { Binder = new LimitedBinder () };
var data = (List<Exception>)formatter.Deserialize (fs);
確定哪些類型是安全的非常困難,除非必要,否則不建議採用這種方法。如果從不信任的資料反序列化,則有許多類型可能允許非遠端程式碼執行漏洞。拒絕服務尤其常見。例如,System.Collections.HashTable 類別從不受信任的流中反序列化是不安全的——流可以指定內部「儲存桶」數組的大小並導致記憶體不足的情況。
4)僅序列化簡單資料傳輸物件(DTO) 。不要序列化/反序列化類型資訊。例如,TypeNameHandling.None
在 Json.net 中僅使用(預設):
class DataForStorage
{
public string Id;
public int Count;
}
var data = JsonConvert.SerializeObject<DataForStorage>(json, new
JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.None
});
將產生以下不帶類型資訊的 JSON,但可以完全反序列化回來:
{
"Id": null,
"Count": 0
}
參考
CWE-502:不受信任資料的反序列化
BlackHat USA 2017:13號星期五:JSON 攻擊
BlueHat v17:危險內容 - 保護 .Net 反序列化
BlackHat USA 2012:你是我喜歡的類型嗎?
OWASP:不受信任資料的反序列化
,適用於各種 .NET 格式化程式的反序列化有效負載產生器,
.NET 反序列化被動掃描器
資料來源:https://security-code-scan.github.io/
沒有留言:
張貼留言