Внедрение SMTP-заголовка в ASP.NET? - PullRequest
8 голосов
/ 12 марта 2009

Мой веб-сайт ASP.NET имеет глобальный обработчик ошибок, который отправляет мне (и другому разработчику) электронное письмо при возникновении любых ошибок в веб-приложении. Недавно мы получили сообщение об ошибке, которое содержало CC на адрес электронной почты, о котором мы никогда не слышали. Страшно то, что список разработчиков, которым отправлено сообщение об ошибке, жестко запрограммирован в скомпилированном коде ASP.NET. Мы не видим, как можно было добавить CC.

Мы также очень подозрительно относимся к нечестной игре, потому что запрос, вызвавший ошибку, был попыткой использовать одну из наших форм для рассылки спама. IP-адрес, который отправил запрос, также указан в http://www.projecthoneypot.org/.

Наше лучшее предположение сейчас состоит в том, что запрос был искажен каким-то образом, что он вставил заголовок CC в электронное письмо. Проблема в том, что мы не можем понять, как это можно сделать. Мы используем System.Net.Mail для отправки электронных писем, и это, кажется, защищает от подобных вещей. Субъект объекта MailMessage принимает только одну строку, чтобы вы не создали многострочную тему со строкой CC. Установка адресов to и cc в MailMessage кажется довольно надежной. И я не вижу, как вы могли бы добавить заголовок CC в теле сообщения. Я не могу найти никакой информации по этому вопросу, и я хотел бы знать, если это реальная проблема.

РЕДАКТИРОВАТЬ: Кто-то запросил код. Это немного долго, но вот оно:

public class Global : System.Web.HttpApplication
{
    protected void Application_Error(Object sender, EventArgs e)
    {
        // Get the last exception.
        Exception objException = Server.GetLastError();

        // Work out the error details based on the exception.
        string ErrorType = "";
        string ErrorDescription = "";
        string ErrorHtml = "";

        if (objException == null)
        {
            // This should never occur.
            ErrorType = "Unknown Error";
            ErrorDescription = "Unknown Error";
        }
        else if (objException.GetType() == typeof(HttpException))
        {
            // This will occur when the ASP.NET engine throws a HttpException.
            HttpException objHttpException = objException as HttpException;
            if (objHttpException.GetHttpCode() == 404)
            {
                string Resource = Globals.GetFullUrl(this.Context);
                Server.ClearError();
                Response.Redirect("/ResourceNotFound.aspx?BadUrl=" + Server.UrlEncode(Resource));
                return;
            }
            else
            {
                ErrorType = objHttpException.GetHttpCode().ToString();
                ErrorDescription = objHttpException.Message;
            }
        }
        else if (objException.GetType() == typeof(HttpUnhandledException) && objException.InnerException != null && objException.InnerException.GetType() == typeof(HttpException))
        {
            // This will occur when the code throws a HttpException (e.g. a fake 404).
            HttpException objHttpException = objException.InnerException as HttpException;
            if (objHttpException.GetHttpCode() == 404)
            {
                string Resource = Globals.GetFullUrl(this.Context);
                Server.ClearError();
                Response.Redirect("/ResourceNotFound.aspx?BadUrl=" + Server.UrlEncode(Resource));
                return;
            }
            else
            {
                ErrorType = objHttpException.GetHttpCode().ToString();
                ErrorDescription = objHttpException.Message;
            }
        }
        else if (objException.GetType() == typeof(HttpUnhandledException))
        {
            // This will occur when a page throws an error.
            HttpUnhandledException objHttpUnhandledException = (HttpUnhandledException) objException;
            ErrorType = objHttpUnhandledException.GetHttpCode().ToString();
            if (objHttpUnhandledException.InnerException != null)
                ErrorDescription = objHttpUnhandledException.InnerException.Message;
            else
                ErrorDescription = objHttpUnhandledException.Message;
            if (objHttpUnhandledException.GetHtmlErrorMessage() != null)
            {
                ErrorHtml = objHttpUnhandledException.GetHtmlErrorMessage();
            }
        }
        else if (objException.GetType() == typeof(HttpRequestValidationException) && !Globals.IsTtiUser(this.Context))
        {
            // Do nothing.  This is mostly just spider junk and we don't want to know about it.
        }
        else
        {
            // This will occur when the ASP.NET engine throws any error other than a HttpException.
            ErrorType = objException.GetType().Name;
            ErrorDescription = objException.Message;
        }

        // Send an email if there's an error to report.
        if (ErrorType != "" || ErrorDescription != "")
        {
            Globals.SendErrorEmail(this.Context, ErrorType, ErrorDescription, ErrorHtml);
        }
    }

    public static void SendErrorEmail (HttpContext context, string errorType, string errorDescription, string errorHtml)
    {
        // Build the email subject.
        string Subject = "EM: " + errorType + ": " + context.Request.ServerVariables["SCRIPT_NAME"];

        // Build the email body.
        string Body;

        StringBuilder sb = new StringBuilder("");
        sb.Append("Server:\r\n");
        sb.Append(Globals.Server.ToString() + "\r\n");
        sb.Append("\r\n");
        sb.Append("URL:\r\n");
        sb.Append(Globals.GetFullUrl(context) + "\r\n");
        sb.Append("\r\n");
        sb.Append("Error Type" + ":\r\n");
        sb.Append(errorType + "\r\n");
        sb.Append("\r\n");
        sb.Append("Error Description" + ":\r\n");
        sb.Append(errorDescription + "\r\n");
        sb.Append("\r\n");
        sb.Append("Referring Page:\r\n");
        sb.Append(context.Request.ServerVariables["HTTP_REFERER"] + "\r\n");
        sb.Append("\r\n");
        sb.Append("Date/Time:\r\n");
        sb.Append(DateTime.Now.ToString() + "\r\n");
        sb.Append("\r\n");
        sb.Append("Remote IP:\r\n");
        sb.Append(context.Request.ServerVariables["REMOTE_ADDR"] + "\r\n");
        sb.Append("\r\n");
        sb.Append("User Agent:\r\n");
        sb.Append(context.Request.ServerVariables["HTTP_USER_AGENT"] + "\r\n");
        sb.Append("\r\n");
        sb.Append("Crawler:\r\n");
        sb.Append(context.Request.Browser.Crawler.ToString() + "\r\n");
        sb.Append("\r\n");
        sb.Append("Admin User:\r\n");
        sb.Append(context.User.Identity.Name + "\r\n");
        sb.Append("\r\n");
        sb.Append("\r\n");
        Body = sb.ToString();

        // If there's HTML to represent the error (usually from HttpUnhandledException),
        // then stuff the body text into the HTML (if possible).
        bool HtmlMessage = false;

        if (errorHtml != "")
        {
            Regex r = new Regex("(?<thebodytext><body.*?>)", RegexOptions.IgnoreCase);
            if (r.IsMatch(errorHtml))
            {
                Body = Body.Replace("\r\n", "<br>");
                Body = r.Replace(errorHtml, "${thebodytext}" + Body, 1);
                HtmlMessage = true;
            }
        }

        // Send an email to the TTI developers.
        MailMessage objMail;
        objMail = new MailMessage();
        objMail.From = new MailAddress("from-address");
        objMail.To.Add(new MailAddress("to-address"));
        objMail.CC.Add(new MailAddress("cc-address"));
        objMail.CC.Add(new MailAddress("another-cc-address"));
        if (HtmlMessage)
            objMail.IsBodyHtml = true;
        else
            objMail.IsBodyHtml = false;
        if (errorType == "404")
            objMail.Priority = MailPriority.Low;
        else
            objMail.Priority = MailPriority.High;
        objMail.Subject = Subject;
        objMail.Body = Body;

        try
        {
            SmtpClient objSmtpClient = new SmtpClient();
            objSmtpClient.Send(objMail);
        }
        finally
        {
            // Do nothing.
        }
    }
}

Ответы [ 3 ]

4 голосов
/ 18 марта 2009

Я мог видеть, что это цель творческой атаки ОЧЕНЬ .... Вы вставляете данные, контролируемые пользователем, в ваше тело сообщения ... В этот момент хитрое использование двоичных данных МОЖЕТ привести к ТЕЛЕ, который отправляет правильные данные во время сеанса SMTP, чтобы отформатировать их ПРОСТО ПРАВО ... Если можно, я бы предложил либо преобразовать тело во весь текст ASCII, либо во время построения строки напишите дезинфицирующее средство для строк, в которое допускаются только RFC-символы. (Фильтрует URL-адреса, REFERRER, Удаленный адрес и UserAgent). Это ваши наиболее вероятные точки атаки.

Вторая мысль может состоять в том, чтобы создать базовое электронное письмо в коде и ПОДКЛЮЧИТЬ тело, которое вы создали в виде текста, файла HTML или PDF.

Имейте в виду, что данные SMTP ENVELOPE NOT совпадают с данными сообщения .... Если кто-то был достаточно хитрым, чтобы отправить правильное тело, которое вызвало отправку CRLFCRLF.CRLFCRLF во время части тела , это прервало бы отправку, и затем, если бы они продолжали отправлять данные, они могли бы отправить всю ПОЧТУ ОТ: RCPT TO:, ДАННЫЕ и т. д. (Конечно, это маловероятный сценарий ...) ...

Мне бы очень хотелось увидеть RAW источник полученного вами электронного письма ... (Как в шестнадцатеричном дампе фактической SMTP-транзакции, а не то, что Outlook хочет, чтобы вы видели, или что-то в этом роде).

Вы также можете попробовать кодировать свое тело, используя QP или B64 перед отправкой сообщения .... Это может решить вашу проблему ...

Это интересный вопрос, и я с нетерпением жду его результатов.

1 голос
/ 18 марта 2009

Ваш код выглядит очень защищенным, поэтому я не думаю, что проблема на вашей стороне.

IMO, либо кто-то перехватил SMTP-сообщение во время его отправки на почтовый сервер и ввел дополнительную строку CC:; или почтовый сервер был взломан.

Если вы не можете найти ответ, я предлагаю связаться с Microsoft напрямую - возможно, вы обнаружили уязвимость в .NET Framework.

0 голосов
/ 19 июля 2009

В качестве обходного пути, почему бы вам не зашифровать сообщение электронной почты с помощью асимметричной надписи (например, надписи с открытым ключом)? Таким образом, только предполагаемый получатель сможет прочитать его.

Таким образом, даже если злоумышленники получат копию вашего сообщения (каким бы то ни было способом), он будет им полезен.

Давайте посмотрим правде в глаза: если у вас есть высококлассный веб-сайт, такой как ФБР или Google, многие очень креативные люди потратят много времени и пойдут на все, чтобы его оценить. Чрезвычайно важно защитить подробные сообщения об ошибках.

...