Pull to refresh

Comments 68

Достаточно известная фишка java reflection, странно что вы о ней не знали.
Статья хороша как напоминание, но её можно сделать лучше, если добавить описание класса SecurityManager и рассказать почему подобное поведение не является нарушением java security model.
Хотя, конечно, кто захочет — найдёт всё в гугле.
Java позволит вам выстрелить себе в ногу, если вы произнесёте заклинание «import java.lang.reflect» :)
Не всегда. Нужна ещё «правильная» политика. ;)
Обойти защиту вроде можно было всегда и везде) нельзя все предусмотреть, а иногда абсолютной защите мешает наличие противоречий (т.е. при организации такой защиты нельзя не пожертвовать функциональностью)
/path/to/demo.policy:
grant {
  permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
};


test.A.java:
package test;

public class A {
	static {
		if (System.getSecurityManager() == null)
			System.setSecurityManager(new SecurityManager());
	}
	private int x = 33;

	public void test() {
		System.out.println(x);
	}
}


access.X.java:
package access;

import java.lang.reflect.Field;
import test.A;

public class X {
	public void getAccess() throws Exception {
		A a = new A();
		Field x = a.getClass().getDeclaredField("x");
		x.setAccessible(true);
		x.setInt(a, x.getInt(a) + 1);
		System.out.print("private field: ");
		a.test();
	}
	
	public static void main(String[] str) throws Exception {
		new X().getAccess();
	}
}


> java access.X
Exception in thread «main» java.security.AccessControlException: access denied (java.lang.reflect.ReflectPermission suppressAccessChecks)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:107)
at access.X.getAccess(X.java:10)
at access.X.main(X.java:17)

> java -Djava.security.policy=/path/to/demo.policy access.X
private field: 34
Ппц, сначала какие-то костыли, чтоб разрушить стандарт, потом костыли на костыли, чтоб он всё-таки остался стандартом. Мдя уж.
смотри пред пост iZENfire — это уже костыли на костыли на костыли :)
Посмотрите на это под другим углом, рефлекшон в этом смысле можно воспринимать как работу с кодом — нечто эквивалентное динмаически разобрать classfile, подправить и собрать, и залоадить. Тоесть эта «фича» будет в любом языке с динамической загрузкой.
эмм, что то мне подсказывает, что любой java/.net-разработчик знает(должен знать) что такое рефлекшн и как его (не)использовать. а за вас конечно рад — никогда не поздно узнавать что-то новое :)

По этому примеру — можно запускать jvm с SecurityManager, и без всякого доп. кода она будет стучать по рукам при попытке сделать setAccessible(true), или прописать нормальные security policy для jvm(в большинстве случаев так и будет).
В Java Persistance API доступ к скрытым полям entity-классов активно используется. Это полезно с точки зрения производительности. Чем дёргать каждый раз методы-аксессоры, проще работать с полями напрямую. На сколько я знаю, .NET таких вещей не позволяет.
хм,
FieldInfo fieldInfo = typeof(Test).GetField(«xProperty», BindingFlags.Instance | BindingFlags.NonPublic);
fieldInfo.SetValue(test, 48);
вполне работает(xProperty — private), да и вообще System.Reflection сильно похож на java.lang.reflect
(поправьте если ошибаюсь, с .net по работе не сталкиваюсь)
Действительно работает. Извиняюсь =) Тоже не дотнетчик…
UFO just landed and posted this here
Есть еще один замечательный способ отстрелить себе ногу — класс Unsafe, который позволяет напрямую работать со структурами в памяти.
>РАЗРАБОТЧИК, БДИ!
>При отсутствии SecurityManager на этапе выполнения в твой класс может залезть кто-угодно и изменить что-угодно!
Я надеюсь это не руководство к действию?

У страха глаза велики. Не нужно так защищать свой код без веской на то причины.

* Не всегда наследование хорошо спроектировано. Представьте, что вы переопределяете в своем классе метод родителя, использующий несколько приватных методов, без которых вам при переопределении не обойтись. Варианта два: или дублировать код (если есть исходники, а есть они не всегда), или использовать рефлекшн. Допустим, исходников нет, а предусмотрительный автор кода включил СекьюритиМенеджер. Ваши пожелания автору? :)

* Волшебные фреймворк Seam активно использует рефлекшн для реализации своей dependency bijection (если у поля есть аксессоры, то используются они, иначе — рефлекшн). Что будет с вашим Seam-приложением в после включения СекьюритиМенеджера?

Мораль: подумайте дважды, прежде чем бдеть
Включение SecurityManager'а напоминает всякие методы мега-защиты дельфовских программ, которые применяют 14-летние школьники — «А вдруг кто-то сможет влезть в мой код?!!».
Мда, слава богу, я не программирую на Яве) Всегда не любил этот язык, и как пользователь, и как программист, выясняется — не зря. Нафик в якобы правильном ООП языке такие извраты — не понять.
Вам — «не понять», а другим оно нужно. В PHP который у вас написан в профиле это тоже есть — ru2.php.net/oop5.reflection
Блин, мне стыдно (( Я догадываюсь, чот он полезн для отладки? Полез в Гугл искать «зачем нужен рефлекшн»)
Не только для отладки. А вообще для метапрограммирования, для генерации кода. Посмотрите на API ActiveRecord, DataMapper — отличные примеры грамотной (в меру :-) генерации кода.
Мдааа, выдержит ли у автора сердце если он откроет для себя манипулирование байт-кдом в рантайме?
я его время от времени тренирую на подобных вот примерах :)
не, ну это можно было ещё на Ассемблере делать
Известная фича. Очень часто используется при написании тесткейсов. Проблем, если честно, никаких не вижу, так как если в рантайме только твой код, то ничего страшного, а если подключается чужой, то самоубийц не включающих SecurityManager я не видел:)
Инкапсуляция это прежде всего сокрытие а не защита.

Инкапсуляция позволяет правильно работать с объектом, если же хочется надругаться над классом — reflaction в помощь, но при этом никто не гарантирует что вы получите правильный результат, кроме того, класс вовсе не обязан обрабатывать такие ситуации
Именно так. Программисту дают возможность сделать это, а воспользуется ли он ей — это его дело. Понятно, что перед использованием рефлекшена для такой цели нужно несколько раз подумать и быть готовым к возможным багам. «Правильный» язык должен дать возможность сделать все, что угодно, но при этом при написании кода нужно понимать, что делаешь. К сожалению, пока что никто не придумал язык, на котором мог бы успешно писать совершенно безмозглый человек. А если новичек решил все же залезть рефлекшеном в приватные поля, то нужно ему обьяснить, почему так делать плохо и разобратся, а вдруг использование этого инструмента в даном случае действительно оправдано?
Наконец-то кто-то обратил на это внимание!
Абсолютно верно!
Подскажите, а так скрывать поля нужно по соображениям заповедей или практической необходимости? Хакиры меняют вам значения констант?
Скрывать надо чтоб у недопрограмеров не было соблазна обращаться к полям объектов напрямую, проще скрыть чем объяснять каждому все паттерны программирования.
т.е. недопрограммеры так и норовят загрузить util.reflect и залезть в private-поля?
Скорее да, чем если бы они хорошенько изучили предоставленный разработчиком «закрытого» класса интерфейс и воспользовались его функциональностью правильно.
Иными словами, вы относитесь к своим программерам почти как к внешним хакерам, а не к товарищам по команде? Жестко у вас там.
Подождите, вы ведь сейчас говорите об инкапсуляции? При чем тут товарищи или хакеры? Инкапсуляция — это один из базисов ООП. Это подход общий для всех. Его сила — в том, чтобы ВСЕ без исключения его соблюдали (и дело совершенно не в товарищеских настроениях или личных мотивах). Представте, что будет, если отметить инкапсуляцию «Вась, а сделай лично для меня вот в этом классе такую-то переменную — мне так легче, а выводи все свои результаты вот в таком виде — так легче будет Костяну. А для Ирочки заведи еще переменную i_love_you ..» Детский сад не устраивайте. Разработка — это разработка, и к личным предпочтениям дела не имеет.
Я имею в виду, что предлагается писать System.setSecurityManager(new SecurityManager()) чтобы не дать Васе завести свою переменную через java.util.reflect. Возникает вопрос: почему бы Васю не уволить, или научить, что делать a.getClass().getDeclaredField(«x») не следует. Или, что лучше, поинтересоваться, по какой такой причине ему нужно залезть в класс именно так. Мож, он баг какой так хитро патчит.
К инкапсуляции и ооп мое любопытство не имеет прямого отношения.

>>Подскажите, а так скрывать поля нужно по соображениям заповедей или практической необходимости?<<
— из соображений инкапсуляции.

>>Я имею в виду, что предлагается писать System.setSecurityManager(new SecurityManager()) <<
— не предлагается, а напоминается, что есть такая весчь и это нужно иметь в виду.

спор ниочем.
Тут нет спора. Товарищ мне ответил на «т.е. недопрограммеры так и норовят загрузить util.reflect и залезть в private-поля?» тем, что да, норовят. Мне становится интересно, где же водятся такие страшные программеры. Соображения инкапсуляции тут ни при чем. И спора тоже нет. Раз есть суровые челябинские программеры, нужно защищаться, конечно. Просто это как-то сюрреалистично смотрится.

У меня нет команды. Но направление мысли правильное — инкапсуляция подразумевает приемлемую для всех защиту кода.

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

Язык предоставляет право выбора механизма и политики, а так же право смешивать их.
>> более защищённый код, обусловленный политикой компании <<

Вот это мне и интересно. Чем руководствуются авторы политики защиты private-переменных от инсайдеров. Наличием злобных инсайдеров?

>> Язык предоставляет право выбора механизма и политики, а так же право смешивать их. <<

разумно
Уважаемое хабрачмо, хватит тут минусовать коллегу! Если идет дискуссия, есть два мнения. А минусовать нужно за спам и глупости. Опускание оппонента в _дискуссии_ не идет на пользу никому.
как эта «новость» попала на главную? о_О
Это азбука. Стыдно вообще то такое писать. Разработчик БДИ… БДИ… Прежде чем защищаться, надо сначала научиться писать то, что стоит защищать.
Сколько вам открытий чудных предстоит. Есть еще класс Unsafe, он и не такое позволяет.
Ваш пример гораздо уязвимей при использования ординарного наследованния. Т.е. гораздо вероятнее сценарий предопределения test(). Мне кажется, до того как защищаться от умышленных и маловероятных на практике атак через рефелкшин, стоит принять элементарные меры, типа запрещение расширения класса и final на неизменяемые переменные.
какие атаки, при чем тут защищаться? Я говорю об подходе к программированию (который в народе именуется ООП) и одним из его «китов» — инкапсуляция, и о том, что надеятся на инкапсуляцию без дополнительных телодвижений не стоит.
этот кит уязвим по определению. Без всяких технических изысков, без рефлешена. Надеятся на мистику не надо, но надо понимать ограничения которые накладывает парадигма.

Что касается атаки, то это такой термин, часто применяющийся в литературе при описании подбных вещей. К хакерам он отншение не имеет.
Reflection — очень полезный инструмент. Используется во многих библиотеках, связанных с обработкой метаданных (Hibernate например использует рефлекш по полной). Но важно понимать, что это всего лишь инструмент. Когда вы используете этот инструмент — вы должны представлять возможные последствия (вроде нарушения инкапсуляции). Сетовать на то, что инструмент де плох — так это уже проблема «плохого танцора»: вы с тем же успехом можете «выстрелить себе в ногу» и говорить, что пистолет вам дали плохой.

Что касается «защиты» с помощью SecurityManager — то это глупость. С одной стороны, масса библиотек просто перестанет работать, а с другой стороны, если кому-то надо будет залезть в каш класс, то он все равно залезеть, хотя бы путём подмены байткода, включающего SecurityManager.
* рефлекшн
* все равно сможет залезть
Кажется при проектировании Java разработчики стремились создать как можно более простой и надежный язык… это выкинем, это скроем… будет меньше ошибок. Мои знакомые программисты на Java говорят, что программы на C++ подвержены ошибкам и вообще C++ очень сложный. А я всегда говорил:
Сетовать на то, что инструмент де плох — так это уже проблема «плохого танцора»: вы с тем же успехом можете «выстрелить себе в ногу» и говорить, что пистолет вам дали плохой.

:)
Боюсь склонен согласиться с вашими знакомыми. C++ не то чтобы сложен — просто есть набор дополнительных (по сравнению с Java) нюансов, которые всё время нужно держать в голове. С другой стороны в Java есть эти самые отражения и несколько своих нюансов. Но их-то обычно как раз-таки не нужно учитывать.

Рефлекшн не нарушает инкапсуляция в том смысле, что они не нарушают сокрытие реализации. Автору не нравится то, что рефлекшн позволяет получить доступ к данным в private-поле. Но ведь и в том же C++ при желании можно через указатель на объект добраться до приватных полей объекта. Часто это учитывают? Да почти никогда, за исключением кода, предполагающего обфускацию (проверялки леценций, активации и т.п.).

Имхо, автор предлагает решение для несуществующей проблемы.
давайте не будем устраивать холиваров. Есть С++ и есть Java — и они разные и предусмотрены для разных вещей.
это вы ещё не программировали на ABAP :)
там загрузить в память нужную чужую программу, изменить в ней приватные поля, сгенерировать кусок кода, выполнить, извлечь данные из приватных переменных, писать данные в таблицы доступные только для чтения и т.п. — в порядке вещей :)
Будтье осторожны! Розетка — очень опасное устройство! Раньше я тоже думал, что она безопасна. Но вчера я сделал следующее. Взял отвертку и аккуратно открытил крышку, чтобы залезть внутрь. Потом лизнул ро… Бац! Помните! Розетка — очень опасное устройство!
отличное сравнение! :-)
Поясню свой минус за статью. Расставлять проверки там, где проверять не надо — это антипаттерн, это замусоривание кода. Не стоит воспринимать других разработчиков, как людей, которые хотят встроить экcплоит в разработку соседа — так коллективной разработки не получится…
Отличное сравнение с розетками! Но тут вы не правы. Я не агеритую пользоваться джава секюрити, а просто напоминаю, что такая возможность есть, т.к. многие программеры просто это не учитывают, а еще больше прогеров вообще об этом не знают (как я например до недавнего момента — знал про розетку, но что там такое напряжение — не знал )). Так что "Розетка — очень опасное устройство!" :)
>Поэтому у нас есть все основания считатать, что метод test() возвращает всегда число 33. Более того, мы бы
>могли использовать это предположение в своем коде, завязав на этом какую-либо логику.

Истинное нарушение инкапсуляции — в коде, вызывающем метод test предполагать, что он вернет 33.
/* считал, что java — это 100% ОО язык */

Смешная шутка, про 100%

/* Единственное, для чего я написал этот пост — предупредить всех явистов */

И, таки, вы в это сами верите? :-) Вот имхо полезное упражнение: найти «не менее пяти причин почему я опубликовался на Хабре».
До тех пор, пока в яве сушествуют атомарные типы данных, 100% ОО языком его щитать нельзя. Только вот возникает вопрос: настолько ли это плохо, как думает автор?
На это можно взглянуть и с другой точки зрения — не все что написано в программе для машины, скорее это для другого человека. Если ты увидел private, значит не надо завязываться на тип или имя этого поля или вообще учитывать его существование — и твоя программа будет устойчива. А компилятор тебе в этом поможет. Ну а если уж решил таки что-то с этим полем сделать — вот тебе способ.
Давайте всё же различать этап компиляции от этапа исполнения. Традиционно модификаторы доступа пришли от языка C++, в котором они существуют только «на листе» (в бинарном виде нет отличия). Инкапсуляция это свойство языка программирования (а не виртуальной машины) синтаксис которого проверяет компилятор. Поэтому полагаю, что свойство инкапсуляции языка Java данный пример не нарушает.
Именно так! Инкапсуляция вобще должна упростить жизнь при разроботке. И в рантайме это слово роли не играет.
ппц… при чем тут инкапсуляция?..
т.е.? вы хотите поломать еще и константы?
Если я конечно не ошибаюсь, то решлекшн позволяет и модификаторы полей менять.
Sign up to leave a comment.

Articles