Repository for OWASP Uruguay Meetup: Workshop OWASP AntiSamy y SonarQube 101
Repositorio para el Meetup organizado por OWASP Uruguay: Workshop OWASP AntiSamy y SonarQube 101
En este workshop aprenderemos acerca del proyecto de OWASP AntiSamy y sobre la herramienta de SAST SonarQube
Video aquí
Presentación AntiSamy aquí
Se utilizará un fork del proyecto simplecommerce/SimplCommerce como proyecto de ejemplo a modificar.
Los requisitos del paso a paso para el ambiente de demo pueden variar, aquí se describe uno de ellos:
- Descargar e instalar NodeJS.
- Descargar e instalar Visual Studio 2019 con el SDK y runtime de .NET 5.0. En caso de seleccionar componentes indviduales en la instalación, asegurarse de marcar ".NET Core cross-platform development", esto debería instalar lo necesario para usar .NET 5.0.
- Descargar e instalar SQL Server. La forma más sencilla y descartable es utilizar Docker, siguiendo la guía en contenedores Linux (puede ser también con contenedores Windows):
- Descargar la imagen oficial en su última versión:
docker pull mcr.microsoft.com/mssql/server:2019-latest
- Iniciar un contenedor estableciendo una contraseña para el usuario
SA
:docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=<YourStrong@Passw0rd>" ` -p 1433:1433 --name sql-server -h sql-server ` -d mcr.microsoft.com/mssql/server:2019-latest
- Probar el servicio y conexión con el comando:
Si se muestra la línea
docker exec -it sql-server /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "<YourStrong@Passw0rd>"
1>
, el login fue exitoso. Se puede salir con el comandoexit
.
- Descargar la imagen oficial en su última versión:
- Clonar el repositorio del proyecto:
git clone https://github.com/spassarop/SimplCommerce.git
- Abrir la solución y modificar el string de conexión en
SimplCommerce\src\SimplCommerce.WebHost\appsettings.json
:"ConnectionStrings": { "DefaultConnection": "Server=.;Database=SimplCommerce;MultipleActiveResultSets=true;User Id=sa;Password=<YourStrong@Passw0rd>;" }
- Compilar la solución.
- Establecer
SimplCommerce.WebHost
como Startup Project si no lo está. - Abrir la ventana Package Manager Console. Allí ejecutar el comando
Update-Database
. Esto crea la base de datos a base de migraciones existentes en la solución. - En Visual Studio presionar "Control + F5".
- Entrar a la URL de la aplicación y crear datos para la industria de tipo "Fashion". El back-office del sitio es accesible vía
/Admin
con las el usuario[email protected]
y contraseña1qazZAQ!
.
- Con sesion iniciada como administrador, acceder a un nuevo producto mediante el cabezal de la web en Catalog > Products y luego con el botón "+ Create Product", o accediendo directamente en
/Admin#!/product-create
. - Cargar datos arbitrarios para el producto. Particularmente en cualquiera de los campos que permiten ingresar "texto enriquecido" (HTML a fin de cuentas) presionar el botón
</>
(Code View) para ver el HTML resultante de la escritura. - Allí insertar el siguiente HTML:
<p>Una camiseta <b>con toda la onda</b>.</p><p><script>alert(document.cookie)</script>Unisex.</p>
- Guardar el producto, dirigirse al nuevo producto en la raíz del sitio web y acceder a la publicación.
- Ver la alerta al cargar la página, con las cookies accesibles por JavaScript.
Aquí entra en juego OWASP AntiSamy .NET. La forma más sencilla de incluirlo es instalando su correspondiente paquete NuGet. Esto puede hacerse con los siguientes pasos:
- Click derecho sobre el proyecto
SimplCommerce\src\Modules\SimplCommerce.Module.Catalog
. - Click en "Manage NuGet packages...".
- Buscar "antisamy", seleccionar el paquete "OWASP.AntiSamy" y hacer click en instalar.
Una vez instalado, puede crearse una nueva clase en el mismo proyecto llamada HtmlSanitizer.cs
con la siguiente estructura:
using OWASP.AntiSamy.Html;
namespace SimplCommerce.Module.Catalog
{
internal static class HtmlSanitizer
{
public static string Santize(string badHtml)
{
}
}
}
Dentro de la función Sanitize
el código más simple se construye con los siguientes pasos:
- Retornar si
badHtml
es nulo o vacío:
if (string.IsNullOrEmpty(badHtml))
{
return badHtml;
}
- Crear una instancia de AntiSamy:
var antisamy = new AntiSamy();
- Ejecutar el escaneo con la política por defecto:
CleanResults results = antisamy.Scan(badHtml, Policy.GetInstance());
- Retornar el HTML sanitizado:
return results.GetCleanHtml();
Para mitigar se deben cubrir dos frentes. La salida de la información (cuando se muestra en la vista) y la entrada (cuando es editado el producto). Como el texto malicioso ya fue ingresado, mitigar las salidas implica sanitizar el HTML en la carga del modelo y retorno de la API para el método GET:
ProductController.cs
>ProductOverview(long id)
yProductDetail(long id)
:ShortDescription = _contentLocalizationService.GetLocalizedProperty(product, nameof(product.ShortDescription), HtmlSanitizer.Santize(product.ShortDescription)),
ProductApiController.cs
>Get(long id)
ShortDescription = HtmlSanitizer.Santize(product.ShortDescription),
Con esto, para la propiedad ShortDescription
(también debería hacerse para Description
y Specification
) se evita la ejecución de código JavaScript para el ejemplo de ataque utilizado. De hecho, el resultado pasa a ser:
<p>Una camiseta <b>con toda la onda</b>.</p><p>Unisex.</p>
Para mitigar la inyección en futuras creaciones/modificaciones de producto, aplicar en:
ProductApiController.cs
>Post(ProductForm model)
:ShortDescription = HtmlSanitizer.Santize(model.Product.ShortDescription),
ProductApiController.cs
>Put(long id, ProductForm model)
:product.ShortDescription = HtmlSanitizer.Santize(model.Product.ShortDescription);
Puede irse un paso más allá basarse en los resultados del escaneo para retornar un error al detectar entradas maliciosas. Para esto, se agrega en la clase HtmlSanitizer
una nueva función:
public static List<string> ValidateHtml(string badHtml)
{
if (string.IsNullOrEmpty(badHtml))
{
return new List<string>();
}
var antisamy = new AntiSamy();
CleanResults results = antisamy.Scan(badHtml, Policy.GetInstance());
return results.GetErrorMessages();
}
Es similar a Sanitize
pero retorna una lista con los errores detectados en lugar del HTML sanitizado. Luego puede aplicarse de la siguiente forma para retornar los errores al cliente y facilitar la comprensión del error:
ProductApiController.cs
>Put(long id, ProductForm model)
yPost(ProductForm model)
:var errors = HtmlSanitizer.ValidateHtml(model.Product.ShortDescription); if (errors.Count > 0) { return BadRequest(new { error = new string[] { "Errors in short description: " + string.Join(",\n", errors) } }); }
Presentación SonarQube 101 aquí
Links de interes:
- SonarQube: https://www.sonarqube.org
- SonarQube Docker: https://hub.docker.com/_/sonarqube
Otros links:
- OWASP mantiene un listado de herramientas para SAST en: https://owasp.org/www-community/Source_Code_Analysis_Tools
- SpotBugs: https://spotbugs.github.io
- PMD: https://pmd.github.io
- AppScan (HCL): https://www.hcltech.com/software/appscan-standard
- CxSAST: https://www.checkmarx.com/products/static application-security-testing/
- Fortify (Microfocus): https://www.microfocus.com/en/us/products/static-code-analysis-sast/overview