Friday, October 18, 2013

AntiXSS for Web API

Most people forget to protect their site from XSS attack.
They simply accepting the fact that the smart javascript libary (AngularJS) will protect them by escaping the json characters.

But how about your legacy site, it still allow your XSS script to harm innocent user.

Unfortunately AntiXSS library doesn't protect you with Double open angle brackets which can pass this script in especially if the client use firefox.

//Double open angle brackets
//Using an open angle bracket at the end of the vector instead of a close angle bracket causes different behavior in Netscape //Gecko rendering. Without it, Firefox will work but Netscape won't:
<iframe src=http://ha.ckers.org/scriptlet.html <
Here is the complete snippets

using Microsoft.VisualStudio.TestTools.UnitTesting;
using SEEK.Employment.Profile.API.Validation;
public class AntiXssValidator : IAntiXssValidator
{
public static string[] XSSTagStringsForDoubleOpening = {"iframe",
"script",
"style",
"input"
};
/// <summary>
/// Validate the words which contains potential XSS Attack.
/// This will compare the encoded with sanitize html fragments, if it finds any differences means that it contains unsafe html tags.
/// Here is the least of potential XSS attacks
/// https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
/// </summary>
/// <param name="words"></param>
/// <returns></returns>
public bool Validate(string words)
{
if (string.IsNullOrEmpty(words))
{
return false;
}
//this will protect all this script attack list https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
var sanitizedWords = Sanitizer.GetSafeHtmlFragment(words);
var sanitizedWordsDecoded = HttpUtility.HtmlDecode(sanitizedWords);
//compare the sanitizedWord to check whether there is invalid tag
if (sanitizedWordsDecoded != words)
{
return true;
}
//except this one, we should protect againts Double open angle brackets
var checkWords = words.ToLower().Trim().Replace(" ", ""); //take out all space to protect < iframe
return XSSTagStringsForDoubleOpening.Any(tag => checkWords.Contains("<" + tag));
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SEEK.Employment.Profile.API.Validation;
[TestClass]
public class MaliciousCharactersValidatorTests
{
private IAntiXssValidator validator;
[TestMethod]
public void Validate_WithMaliciousCharacters_ReturnsTrue()
{
string wordsToValidate = "<script>";
validator = new AntiXssValidator();
bool result = validator.Validate(wordsToValidate);
Assert.IsTrue(result);
}
[TestMethod]
public void Validate_WithoutMaliciousCharacters_ReturnsFalse()
{
string wordsToValidate = "abcdefghijklomnopqrstuvwxyz0123456789~!@#$%*()";
validator = new AntiXssValidator();
bool result = validator.Validate(wordsToValidate);
Assert.IsFalse(result);
}
[TestMethod]
public void Validate_WithNullString_ReturnsFalse()
{
string wordsToValidate = null;
validator = new AntiXssValidator();
bool result = validator.Validate(wordsToValidate);
Assert.IsFalse(result);
}
[TestMethod]
public void Validate_WithEmptyString_ReturnsFalse()
{
string wordsToValidate = string.Empty;
validator = new AntiXssValidator();
bool result = validator.Validate(wordsToValidate);
Assert.IsFalse(result);
}
[TestMethod]
public void Validate_ShouldAllowLessAndGreaterThanCharacter()
{
string wordsToValidate = "managing < 5 people";
validator = new AntiXssValidator();
bool result = validator.Validate(wordsToValidate);
Assert.IsFalse(result);
}
[TestMethod]
public void Validate_ShouldBlockDoubleOpeningMaliciousTag()
{
string wordsToValidate = "<iframe script <";
validator = new AntiXssValidator();
bool result = validator.Validate(wordsToValidate);
Assert.IsTrue(result);
}
}