Как я уже говорил, подготовка к поездке идёт полным ходом. Следующая часть — билеты. Если посмотреть на цены европейских перевозчиков (DBahn, SNCF), то можно сразу начинать расстраиваться. Поездка через всю Европу с десятком-другим остановок обойдётся в приличную сумму, размер которой, помимо объективных факторов, может зависеть ещё от расположения планет и угловой скорости вращения сферического коня в вакууме. Но мне повезло набрести на неплохое решение — InterRail. Это такой проездной для практически всех европейских железных и не только дорог. Конечно, цена на него может показаться довольно большой, шутка ли — 509 евро за проездной на 22 дня. Но если посчитать общую стоимость всего, что придётся потратить, если ехать без него, то понимаешь: нужно брать.
В нем, конечно, много особенностей. Например, проездной не оплачивает бронирование места, т. е. если поезд с обязательным бронированием, как французские TGV или немецкие ICE, то придётся доплатить, но обычно эта цена в пределах 4 евро, иногда до 10. Ещё проездным нельзя пользоваться в стране своего проживания, но нас это не касается: в Беларуси InterRail не работает вообще. Зато при необходимости можно бесплатно воспользоваться некоторыми паромами и даже автобусами (карту со всеми действующими маршрутами можно взять у них же на сайте).
Свои впечатления постараюсь описать после поездки. Но, может, кто-нибудь уже пользовался этой услугой и сможет поделиться своим опытом или даже предупредить о возможных подводных камнях? Я был бы весьма признателен.
UPD: Детали и впечатления можно прочитать тут.
Сегодня я сдавал ЦТ по математике. И пока сидел в ожидании начала, вспомнил, как когда-то уже давным-давно сдавал экзамен по математике в школе. И тогда была нам предложена вот такая задача (точных цифр и формулировок я уже не помню).
Есть конус, повернутый основанием вверх, с радиусом основания \(R\) и высотой \(h\). Он на половину своей высоты \(h/2\) заполнен водой. В этот цилиндр погружается шар с радиусом \(r\). Какой объем шара будет погруженным в воду?
Не нашел, чем нарисовать подобную композицию, поэтому вот рисунок от руки:
Вышло так, что я до сих пор помню, как на протяжении практически всего экзамена пытался решить эту задачу, и в конце-концов учительница, видя что горит моя (и ее) 10-ка, просто дала мне списать решение из ответов. Каково же было мое удивление, когда я увидел решение. Тогда я пытался сказать, что решение неадекватно и не соответствует задаче, но меня никто не стал слушать.
И вот сейчас я вспомнил про нее и решил спросить у тех, кто что-то помнит в математике и стереометрии. Как бы вы решали подобную задачу? Не кажется ли вам, что ее нельзя решить, используя только школьный курс математики?
Жду комментариев. А потом расскажу о своем решении и решении Минобразования.
Как вы, должно быть, знаете, хранить пользовательские данные на клиентской стороне плохо: их ведь может подделать злостный хакер. Но сегодня я вам расскажу, как можно безопасно хранить сессионные данные, не волнуясь за их сохранность. Расскажу на примере Snap Framework, на котором и написан этот блог.
Суть метода — шифрование (кто бы мог подумать). Snap для своих сессий использует библиотеку Web.ClientSession, а поэтому что и как обрабатывается можно смело подсмотреть в документации:
- Шифруем данные куки с помощью AES в режиме CTR. Это позволяет хранить важные данные на стороне клиента, не беспокоясь о том, что их кто-нибудь украдет.
- Подписываем зашифрованные данные с помощью Skein-MAC-512-256. Кроме определения потенциальных ошибок в хранении и передачи данных (целостность), MAC предотвращает изменения данных и подтверждает, что они действительно были сгенерированны этим сервером (подлинность).
- Кодируем результат с помощью Base64, таким образом избавляемся от различных непечатных символов и отдаем браузеру простую строку.
Изучая всякие википедии о AES, я не особо понял, какие преимущества нам даст режим CTR в плане безопасности, но судя по описанию коммитов к библиотеке в этом режиме результат получается чуть более компактным и сам алгоритм чуть проще реализовывается. Про любой другой язык я могу сказать, что почти наверняка есть библиотека, которая уже реализует этот алгоритм за вас, так что сильно думать не придется.
Skein — это просто хороший алгоритм хеширования. К сожалению, он не победил в конкуре на замену SHA-2, и название SHA-3 получил другой алгоритм. Поэтому самое время заменить Skein на новоявленный SHA-3 — Keccak.
Ну и напоследок несколько мыслей. Во-первых, этот метод хранения несколько надежнее традиционных, т.к. никакой форс-мажор на сервере не позволит данным потеряться. А если что-нибудь произойдет со стороны клиента и кука исчезнет, то на сервере у нас не останется никаких мусорных данных об уже не существующей сессии. Во-вторых, таким образом не получится хранить большие объемы данных, т.к. размер куки ограничен, да и передача каждый раз пары десятков-сотен килобайт вполне может увеличить нагрузку на сервер. В-третьих, увести сессию можно точно так же, как и обыкновенную, поэтому увеличения безопасности тут не будет.
В общем, идея имеет право на жизнь. Особенно на небольших, как в моем случае, объемах хранимых данных. Лично я храню только одно поле: является ли текущий пользователь админом или нет.
Хозяйке на заметку: чтобы вашу сессию нельзя было увести с помощью JavaScript, добавьте в заголовок Set-Cookie
параметр HttpOnly
. Куки с таким параметром не появляются в document.cookie
.
Осталась одна загадка, связанная с моим сайтом. Кука _session
передается как сессионная и должна удаляться с закрытием браузера. Так и происходит в Opera, но не Google Chrome и Firefox. Может есть какая-то стандартизированная или неофициальная фича, которая не дает сессионным кукам удалиться?
Итак, я написал некоторое количество тестов, поправил несколько багов. Пришла пора задуматься: что же делать дальше? Как должно выглядеть API будущей библиотеки? Пожалуй, я выложу несколько вариантов своего видения, чтобы вы могли высказать свои за и против. Очень надеюсь на ваши комментарии.
Вариант 1:
NewClass = Class.extend("OptionalDisplayName", {
constructor: function () {},
properties: {
property1: {
set: function(value) {this.properties_.property1 = value;},
get: function() {return this.properties_.property1;},
initial: 'Initial value'
},
property2: { // Property with default getter and no setter
get: true
}
},
statics: {
staticField1: null,
staticMethod1: function(arg1) {return arg1;},
properties: {
staticProperty1: {get: true, set: true}
}
},
field1: null,
method1: function() {}
});
NewClass2 = NewClass.extend({
constructor: function () { this.inherited(); },
method1: function() { this.inherited(); },
method2: function() {}
});
Вариант 2:
NewClass = createClass({
__name__: "OptionalDisplayName",
constructor: function () {},
property1: __property__({
set: function(value) {this.properties_.property1 = value;},
get: function() {return this.properties_.property1;},
initial: 'Initial value'
}),
property2: __property__({ // Property with default getter and no setter
get: true
}),
staticField1: __static__(null),
staticMethod1: __static__(function(arg1) {return arg1;}),
staticProperty1: __staticProperty__({get: true, set: true}),
field1: null,
method1: function() {}
});
NewClass2 = createClass({
constructor: function () { this.inherited(); },
extend: NewClass,
method1: function() { this.inherited(); },
method2: function() {}
});
Какой из вариантов кажется более логичным и удобным? Может, составить какой-то смешанный вариант или у вас есть предложения, кардинально отличающиеся от этих двух? Не стесняемся, пишем комментарии.
«Капсулу с посланием потомкам заложат в основание белорусской АЭС 3 августа».
Вот допустим, вы решили посадить сына построить дом. И тут вам на голову упало яблоко или кирпич: вы решили заложить в основание послание к потомкам. Что вы там напишете?
Мой вариант: «Молодец! Ты снес к чертям эту халупу. А теперь еще 25 метров вниз и там будет клад. Можешь не благодарить. Твои предки».