Pull to refresh

Comments 31

Это в самом Контуре такие сервисы пишут?
Ну что вы, как вы могли такое подумать? ;)

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

Я, конечно, слышал, что рандомность Random весьма условна, но чтоб настолько...

Вообще-то, это довольно хороший алгоритм: из некриптостойких алгоритмов лучше него только Вихрь Мерсенна.
Объясните пожалуйста тупому, почему в первом примере rnd.Next() всегда возвращает 0
MSDN
The System.Random class and thread safety
Instead of instantiating individual Random objects, we recommend that you create a single Random instance to generate all the random numbers needed by your app. However, Random objects are not thread safe. If your app calls Random methods from multiple threads, you must use a synchronization object to ensure that only one thread can access the random number generator at a time. If you don't ensure that the Random object is accessed in a thread-safe way, calls to methods that return random numbers return 0.
Но каков механизм появления этих нолей?
В конструкторе Random устанавливаются значения inext в 0, inextp в 21. При безопасном использовании это расстояние сохраняется.
Смотрим на InternalSample (есть в статье).
Вангую, что из-за доступа из разных потоков к переменным inext и inextp может происходить рассинхронизация и расстояние между ними не всегда будет постоянным. Если так вдруг случилось, что inext и inextp совпали, то за 55 вызовов массив SeedArray весь обнулится. Далее все «случайные» числа будут высчитываться как 0 — 0.
Про забавную совместимость забавно, у меня использовался как-то string.GetHash() и между версиями 1.1 и 2 в .NET они его поменяли.

Поскольку функция была не особо часто используемой, но нужной для инфраструктурной совместимости, родилось public static int GetHashCode11 (string str), и чтобы особо не париться, сделано это было через вызов StringGetHashCode11.exe.
И на всех серверах стоит .NET 1 для этого :)

Ужосы какие, наверное про изменение хеша в общем тоже кто-то где-то статью написал. А с рандомом вот решили не делать.
> Про забавную совместимость забавно, у меня использовался как-то string.GetHash() и между версиями 1.1 и 2 в .NET они его поменяли.

Что увидительно, и про GetHashCode, и про Random написано одинаково, что их реализация от платформы к платформе может отличаться.

Note that the example may produce different sequences of random numbers if run on different versions of the .NET Framework.
Я особо и не против, у меня много такой мякоты, но вся она находится в namespace Darwin от слова премия :)
… в ту же степь там всякие переходники ремоутинга, сериализации, биндеры какие-то,
я просто с уважением отношусь к legacy коду, его к сожалению очень много накапливается за года, но если он хорошо работает, то часто лучше адаптироваться
я просто с уважением отношусь к legacy коду, его к сожалению очень много накапливается за года, но если он хорошо работает, то часто лучше адаптироваться

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

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

Вообще много таких примеров на самом деле. Зависит от того, дорог ли вам как память код 20-летней давности. Я не сторонник рефакторинга раз в 5 лет, если он и так отлично работает. До определенного предела конечно.
Но такие вот особо одаренные случаи как результат тоже неизбежны.
Вполне верю. Взять тот же C/C++ — до распространения 64-битных архитектур никто не заморачивался над размером указателя, да и не было гайдлайнов на эту тему.
Некоторые фрагменты кода из прошлого века сейчас нормально так работают, преимущественно C/CPP конечно, некоторые конечно выкинуты. Некоторые вообще на умерших языках типа managed extensions для CLR первой версии, которую зарубили.
Переписывать это — трудозатраты часто совершенно не оправданы.
Это всё только взгляд с перспективы десятков лет, конечно. Так вот сев подумав, «да я всё перепишу», но когда кода реально много, иногда переписывать — это просто полная дурь.

Спасибо, интересно видеть, как на практике это можно взломать.


При использовании Random нужно не забывать о синхронизации доступа либо использовать ThreadStatic-экземпляры. При создании нескольких экземпляров, необходимо позаботиться о seed, чтобы не получить множество одинаково инициализированных объектов.

Не понимаю. Т.е. рекомендация такая. Делай один уникальный Random для каждого потока (ThreadStatic). Т.к. если один Random для многих потоков, то его легче предсказать. А как о seed заботиться? Количество тиков из DateTime по какому-нибудь модулю брать или как?

Можно брать seed из другого рандома или из RNGCryptoServiceProvider.
w1ld Количество тиков (Environment.TickCount) и так используется в конструкторе Random по умолчанию, его нежелательно использовать в сценарии ThreadStatic экземпляров, потому что тогда seed может оказаться одинаковым для всех экземпляров (потоков), если конструктор будет вызван одновременно. Обычно используют один статический экземпляр Random, как предложилmayorovp, для получения случайных seed (под локом), которыми, в свою очередь, инициализируют ThreadStatic экземпляры.
У меня например в однострочник реализовано:
private static readonly ThreadLocal<Random> Random = 
   new ThreadLocal<Random>(
      () => new Random(Environment.TickCount ^ Thread.CurrentThread.ManagedThreadId)
   );

Просто и удобно
не раскрывать лишнюю информацию об окружении.

Плохой совет, на мой вкус. Security through obscurity же.
Сложный вопрос. Компьютер же — детерминированная штука. Так что либо нужно генерировать случайные числа с помощью квантового генератора, либо не подпускать атакующего на такое расстояние, где он может знать полное окружение.
К тому же, я сомневаюсь что этот принцип вообще применим в данном случае. Ведь сокрытие ключа шифрования — это не «Security through obscurity», не так ли? То же самое относится и к внутреннему состоянию ГПСЧ.
Если я правильно понял автора статьи, то этой фразой он хотел сказать: «ответ сервера, который содержит HTTP-заголовок с именем реплики сервиса — это зло».

Хотя, как мне кажется, это вполне нормальная информация, которая может быть частью Public-api, и использоваться например для клиентской балансировки.
Да, под раскрытием информации я имел в виду имя реплики. Действительно спорный совет, безопасность зачастую противоречит удобству (отладки в данном случае). И всё же эта информация помогла в данном сценарии атаки. Другой пример раскрытия информаци — стэктрейсы, тоже помогают в отладке, но показывать их потенциальному злоумышленнику не кажется хорошей идеей.

Секундочу, но ведь security through obscurity — не про то! Принцип не говорит, что нужно дать атакующему всю информацию об устройстве [криптографической] системы, кроме условного «закрытого ключа». Он говорит, что не нужно полагаться на неосведомлённость атакующего как на главную защитную меру. А как на дополнительную — конечно же, можно и нужно полагаться.


Управление рисками, в том числе безопасности, подчиняется экономическим законам. Если затраты на атаку превышают выгоду от неё, нарушитель трижды подумает. Особенно если за ним не стоит большое государство или корпорация с, условно, неограниченными ресурсами. Именно поэтому раскрытие лишней информации о системе вредно — снижаются затраты на атаку, возрастает её вероятность.


Но что же я такое говорю! Конечно же, нужно передавать в HTTP-заголовках все названия и версии используемых языков программирования, фреймворков, middleware и операционных систем, сделать доступной из браузера папку .git, закоммитить node_modules, опубликовать Dockerfile-ы, а лучше сразу выкладывать списки уязвимостей с cve.mitre.org, которые ещё не закрыты, чтобы атакующие не подумали, что от них что-то скрывают (:

А как на дополнительную — конечно же, можно и нужно полагаться.


1)
Меня, если честно, очень эта фраза цепляет. Давайте рассмотрим криптографическую систему с закрытым ключом. Эта система устойчива до тех пор, пока:
1) Не доказано, что P == NPen.wikipedia.org/wiki/P_versus_NP_problem
2) Закрытый ключ не скомпрометирован.
3) Вычислительные мощности находятся на примерно таком же уровне. Например, никто не построил себе кластер из миллиарда машин, либо квантовые компьютеры не стали стабильными, или ещё что-то.

Вы гововрите: «можно скрывать информацию о системе, для того чтобы использовать это, как дополнительный уровень зищиты». Я не очень понимаю, что бы это могло значить. Ваша система уже секьюрна, пока выполняются эти 3 условия. Либо мы пытаемся играть в: «наша система не секьюрна, но сломать её экономически дорого, так как ни у кого нет доступа до кода из вне, и т.д.».

2)
Честно, я немного не понял про версии софта в заголовках, и публичные Dockerfile.

a) Если ваш сервис работает на Некотором Софте, и в этой версии Софта нашли уязвимость, ваша система может быть взломана, вопрос лишь времени (ну да, если наша позиция — не сделать секьюрную систему, а сделать дорого взламываемую систему — это ок).

b) Что секретного в ваших Dockerfile? Вы храните там Secrets? Ну, в этом случае вы не следуете общепринятым практикам, и вина на вас. Это как хранить пароли в базке.
Мы разошлись во мнениях потому, что говорим о разных «системах». Про security through obscurity уместно вспоминать в разговоре об абстрактных моделях (в частности, криптографических систем).

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

Поэтому писать на сайте компании «наш сисадмин Вася живёт по такому-то адресу, у него больные родители, заурядная зарплата и грабительский процент по ипотеке» — можно, но неосмотрительно. Это упростит реализацию атаки по некоторым из возможных векторов.
UFO just landed and posted this here
Sign up to leave a comment.