Безопасное квотирование

April 24th, 2012 1 comment

В продолжение темы habrahabr.ru/post/142599…

Кроме проверки цифровых данных посредством приведения их функциями intval() и floatval(), есть случаи когда не стоит использовать mysql_real_scape_string().

Приведу пару примеров.

Логины, например при авторизации. Практически везде пространство логинов ограничено определенным набором символов. Например у нас разрешены латинские буквы и цифры в логине. Кроме того логин не должен быть длиннее 8 символов. Тогда прежде чем использовать введенную пользователем строку в запросе стоит выполнить следующий простой код:

$login = preg_replace("#[^a-z0-9]#i", "", $login);
$login = substr($login, 0, 8);

Ключи – разнообразные ключевые строки, напимер хеши md5 и sha1, которые используются как ключ при восстановлении пароля или подтверждении регистрации из E-mail. Хеши опять же имеют известную длинну и набор символов. Так что к ним можно применить предыдущий код соответственно его модифицировав.

К чему я все это пишу – к тому что если вы можете предвидеть структуру значения поля, её надо проверять избегая mysql_real_scape_string(). Потому как ваша проверка, ручная всегда будет более точной.

Categories: Вне категорий Tags:

jQuery: Отмена активных ajax запросов

October 27th, 2011 3 comments

Иногда требуется отменить все активные XHR, либо какую-то часть из них. Для этой нужны я накидал плагин  для jQuery, которые автоматически добавляет все XHR в коллекцию с возможностью указать тег.

 

(function($) {
	$.xhrPool = {};
 
	$.xhrPool.abortAll = function() {
		$.each(this, function(pool, map) {
			$.each(map, function(idx, jqXHR) {
				jqXHR.abort();
				$.xhrPool[pool].splice(idx, 1);
			});
		});
	};
 
	$.xhrPool.abort = function(pool) {
		if (typeof this[pool] != "undefined") {
			$.each(this[pool], function(idx, jqXHR) {
				try {jqXHR.abort();} catch (e) {}
				$.xhrPool[pool].splice(idx, 1);
			});
		}
	};
 
	$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
		var pool = options.xhrpool || "global";
 
		if ( ! $.xhrPool[pool]) {
			$.xhrPool[pool] = [];
		}
 
		$.xhrPool[pool].push(jqXHR);
	});
})(jQuery)

Тег указывается в вызове $.ajax() опцией xhrpool. Без указания этой опции все XHR будут добавляться в $.xhrPool.global. Также доступны 2 метода: $.xhrPool.abortAll() — останавливает все запросы и $.xhrPool.abort(“%pool%”) — останавливает запросы в коллекции с указанным тегом.

Отправка большой формы через Ajax

October 11th, 2011 No comments

Любой кто хотя-бы раз отправить большую форму через Ajax – сталкивался с проблемой, когда в функции отправки приходилось перечислять заново все поля.

Решение проблемы достаточно просто: нам нужна функция которая сама разберет форму.

Первый вариант который приходит в голову:

function parseForm(identifier){
  // Получаем все инпуты содержащиеся в форме
  var inputs = document.getElementById(identifier).getElementsByTagName('input');
  // Инициализируем массив для результатов
  var values = {};
  // Перебираем элементы
  for (var n in inputs){
    values[inputs.n.getAttribute('name')] = inputs.n.getAttribute('value');
  }
}

Но тут есть проблема, в частности с input’ами типа checkbox и radio.

Немного модифицируем функцию что-бы корректно обрабатывать их.

function parseForm(identifier){
  // Получаем все инпуты содержащиеся в форме
  var inputs = document.getElementById(identifier).getElementsByTagName('input');
  // Инициализируем массив для результатов
  var values = {};
  // Перебираем элементы
  for (var n in inputs){
    if(inputs[n].getAttribute('type') == "checkbox"){
      // Если тип - checkbox, то получаем значение checked
      values[inputs[n].getAttribute('name')] = inputs[n].getAttribute('checked');
    }else if(inputs[n].getAttribute('type') == "radio"){
      // Если тип - radio, получаем список всех input с этим именем
      var radios = document.getElementById(identifier).getElementsByName(inputs[n].getAttribute('name'));
      // Перебираем их
      for (var i in radios){
        // Если текущий элемент выделен то ложим в результат его значение
        if(radios[i].getAttribute('checked')){
          values[inputs[n].getAttribute('name')] = radios[i].getAttribute('value');
        }
      }
    }else{
      values[inputs[n].getAttribute('name')] = inputs[n].getAttribute('value');
    }
  }
 
  return values;
}

Вот казалось бы и все, но мы еще забыли про select.
Для него придется добавить в функцию еще часть:

var selects = document.getElementById(identifier).getElementsByTagName("select");
 
for(s in selects){
    var options = selects[s].getElementsByTagName("option");
    // Перебираем их
    for (var i in options){
      // Если текущий элемент выделен то ложим в результат его значение
      if(options[i].getAttribute('selected')){
        values[selects[n].getAttribute('name')] = options[i].getAttribute('value');
      }
    }
}

Реализация этого кода на jQuery останется домашним заданием 🙂

SSD и Linux

October 3rd, 2011 No comments

Наверное все знают, что SSD это круто в плане скорости чтения. Но совсем не круто в плане скорости записи, и еще менее круто в плане живучести. Особенно MLC устройства.

Для того, чтобы решить обе эти проблемы в среде Windows, к каждому SSD девайсу прилагается тулз который позволяет настроить over-provisioning.  Но что делать *nix-администраторам?

Для того чтобы все было айс и тру – нам нужна тулза hdparm, и SSD винт должен быть подключен не в рейд-контроллер, а напрямую к SATA порту.

  1. Устанавливаем мастер-пароль для SSD.
    hdparm –user-master u –security-set-pass 123 /dev/sdb
  2. Выполняем secure erase.
    hdparm –user-master u –security-erase 123  /dev/sdb
  3. Ограничиваем доступный размер диска. Нужное вам количество секторов посчитайте сами.
    hdparm -Np1234567890 /dev/sdb

Если по какой-то причине с пунктом 3 у вас не сложилось, например как у нас – проблема с HPA, которую лениво решать, то можно его заменить: при создании рейда (если планируете использовать SSD  в рейде) либо при создании разделов (если планируете использовать SSD как самостоятельное устройство) вы должны указать только часть реального объема диска. Например – 80%. Или 50%. Все оставшееся после подобной разметки пустое место SSD автоматически применит для wear-off leveling, что значительно продлит жизнь вашему SSD и однозначно придаст ему бодрости при записи.

Categories: Linux Tags: , ,

Кроссдоменный AJAX

September 24th, 2011 No comments

Самое простой способ — использование объекта XMLHTTPRequest в современных браузерах(Chrome 6+, Firefox 3.5+, Internet Explorer 8+, Safari 4+) и наличие у сервера заголовка Access-Control-Allow-Origin: *

Все бы хорошо, вот только заметили ли вы в списке Оперу? Нет, она еще не поддерживает XMLHTTPRequest 2. Да и то что IE не поддерживает в 7-ой версии может оказаться для кого-то критичным. Ниже последуют другие способы кроссдоменного ajax’a.

Проксирование

Довольно популярный метод, представляет из себя отправку запроса на серверный скрипт, который получает целевой url и возвращает его в ответе на запрос. Привязка к серверному скрипту делает этот метод недопустимым в некоторых случаях. Да и как минус можно задержку отметить.

 JSONP

JSON Padding или так называемый “JSON с подкладкой” — способ с JSON при котором имя калбэка указывается в аргументах запроса. При этом запрос подгружается через тег script, а в ответе json-объект должен быть заключен в вызов того самого переданного калбэка. Пример:

запрос http://example.com/?q=somevalue&callback=mycallback
ответ  mycallback({"foo": "bar", "a": "x"})

Из минусов возможность использовать только GET-запрос.

Политика безопасности также позволяет загружать css’ники с любых доменов, поэтому аналогично JSONP появился метод CSSHttpRequest. Особой выгоды в нем не вижу, кому интересно могут глянуть на странице проекта.

XHRIframeProxy

Метод основан на том, что ифреймы находясь на разных доменах могут общаться через url hash.

Для использования необходимо 2 ифрейма, первый – клиентский, с тем же url, что и основное окно. Второй – внутри клиентского, с серверным url. Данные отправляем через форму с указанным target=имя фрейма. Затем запускаем ф-цию с интервалом в 1 секунду и ждем пока хэш страницы не изменится – тогда мы получили ответ от сервера. Идентификатор изменяется серверным скриптом через self.parent.location = ‘…’

Стоит иметь ввиду, что в Internet Explorer есть ограничение на кол-во символов в url и составляет  2048 символов. Так что при передачи большого кол-ва данных придется делить на различные запросы. Кроме того необходимо в серверный фрейм передавать url основного окна – серверный фрейм его узнать не сможет.

При использовании https  этот способ корректно работать не будет.

postMessage (HTML 5)

В спецификации HTML 5 был введен метод

otherWindow.postMessage(message, targetOrigin);

в котором otherWindow – ссылка на другой объект window (например ифрейм на этой странице), message – данные которые должны послать, targetOrigin – домен на которой должны отослать данные, либо “*” – для всех доменов. targetOrigin должен соответствовать url объекта window на который посылаются данные.

В окне-приемнике должен быть зарегистрирован обработчик onmessage чтобы получить сообщение. Пример:

if (window.addEventListener){ 
    window.addEventListener(“message”, listener,false);
} else {
    window.attachEvent(“onmessage”, listener);
}

Ф-ция listener получается один параметр – объект event, по свойству origin которого можно узнать домен, с которого отправляли данные.

Работает postMessage в FF3+, IE8+ , Chrome, Safari 5, Opera10+. Стоит отметить что в IE8 нельзя общаться через окна/табы.

Практика работы с AoE

September 24th, 2011 No comments

Пост пишу с планшета, потому не будет пространных объяснений, только резюм и факты 🙂

 

  • Для AoE Target используем только GGAoED.
  • Для AoE Initiator используем только свежие модули, и компиляем руками. Пакетные из ядра – в /dev/null
  • На таргете direct-io=true
  • На инициаторе io scheduler ставим noop.
  • На обоих сетевухах и свитче между ними включаем опцию Jumbo Frames. Размер фрейма 8192.
  • Hardware flow control.
  • noatime, nodiratime – без вариантов.
  • XFS и Ext3/4 желательно использовать с barrier=1
  • Буфера на сетевых картах поднимать сверх всякой меры.
  • Процессу GGAoED приоритет ionice – максимальный. Вплоть до realtime.
Применяя эти маленькие хитрости, оверхед на аое у вас крайне редко будет превышать 1мс.
Categories: Linux Tags: ,

MySQL и битые таблицы

September 20th, 2011 No comments

Положим, вам не повезло, и у вас побился файл таблицы mysql.users, а бэкапа нет. И repair не помогает. И войти на сервер уже нельзя. И пользователи негодуют. Как быть?

Расскажу, как мы решили подобную проблему однажды.

  1. Выключаем покалеченный сервер.
  2. Подкладываем содержимое var/mysql с любого “здорового” сервера аналогичной версии
  3. Запускаем сервер, и входим с логином/паролем “здорового” сервера.

Начиная с этого момента, у нас уже есть рут-доступ к серверу бд. Теперь нужно восстанавливать пользователей.

Если ваши условия схожи с нашими, то файлы  у вас разложены в в определенном порядке и с определенной структурой.  В нашем случае логины мы знаем зная путь к папке пользователя.
Т.е. фактически из тройки сервер-логин-пароль, два элемента мы уже знаем. Остается узнать установленные ранее пароли. И сделаем мы это просто:

find /directory -type f -exec grep ‘{sql.server.ru}’ \; >mysql.log

На выходе, в файле mysql.log мы получим список файлов, в котором встречается указанный вами адрес сервера.

После этого остается только распарсить файлы на предмет паролей, и затулить их обратно в базу данных.
Что опять же, не очень сложно, если формат пароля более-менее стандартизирован, и основные CMS известны: Joomla, WordPress, Drupal, e107, SMF, phpBB итд. Детали парсинга, если пожелает, раскроет fer0x.

Несколько часов жужжания – и 95% паролей восстановлено. Оставшиеся 5% – восстанавливать прийдется руками, по мере обнаружения не восстановленных.

Categories: Linux Tags:

В жизни каждого админа наступает момент,

September 19th, 2011 No comments

когда он судорожно вспоминает дату  создания последнего бэкапа 🙂

Сегодня я расскажу о прошедших выходных, и мертвом винте на рейде.

MegaCli64 -LDInfo -LALL -a0

Adapter 0 -- Virtual Drive Information:
Virtual Drive: 0 (Target Id: 0)
Name                :
RAID Level          : Primary-6, Secondary-0, RAID Level Qualifier-3
Size                : 8.177 TB
State               : Partially Degraded
Strip Size          : 64 KB
Number Of Drives    : 20
Span Depth          : 1
Default Cache Policy: WriteBack, ReadAheadNone, Cached, No Write Cache if Bad BBU
Current Cache Policy: WriteBack, ReadAheadNone, Cached, No Write Cache if Bad BBU
Access Policy       : Read/Write
Disk Cache Policy   : Disk's Default
Encryption Type     : None
Default Power Savings Policy: Controller Defined
Current Power Savings Policy: None
Can spin up in 1 minute: Yes
LD has drives that support T10 power conditions: No
LD's IO profile supports MAX power savings with cached writes: Yes

Значиццо в рейде есть мертвый диск.

Ищем и находим:

MegaCli64 -PDList -a0

Вывалит инфу по всем дискам. Ищем наш:

Enclosure Device ID: 31
Slot Number: 12
Enclosure position: 0
Device Id: 20
Sequence Number: 4
Media Error Count: 2
Other Error Count: 4
Predictive Failure Count: 0
Last Predictive Failure Event Seq Number: 0
PD Type: SATA
Raw Size: 465.761 GB [0x3a386030 Sectors]
Non Coerced Size: 465.261 GB [0x3a286030 Sectors]
Coerced Size: 465.25 GB [0x3a280000 Sectors]
Firmware state: Unconfigured(bad)
SAS Address(0): 0x5003048000d01f58
Connected Port Number: 0(path0)
Inquiry Data: WD-WMAYP1206128WDC WD5003ABYX-01WERA0 01.01S01
FDE Capable: Not Capable
FDE Enable: Disable
Secured: Unsecured
Locked: Unlocked
Needs EKM Attention: No
Foreign State: None
Device Speed: 3.0Gb/s
Link Speed: 3.0Gb/s
Media Type: Hard Disk Device
Drive: Not Certified
Drive Temperature :31C (87.80 F)

 

Смотрим в лог контроллера, что там произошло:

/17/11 22:46:23: EVT#06910-09/17/11 22:46:23: 267=Command timeout on PD 14(e0x1f/s12) Path 5003048000d01f58, CDB: 28 00 01 bf 1e 80 00 00 80 00^M
09/17/11 22:46:23: EVT#06911-09/17/11 22:46:23: 267=Command timeout on PD 14(e0x1f/s12) Path 5003048000d01f58, CDB: 28 00 27 a0 23 80 00 00 80 00^M
09/17/11 22:46:23: EVT#06912-09/17/11 22:46:23: 267=Command timeout on PD 14(e0x1f/s12) Path 5003048000d01f58, CDB: 28 00 0d 5d 1d 17 00 00 08 00^M
...........
...........
...........
09/17/11 22:50:13: Disc-prog= 0....resetProg=0 aenCount=0 transit=0 ^M
09/17/11 22:50:14: EVT#06995-09/17/11 22:50:14: 113=Unexpected sense: PD 14(e0x1f/s12) Path 5003048000d01f58, CDB: 28 00 0b 77 f3 3f 00 00 08 00, Sense: 6/29/00^M
09/17/11 22:50:14: Raw Sense for PD 14: 70 00 06 00 00 00 00 0a 00 00 00 00 29 00 00 00 00 00^M
09/17/11 22:50:14: HDD read error from RDM!!!!!^M
09/17/11 22:50:14: HDD Read FAILURE!!!! Id 71 line 0^M
09/17/11 22:50:14: FAILED: Id 71 block 57fca2d7 num 16^M

 

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

 

Смотрим в смарт, чего там с пострадавшим винтом:

smartctl -a -d megaraid,20 /dev/sda

Интересует нас раздел значений и порогов.

ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
1 Raw_Read_Error_Rate     0x002f   198   198   051    Pre-fail  Always       -       66784
3 Spin_Up_Time            0x0027   164   143   021    Pre-fail  Always       -       2758
4 Start_Stop_Count        0x0032   100   100   000    Old_age   Always       -       41
5 Reallocated_Sector_Ct   0x0033   134   134   140    Pre-fail  Always   FAILING_NOW 1363
7 Seek_Error_Rate         0x002e   200   163   000    Old_age   Always       -       0
9 Power_On_Hours          0x0032   097   097   000    Old_age   Always       -       2238
10 Spin_Retry_Count        0x0032   100   253   000    Old_age   Always       -       0
11 Calibration_Retry_Count 0x0032   100   253   000    Old_age   Always       -       0
12 Power_Cycle_Count       0x0032   100   100   000    Old_age   Always       -       38
192 Power-Off_Retract_Count 0x0032   200   200   000    Old_age   Always       -       37
193 Load_Cycle_Count        0x0032   200   200   000    Old_age   Always       -       3
194 Temperature_Celsius     0x0022   113   110   000    Old_age   Always       -       30
196 Reallocated_Event_Count 0x0032   001   001   000    Old_age   Always       -       1363
197 Current_Pending_Sector  0x0032   200   200   000    Old_age   Always       -       0
198 Offline_Uncorrectable   0x0030   200   200   000    Old_age   Offline      -       0
199 UDMA_CRC_Error_Count    0x0032   200   200   000    Old_age   Always       -       0
200 Multi_Zone_Error_Rate   0x0008   001   001   000    Old_age   Offline      -       79357

Вот:   5 Reallocated_Sector_Ct   0x0033   134   134   140    Pre-fail  Always   FAILING_NOW 1363

Эта ошибка – это уже признак неизбежной смерти винчестера.  1363 бэд сектора были переназначены. Это число теперь будет только расти, т.к. диск “начал сыпаться”. И мы его уже не можем использовать, для нас это уже мусор.

Итого, осталось достать старый винт из сервера, и вставить новый.

А что касается рейда – с ним всё отлично, как только умер один жесткий диск, первый из hot-spare дисков был подключен, и рейд автоматически начал восстанавливаться на нем. Сутки ожидания, и – порядок!

Единственное, отмечу, что на время восстановления рейда среднее время Iowait выросло на 15 миллисекунд.

Categories: Linux Tags: , ,

Особенности переноса < script >.

September 18th, 2011 No comments

Если перед вами встанет задача изменить положение контейнера (DIV’а, SPAN’а, etc), содержащего внутри себя тег SCRIPT, при помощи JavaScript то вы столкнетесь с такой проблемой: после переноса все содержимое страницы будет заменено на результат работы содержимого этого SCRIPT (в независимости от того подключает ли этот тег внешний скрипт или содержит внутренний).

Есть простое решение, этот скрипт уже отработал и больше запускаться не должен можно просто удалить его перед переносом, например вот так:

var scripts = obj.getElementsByTagName("script");
for (var n in scripts){
  obj.removeChild(scripts[n]);
}
// И уже чистый элемент
target.appendChild(obj);

То же самое на jQuery:

$(obj).find("script").remove();
// И уже чистый элемент
$(obj).appendTo(target);

Если же вы хотите что бы скрипты остались в рабочем состоянии то придется немного извратится:

var scripts = obj.getElementsByTagName("script");
var scripts_src_backup = Array();
var scripts_content_backup = Array();
for (var n in scripts){
  if(scripts[n].src !== undefined){
    scripts_src_backup.push(scripts[n].src);
  }else{
    scripts_content_backup.push(scripts[n].innerHTML);
  }
  obj.removeChild(scripts[n]);
}
// Переносим чистый элемент
target.appendChild(obj);
// Возвращаем скрипты на место
for (var n in scripts_src_backup){
  var new = document.createElement("script");
  new.src = scripts_src_backup[n];
  obj.appendChild(new);
}
delete(scripts_src_backup);
for (var n in scripts_content_backup){
  var new = document.createElement("script");
  new.innerHTML = scripts_content_backup[n];
  obj.appendChild(new);
}
delete(scripts_content_backup);

То же на jQuery:

var scripts_src_backup = Array();
var scripts_content_backup = Array();
$(obj).find("script").each(function(index, element){
  if(element.attr("src") !== undefined){
    scripts_src_backup.push(element.attr("src"));
  }else{
    scripts_content_backup.push(element.html());
  }
}).remove();
 
// Переносим чистый элемент
$(obj).appendTo(target);
 
// Возвращаем скрипты на место
for (var n in scripts_src_backup){
  $('< script type="text/javascript" ></ script >').attr("src", scripts_src_backup[n]).appendTo(obj);
}
delete(scripts_src_backup);
for (var n in scripts_content_backup){
  $('< script type="text/javascript" ></ script >').html(scripts_content_backup[n]).appendTo(obj);
}
delete(scripts_content_backup);

Таким образом элемент нормально переместился и скрипты остались рабочими.

Новые шаблоны

September 17th, 2011 No comments

В связи с запуском тестовой версии templatium.ru на Векси больше не будет премодерации шаблонов.

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