Показаны сообщения с ярлыком flash. Показать все сообщения
Показаны сообщения с ярлыком flash. Показать все сообщения

суббота, 9 января 2010 г.

Flex, PHP: Безопасная передача данных с клиента на сервер

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

1. Подписывание данных секретным ключом.
Это дает гарантию, что данные не будут подменены на промежуточных узлах до сервера.
Технология следующая:

a. Генерируем секретный ключ, о котором знает только приложения клиента и сервера
b. К передаваемым данным на сервер добавляем еще один параметр равный хэшу данных с секретным ключом. Пример на Flex:
var key:String = "0000-0000-0000-0000-0000";
var sreq:String = "method=setBalance&addMoney=1000"; //сам запрос
//отсортируем параметры
//чтобы последовательность параметром и на клиенте и на сервере была одинакова. Это гарантирует схождение хэшей.
sreq = sreq.split("&").sort().join("&");
//добавим к параметрам время, чтобы нельзя было продублировать запрос 
var now:Date = new Date();
var sec = now.getTime().toString().substr(0, -3);
//конечный запрос к серверу с дополнительным параметром = хэшу запроса и секретного ключа
sreq = sreq + "&timestamp="+sec+"&sig="+MD5.hash(sreq+key);
c. Сервер принимает запрос и проверяет данные используя свой секретный ключ.
К сожалению нельзя использовать четкое совпадение времени клиента и сервера, т.к. оно может отличаться или сообщение может идти до нескольких секунд. Из-за этого на сервере допускается расхождение timestamp до 100 секунд. Серверный код на PHP:
$_TIME    = $_SERVER['REQUEST_TIME'];//время запроса
$_KEY     = "0000-0000-0000-0000-0000";//тотже ключ, что и на клиенте
$_TIMEOUT = 100; //допустимое отклонение времени
// Проверим валидность данных
//присланная подпись должна сойтись со сгенерированной на основе закрытого ключа 
$q = $_GET;
//отсортируем также как в flex
unset($q['timestamp'], $q['sig']);
ksort($q);
$req = "";
foreach($q as $k=>$v) {
  $req .= $k."=".$v."&";
}
$req = substr($req, 0, -1);

if($_GET['sig'] != md5($req.$_KEY)) {
  //подписи не сошлись. Данные были подменены.
  exit;
}
//Проверим расхождение времени
if($_GET['timestamp'] + $_TIMEOUT < $_TIME) {
  //Расхождение более чем на 100 секунд. Прерываем запрос
  exit;
}

К сожалению такой метод не гарантирует нам, что запрос не будет перехвачен на промежуточном узле и не продублирован N раз в течении этих 100 секунд. Для устранения этого служит следующий пункт:

2. Проверка запроса на дублирование.
Идею я позаимствовал из TCP/IP. Добавим к запросу еще один параметр = счетчику сообщений, а на сервере будем хранить значение последнего счетчика. На клиенте достаточно сгенерировать счетчик = текущее значение + 1, а на сервере принять, проверить что новое значение больше старого и обновить серверный счетчик. Это гарантирует нам, что если злоумышленник перехватит запрос и пошлет его заново, то сервер его отвергнет, т.к. значение будет меньше хранящегося на сервере.
Для реализации этого механизма на сервере нам уже понадобится БД, а на клиенте дополнительный запрос на получение счетчика.

Под катом можно увидеть полный код клиента и сервера:

вторник, 5 января 2010 г.

Flex: Построение древовидного объекта по XML данным

Начал изучать Flex. Хочу поделиться некоторыми наработками, которые могут пригодиться.

Класс для создания древовидного объекта по XML строке.
Допустим у нас есть XML данные, полученные с удаленного хоста или еще как-нибудь. Xml может иметь смешанный тип, т.е. есть и обязательные теги (типа response), так и повторяющиеся на разных уровнях (section, id и т.д.)
Xml:

В программе удобней работать с объектом, чем с текстовыми данными, для этого служит следующий класс:
package Lib { 
  public class XMLTree {
  private var _xml:XML = null;
  private var _data:Object = new Object();
  private var _maxElem:int = 500;//ограничение на число элементов
     
  //конструктор, передается текстовый xml
  public function XMLTree(_xml:String) {
   this._xml = new XML(_xml); //создание объекта
   this.xmlParser(this._xml, this._data);//парсинг
  }
  
  public function getData():Object {
   return this._data;
  }
  
  //рекурсивный обход дерева
  private function xmlParser(node, obj:Object):void {
   var sObjName = "";
   //по каждому ответвлению
   for (var i = 0; i < node.length() && i < this._maxElem; i++) {
     sObjName = node[i].name();
     //если это ветка (имеет дочерей)
     if(node[i].hasComplexContent()) {
       //т.к. названия веток могут повторяться. Если свойство уже существует, то создается свойство с именем = имя + _ + уникальный идентификатор
       if(obj[sObjName] != undefined) {
         sObjName = sObjName + "_" + i;
       }
       //передаем ветку и текущее положение в деревянном объекте на следующий цикл рекурсии
       obj[sObjName] = new Object;
       this.xmlParser(node[i].children(), obj[sObjName]);
     } else {
       //создаем свойство с именем ветки XML объекта и текстовым значением
       obj[sObjName] = node[i].text();
     } //if
    }//for
   }//func
 }//class
}//package
Следующий код создаем объект и перечисляет значения первой ветки section дерева:
var xmlObj:XMLTree = new XMLTree(sXml);
var data:Object = xmlObj.getData();
for(var o in data.response.section) {
  Alert.show(data.response.section[o]);
}