Чем больше я узнавал о силе java.lang.reflect.AccessibleObject.setAccessible
, тем больше удивлялся тому, на что она способна. Это адаптировано из моего ответа на вопрос ( Использование отражения для изменения статического финального File.separatorChar для модульного тестирования ).
import java.lang.reflect.*;
public class EverythingIsTrue {
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String args[]) throws Exception {
setFinalStatic(Boolean.class.getField("FALSE"), true);
System.out.format("Everything is %s", false); // "Everything is true"
}
}
Вы можете делать поистине невероятные вещи:
public class UltimateAnswerToEverything {
static Integer[] ultimateAnswer() {
Integer[] ret = new Integer[256];
java.util.Arrays.fill(ret, 42);
return ret;
}
public static void main(String args[]) throws Exception {
EverythingIsTrue.setFinalStatic(
Class.forName("java.lang.Integer$IntegerCache")
.getDeclaredField("cache"),
ultimateAnswer()
);
System.out.format("6 * 9 = %d", 6 * 9); // "6 * 9 = 42"
}
}
Предположительно, разработчики API понимают, насколько злоумышленниками setAccessible
могут быть злоупотребления , но, должно быть, признали, что он имеет законное использование для его предоставления. Итак, мои вопросы:
- Для чего действительно законное использование
setAccessible
?- Может ли Java быть спроектирована так, чтобы НЕ иметь этой потребности?
- Каковы будут отрицательные последствия (если таковые имеются) такого дизайна?
- Можно ли ограничиться
setAccessible
только законным использованием?- Только через
SecurityManager
?- Как это работает? Белый / черный список, детализация и т. Д.?
- Часто ли приходится настраивать его в ваших приложениях?
- Могу ли я написать свои классы
setAccessible
защищенными независимо отSecurityManager
конфигурации?- Или я во власти того, кто управляет конфигурацией?
- Только через
Думаю, еще один важный вопрос: НУЖНО ли мне беспокоиться об этом ???
Ни на одном из моих классов нет никакого подобия принудительной приватности. Одноэлементный паттерн (отбрасывая сомнения в его достоинствах) теперь невозможно применить. Как показывают мои фрагменты выше, даже некоторые базовые предположения о том, как работают основы Java, даже близко не гарантированы.
ЭТИ ПРОБЛЕМЫ НЕ РЕАЛЬНЫЕ ???
Хорошо, я только что подтвердил: спасибо setAccessible
, строки Java НЕ неизменяемы.
import java.lang.reflect.*;
public class MutableStrings {
static void mutate(String s) throws Exception {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
value.set(s, s.toUpperCase().toCharArray());
}
public static void main(String args[]) throws Exception {
final String s = "Hello world!";
System.out.println(s); // "Hello world!"
mutate(s);
System.out.println(s); // "HELLO WORLD!"
}
}
Я единственный, кто думает, что это ОГРОМНАЯ проблема?
источник
Ответы:
НУЖНО ли мне беспокоиться об этом ???
Это полностью зависит от того, какие типы программ вы пишете и для какой архитектуры.
Если вы распространяете программный компонент под названием foo.jar среди людей во всем мире, вы в любом случае полностью в их власти. Они могут изменять определения классов внутри вашего .jar (посредством обратного проектирования или прямого манипулирования байт-кодом). Они могут запускать ваш код в своей собственной JVM и т. Д. В этом случае беспокойство не принесет вам пользы.
Если вы пишете веб-приложение, которое взаимодействует с людьми и системами только через HTTP, и вы управляете сервером приложений, это тоже не проблема. Конечно, коллеги-кодеры в вашей компании могут создать код, который нарушает ваш шаблон синглтона, но только если они действительно этого хотят.
Если ваша будущая работа заключается в написании кода в Sun Microsystems / Oracle и вам поручено написать код для ядра Java или других доверенных компонентов, вам следует об этом знать. Однако беспокойство только заставит вас потерять волосы. В любом случае они, вероятно, заставят вас прочитать Руководство по безопасному кодированию вместе с внутренней документацией.
Если вы собираетесь писать Java-апплеты, вам следует знать о структуре безопасности. Вы обнаружите, что неподписанные апплеты, пытающиеся вызвать setAccessible, просто приводят к исключению SecurityException.
setAccessible - не единственное, что обходит обычные проверки целостности. Существует не-API, базовый класс Java под названием sun.misc.Unsafe, который может делать практически все, что угодно, включая прямой доступ к памяти. Нативный код (JNI) также может обойти этот вид контроля.
В изолированной среде (например, Java-апплеты, JavaFX) каждый класс имеет набор разрешений, а доступ к Unsafe, setAccessible и определению собственных реализаций контролируется SecurityManager.
«Модификаторы доступа Java не предназначены для использования в качестве механизма безопасности».
Это очень сильно зависит от того, где выполняется код Java. Базовые классы Java действительно используют модификаторы доступа в качестве механизма безопасности для принудительного применения песочницы.
Каковы действительно законные способы использования setAccessible?
Базовые классы Java используют его как простой способ доступа к материалам, которые должны оставаться закрытыми по соображениям безопасности. Например, среда сериализации Java использует его для вызова конструкторов частных объектов при десериализации объектов. Кто-то упомянул System.setErr, и это был бы хороший пример, но любопытно, что все методы класса System setOut / setErr / setIn используют собственный код для установки значения последнего поля.
Еще одно очевидное законное использование - это фреймворки (постоянство, веб-фреймворки, внедрение), которые должны заглядывать внутрь объектов.
На мой взгляд, отладчики не попадают в эту категорию, поскольку они обычно не работают в одном и том же процессе JVM, а вместо этого работают в интерфейсе с JVM с использованием других средств (JPDA).
Может ли Java быть спроектирована так, чтобы НЕ иметь этой потребности?
Это довольно глубокий вопрос, чтобы хорошо ответить. Думаю, да, но вам нужно добавить какой-то другой механизм (ы), который может быть не таким уж предпочтительным.
Можно ли ограничить setAccessible только законным использованием?
Самое прямое ограничение OOTB, которое вы можете применить, - это иметь SecurityManager и разрешить setAccessible только коду, поступающему из определенных источников. Это то, что уже делает Java - стандартные классы Java, поступающие из вашего JAVA_HOME, могут выполнять setAccessible, а неподписанные классы апплетов из foo.com не могут выполнять setAccessible. Как было сказано ранее, это разрешение является двоичным, в том смысле, что оно либо есть, либо нет. Нет очевидного способа разрешить setAccessible изменять определенные поля / методы, запрещая другие. Однако с помощью SecurityManager вы можете запретить классам полностью ссылаться на определенные пакеты, с отражением или без него.
Могу ли я записать свои классы в режим setAccessible-proof независимо от конфигурации SecurityManager? ... Или я во власти того, кто управляет конфигурацией?
Вы не можете, и вы, безусловно, делаете.
источник
Модульное тестирование, внутреннее устройство JVM (например, реализация
System.setError(...)
) и так далее.Многое было бы невозможно реализовать. Например, различные инъекции сохраняемости, сериализации и зависимостей Java зависят от отражения. И почти все, что полагается на соглашения JavaBeans во время выполнения.
Да.
Это зависит от разрешения, но я считаю, что разрешение на использование
setAccessible
двоичное. Если вам нужна детализация, вам нужно либо использовать другой загрузчик классов с другим менеджером безопасности для классов, которые вы хотите ограничить. Думаю, вы могли бы реализовать собственный менеджер безопасности, реализующий более тонкую логику.Нет.
Нет, вы не можете, и да, это так.
Другой альтернативой является «принудительное применение» этого с помощью инструментов анализа исходного кода; например обычай
pmd
илиfindbugs
правила. Или выборочная проверка кода кода, идентифицированного (скажем)grep setAccessible ...
.В ответ на продолжение
Если вас это беспокоит, то, полагаю, вам стоит волноваться. Но на самом деле вам не следует пытаться заставить других программистов уважать ваши дизайнерские решения. Если люди достаточно глупы, чтобы использовать отражение для бесплатного создания нескольких экземпляров ваших синглтонов (например), они могут жить с последствиями.
С другой стороны, если вы имеете в виду «конфиденциальность», чтобы охватить значение защиты конфиденциальной информации от разглашения, вы ложитесь не на то дерево. Способ защиты конфиденциальных данных в приложении Java - не допускать попадания ненадежного кода в изолированную программную среду безопасности, которая имеет дело с конфиденциальными данными. Модификаторы доступа Java не предназначены для использования в качестве механизма безопасности.
Наверное, не единственный :-). Но, ИМО, это не проблема. Принято считать, что ненадежный код должен выполняться в песочнице. Если у вас есть доверенный код / доверенный программист, делающий такие вещи, то ваши проблемы хуже, чем неожиданно изменяемые строки. (Представьте себе логические бомбы, утечку данных через скрытые каналы и т. Д.)
Есть способы справиться (или смягчить) проблему «плохого актера» в вашей группе разработки или эксплуатации. Но они дорогостоящие и ограничительные ... и излишни для большинства случаев использования.
источник
SecurityManager
для этого, потому что все, что вы получаете, - это проверка прав доступа - все, что вы действительно можете сделать, это посмотреть на acc и, возможно, пройтись по стеку, проверить текущий поток).grep java.lang.reflect.
С этой точки зрения отражение действительно ортогонально безопасности / защищенности.
Как мы можем ограничить размышления?
Java имеет диспетчер безопасности и
ClassLoader
является основой модели безопасности. В вашем случае, думаю, вам нужно посмотретьjava.lang.reflect.ReflectPermission
.Но это не решает полностью проблему отражения. Доступные отражающие возможности должны подлежать детальной схеме авторизации, чего сейчас нет. Например, чтобы разрешить определенной структуре использовать отражение (например, Hibernate), но не использовать остальную часть вашего кода. Или разрешить программе отражать только в режиме «только чтение» в целях отладки.
Один из подходов, который может стать мейнстримом в будущем, - это использование так называемых зеркал для отделения отражающих способностей от классов. См. Зеркала: Принципы проектирования для объектов метауровня . Однако существуют различные другие исследования , посвященные этой проблеме. Но я согласен с тем, что проблема более серьезна для динамического языка, чем для статических языков.
Следует ли нам беспокоиться о сверхспособности, которую дает нам отражение?Да и нет.
Да, в том смысле, что платформа Java должна быть защищена
Classloader
менеджером безопасности. Способность вмешиваться в размышления может рассматриваться как нарушение.Нет в том смысле, что большинство систем в любом случае не совсем безопасны. Многие классы часто могут быть разделены на подклассы, и вы уже потенциально можете злоупотреблять системой только этим. Конечно, классы могут быть созданы
final
или запечатаны, чтобы их нельзя было разделить на подклассы в другом jar-файле. Но в соответствии с этим правильно защищены только несколько классов (например, String).Посмотри это ответ о последнем классе для хорошего объяснения. Смотрите также блог Сами Койву чтобы больше о java-хакерских атаках.
Модель безопасности Java в некотором смысле может считаться недостаточной. Некоторые языки, такие как NewSpeak, используют еще более радикальный подход к модульности, когда у вас есть доступ только к тому, что явно дается вам посредством инверсии зависимостей (по умолчанию ничего).
Также важно отметить, что безопасность в любом случае относительна . На уровне языка вы можете, например, не препятствовать тому, чтобы форма модуля потребляла 100% ЦП или всю память до
OutOfMemoryException
. Такие опасения необходимо решать другими способами. Возможно, в будущем мы увидим расширение Java за счет квот на использование ресурсов, но это не на завтра :)Я мог бы подробнее остановиться на этом предмете, но я думаю, что высказал свою точку зрения.
источник