HTML Decode/Encode.

Если кто-то слышал об такой атаке на веб-сайты, как XSS Injection, то он(она) знает что для предотвращение такой атаки необходимо использовать функции HtmlEncode/HtmlDecode. XSS Injection (Cross-site injection) - один из способов атак веб-приложений, главной идеей которого является вставка(инъекция) чужого javascript-кода на атакуемый веб-сайт. Как минимум, это может привести к краже печенек cookies и получении прав администратора сайта. ...

1.1. ASP.NET

Это происходит, примерно, так:
Допустим, у нас есть форма для отправки комментарием, где пользователь вводит свой еmail и текст коммеентария. Самый простой обработчик кнопки добавить окмментарий будет выглядить так:

protected void OnAddCommentClick(object sender, EventArgs e)
{
 var pageId = this.GetPageId();
 var email = this.txtEmail.Text;
 var comment = this.txtComment.Text;
 CommentsService.AddComment(pageId, email, comment);
}

С первой точки зрения этот код не содержит никаких ошибок, компилируется и корректно сохраняет данные в БД. Но, если в тексте  комментария будет любой HTML или JavaScript-код, то при отображении этого комментария на страницу без предварительной обработки мы увидем, что HTML и JavaScript-код без особых проблем обрабатываются браузером.
Таким образом, мы сделали наш сайт уязвимым и нарушили одно из базовых правил проектирования ПО: “Не доверять данным, которые пришли извне нашей системы”. От себя еще добавлю: вдвойне недоверять данным, которые были введенны пользователем. Чтобы исправить это ошибку, необходимо всего-лишь вызвать метод HttpServerUtility.HtmlEncode(), перед сохранением данных в базу:


 var email = Server.HtmlEncode(this.txtEmail.Text);
 var comment = Server.HtmlEncode(this.txtComment.Text);

После чего, строка вида “<script>alert(‘hello!’);</script>” будет сохранена в базу в таком виде:

 &lt;script&gt;alert(‘hello!’);&lt;/script&gt;

Теперь все хорошо: любой введенный пользователем текст не ламает нашу верстку и не вызывает выполнения стороннего javascript (пример 2).
Но все не так просто, как кажется. Теперь, если такой код вставить в обработчике OnAddPage(), где администратор сайта будет добавлять страницы, то вместо красивого текста пользователи увидят набор непонятных символов. Поэтому, при отображении содержимого страницы мы вызываем метод HttpServerUtility.HtmlDecode(), который переведет сохраненный в базе данных текст в такой вид, чтобы браузер его корректно обрабатывал.

Таким образом, мы избавили наш сайт от XSS Injection дали возможность пользователям воодить разнообразные данные без вреда для сайта.

Примечание. Во всех примерах выше свойство странице EnableRequestValidation было установлено в false для возможноти ввода символов “<“ и “>”.
Примечание для ASP.NET 4.0/ASP.NET MVC 2.0. В ASP.NET 4.0/ASP.NET MVC 2.0 появилась конструкция <%: ... %>, которая делает за нас HTML Decode/Encode.

1.2. JavaScript

Если нам необходимо реализовать данную функциональность на клиенте с помощью javascript, то нам на помощь приходит jQuery:

function htmlEncode(html) {
 return $('<div/>').text(html).html();
}
function htmlDecode(text) {
 return $('<div/>').html(text).text();
}

1.3 Python/Django

В фреймворке Django HTML Decode работает по умолчанию, и для его отключение в шаблоне (template) добавить блок {% autoescape off %} и в нем поместить все элементы, для которых нам не нужен HTML Decode, после чего закрыть блок с помощью {% endautoescape %}.

2. URL Decode/Encode.

В отличии от HTML Encode/Decode незаэкранированный url больших проблем не вызывает, за исключением двух моментов: url становится не таким простым и понятным для пользователя и это не очень хорошо влияет на SEO.

2.1 ASP.NET

Рассмотрим такую ситуацию: у нас есть сайт с функцией поиска по нем. Стандартная форма поиска содержит поле для вводи и кнопу "Поиск". В простейшем случае при нажатии на кнопку “Поиск” у нас есть такой обработчик:


protected void OnSerachClick(object sender, EventArgs e)
{
 var searchText = this.txtSearch.Text;
 car url = String.Format(“{0}?q={1}”, VirtualPathUtility.ToAbsoluteUrl(“~/Search.aspx”), searchText);
 Response.Redirect(url);
}

Вполне рабочий код, за исключением того, что в некоторых случаях в итоге мы получаем не очень красивый url вида:

/Search.aspx?q=some%20keywords

Конечно, такой url правильно обрабатывается браузером, но не всем пользователям будет понятно что он значит. Происходит это из-за того, что браузер автоматически заменяет символ пробела и ряд других символов на  их шестнадцатиричное значение вида %XX, где XX - код символа в шестнадцатиричной системе.
Сделано это было давно, кода браузеры были маленкими еще не умели отображать нелатинские символы в строке ввода адреса и для правильного отображения разнообразных символов.
И тут нам на помощь приходят методы HttpServerUtility.UrlEncode() и HttpServerUtility.UrlDecode() которые предназначены для переобразования параметров строки запроса в понятный для пользователей вид. Отдельно отчему, что эти методы предназначены для обработки исключительно параметров url, а для переобразования адреса к необходимому ресурсу существует метод HttpServerUtility.UrlPathEncode(), работа которого больше похожа на работу метода HttpServerUtility.HtmlEncode(), но учитывает все особенности построения url. Этот метод не имеет метода для обратного переобразования, но при правильном подходе к проектированию приложенич это не понадобится.

2.2. JavaScript

Аналог URL Encode в javascript - функция encodeURI()


Те, кто с помощью jQuery написал уже не одну сотню, а может и тысячу строк javascript-кода, в этом посте врядли найдут много нового и/или интересного. Пост рассчитан на разработчиков, которые только начинают знакомиться со всей мощью бтблиотеки jQuery. В нем я дам несколько примеров скриптов, которые я время от времени использую с небольшим количеством изменений или вообще без таковых.

[Добавлено перед публикацией поста]
Все примеры достаточно просты, большинство (5 штук!) взяты из документации, но они активно используются мной и я решил сделать пост-заметку для себя. Может, еще кому-то будет полезно. Те, кто не использует jQuery либо другую библиотеку (еще остались такие?) поймут, что эти несколько строк кода в каждом примере иногда заменяют десятки строк кода на javascript’е без использования сторонних библиотек и своих велосипедов.
[/Добавлено перед публикацией поста]

  • Прочитать и/или изменить значение нужного атрибута в элемента DOM-модели:
    var value = $("#element").attr("custimAttribute");
    и
    $("#element").attr("custimAttribute")=”some text”;
  • Тоже самое, но для классов:
    методы .addClass() (http://api.jquery.com/addClass/) и .removeClass() (http://api.jquery.com/removeClass/
  • Сделать подсветку всех картинок на странице по заданному параметру (имя класса):
    $(".image").mouseover(function () {
        $(this).css("border-width", "2px");
        $(this).css("border-style", "solid");
        $(this).css("border-color", "#1b5790");
    });
  • Сделать какие-либо действия над коллекцией объектов:
    $.each([52, 97], function(index, value) {
        alert(index + : + value);
    });

    http://api.jquery.com/jQuery.each/
  • Присвоить tabIndex для всех элементов ввода на форме:
    $(function(){
        var tabindex = 1;
        $(input,select).each(function() {
             if (this.type != "hidden") {
                  var $input = $(this);
                  $input.attr("tabindex", tabindex);
                  tabindex++; 
             }
        });
     });

    http://greatwebguy.com/programming/dom/setting-your-tabindex-on-your-html-forms-automatically-with-jquery/
  • Отправить AJAX-запрос на сервер:
    как ни странно, но примеры из документации http://api.jquery.com/category/ajax/ достаточно работоспособны чтобы использовать их практически без изменений (ссылку на службу все-таки прийдется подставить свою).
  • Создать start-up скрипт на странице (без использования ScriptManager, входящего в состав ASP.NET):
    $(document).ready(function () {
         alert (hello);
    });

 


В связи с тем, что я повсеместно пытаюсь избавиться от ASP.NET AJAX Control Toolkit (ACT) и перейти на использование jQuery, то от использования Hover Extender от ACT пришлось отказаться. Сразу же было найдено большое количество разнообразных плагинов для jQuery, но большинство из них имели недостаточную реализацию, из-за чегоостановился на jQuery plugin EZPZ Tooltip.

Из основных достоинств данного плагина хочется выделить:

  • "Чистый" HTML - не нужно добавлять специальные атрибуты для элементов, плагин завязывается на отдельный HTML элемент.
  • Конфигурация через наименование (Convention over configuration) - поведение элементов зависит от из ID.
  • Гибкие настройки - с помощью CSS можно настроить как внешний вид контрола, так и его позиционирование.
  • Возможность замены стандартных эффектов путем перехвата соответствующих событий на jQuery.
  • Удобство и простота работы.
  • Маленький размер - 4.5K в сжатов виде.

Более подробно о всем этом можно почитать на странице проекта, а я хочу рассказать как можно его использовать в связке с ASP.NET на примере простого UserControl.

Для инициализации контрола нужно выполнить небольшой javascript:

$("#example-target-1").ezpz_tooltip();
или
$("#target").ezpz_tooltip({contentId:"content"}); - В случае если у наших HTML-элементов будут "нестандартные" для этого контрола ID.

Так как, ASP.NET сам генерирует нам ClientID элементов (я не учитываю специфику ASP.NET 4.0, где можно отключать автоматическую генерацию ClientID), то приходится использовать 2-й вариант таки образом:

 

При этом у нас:

  •  TargetControlId - ID элемента, при наведении на который будет отображаться наш hover (tooltip).
  • ContainerClientId - ID элемента который будет показываться в качестве hover (tooltip).

 Разметка нашего user control выглядит так:

 

Нам остаётся только разместить наш UserControl на странице и подключить необходимые стили и скрипты. Можно ещё сделать WebControl, но это уже зависит от конкретной задачи. В моём случае UserControl был предпочтительнее.

HoverControl.zip (31.35 kb)


Разработчики JavaScript-фреймворка JQuery не перестают нас удивлять. После недавнего релиза JQuery 1.4.1 нам представили инструмент для unit-тестирования кода, написанного на JavaScript - QUnit

В данный момент поддерживаются такие функции как expect(), equals(), ok(), same(), log() и асинхронные тесты. Вместе с фреймворком распространяется css-файл, с помощью которого можно быстро и красиво выводить результаты тестов. Также разработчики активно используют его для тестирования самого JQuery, поэтому можно посмотреть уже готовые приверы от авторов фреймворка.

P.S. Что-то подсказывает мне что не загорами плагин для FireBug, по крайней мере, мне бы этого хотелось...


jQuery 1.4 Alfa 1

Published 12/7/2009 by e0ne in Web Development

Несколько дней назад вышла новая версия популярного JavaScript framework'а jQuery, пока только альфа, которую сами разработчики не рекомендуют использовать в production. Официальная информация на блоге http://blog.jquery.com/2009/12/04/jquery-14-alpha-1-released, а я расскажу о своих первых впечатлениях по сравнению с последней стабильной версией 1.3.2.

Для начала сравним размеры файлов:

jquery-1.3.2.js jquery-1.3.2.min.js jquery-1.4a1.js jquery-1.4a1.min.js
117 KB 55 KB 144 KB

87 KB

Видем что размер вырос, примерно, на 30 килобайт. Много это или мало - судите сами.

Пока в целях тестирования подключил библиотеку к двум проектам, где jQuery спользуется не так активно, но, в моём случае, вся функциональнось работает как и работала, ничего не поламалось, плагины успешно работают. Сравнивать производительность с ранней считаю нецелесообразным, т.к. к релизу, а то и к следующим версиям всё может поменяться как в лучшую, так и худшую стороны.


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

Итак, у нас есть исходный массив:

var arr = new Array();
arr.push('q');
arr.push('w');
arr.push('a');
arr.push('e');
arr.push('r');
arr.push('t');
arr.push('z');
arr.push('y'); 

 Задача состоит в том, чтобы удалить из него элементы со значениями "a" и "z". Первой же идеей было использование оператора delete:

var i = arr.indexOf('a');
delete arr[i];
i = arr.indexOf('z');
delete arr[i];

 И было вы всё хорошо, еслиб не реализация этого самого оператора delete: элементы из массива на самом не удаляются, просто их значение стоновится undefined и, соответственно, свойство lenght не меняется. Можно, конечно, при обходе массива проверять значение элементов и таким образом проверять, удалённый он или нет, но в моём случае это несло за собой изменение логики на сервере, что делать не хотелось и было принято решение реализовать нужную функциональность в классе Array:

Array.prototype.removeByValue = function(item){
        var itemIndex = -1;
        for (var index = 0; index < this.length; index++)
        {
            if (this[index] == item){
                itemIndex=index;
            }
        }
        
        if (itemIndex == -1){
            return this;
        }
        
        var resultLeft = this.slice(0, itemIndex);
        var resultRight = this.slice(itemIndex+1, this.length);
        var resultTotal = resultLeft.concat(resultRight);

        return resultTotal;

 };

 Способ использования:

var a = arr.removeByValue('a'); 
arr = a.removeByValue('z'); 

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


После переходна на новую ферсию .net framework, как и следовало ожидать, некоторый код потерял свою работоспособность. Перестали работать веб-службы. Точнее одна из них.

Серверный её код ничем не выделяется от остальных:

[WebService(Namespace = "myns")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class MyService : WebService
{
  [WebMethod, ScriptMethod(ResponseFormat = ResponseFormat.Json)]
  public List<string> GetData()
  {
     List<string> result;
     //вся необходимая логика
     return result;
  }
}


 Главное отличие её от других заключается в том, что обращение к ней идёт не с помощью ScriptManager и ServiceReference (в таком случае всё работает), а вызывается методом POST по url напрямую с javascript.

 В .net 2.0 всё работало на ура, но в новой версии фреймворка от сервера приходил ответ слудующего вида:

{"d":__type":"typeName","name":"item1","value":"item2"}

Т.е. получался объект d, а не тот, что передавался служюой, из-за чего скипт работал неправильно. Похожая ситуация и её объяснение описываются здесь. Security - это, конечно хорошо, но зачем было менять то, что работало в предыдущих версиях .net по умолчанию? Может это повод над тем, что пора отказываться от веб-служб и начинать использовать WCF? Ведь там это всё настраивается с помощью атрибутов.

А пока решение проблемы не найдено, приходится использовать обвёртку и закрывать от глаз тип d.

 

 


Интеграция различных скриптов на страницу, где используется MS Ajax - задача нередкая, но единого решения для неё не существует. Я поделюсь с вами некоторыми методами, которые помогут интегрировать срипт в вашу страницу.

  • Никогда не подключайте скрипты с помощью тега <script src="..."></script>. Вместо этого следует использовать копмонени ScriptManager либо ScriptManagerProxy.
  • Что бы не говорили разработчики MS Ajax и AjaxControlToolkit, в конец каждого файла со скриптом следует добавить такой код:
    if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
    Данный код указывает на то, что скрипт полностью загрущился и можно его выполнять.
  • Если скрипт необходимо подгружать только в определённых ситуациях (например, добавление нового компонента на форму), то его следует регистрировать с помощью статических методов класса ScriptManager вида RegisterClientScriptXXX().
  • Для выполнения скрипта после загрузки на страницу есть несколько методов:
    1) использовать клиентское событие EndRequest;
    2) создать функцию с именем pageLoad() - метод аналогичен первому;
    3) если скрипт необходимо зарегистрировать с помощью серверного кода, то используйте метод ScriptManager.RegisterStartupScript().

Эти методы не являются панацеей от всех бед, но помогают экономить часть времени, необходимого на интеграцию скриптов. Иногда хватает только этого, иногда приходится часами отлаживать чужой javascript, но знать о них, как я думаю, должен каждый разработчик, использующий MS Ajax.


e0ne's comments

A Web Developer's Blog!