Как ограничить setAccessible только "законным" использованием?
Чем больше я узнал о силе 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!"
}
}
Я единственный, кто думает, что это ОГРОМНОЕ беспокойство?
3 ответа
НУЖНО ЛИ БЫТЬ ОБ ЭТОМ БЕСПОКОЙНЫМ
Это полностью зависит от того, какие программы вы пишете и для какой архитектуры.
Если вы распространяете программный компонент под названием foo.jar среди людей всего мира, вы в любом случае полностью зависите от него. Они могут изменить определения классов внутри вашего.jar (путем обратного инжиниринга или прямого манипулирования байт-кодом). Они могут запускать ваш код в своей собственной JVM и т. Д. В этом случае беспокойство не принесет вам пользы.
Если вы пишете веб-приложение, которое взаимодействует только с людьми и системами через HTTP, и вы управляете сервером приложений, это тоже не проблема. Конечно, коллеги-программисты в вашей компании могут создавать код, который нарушает ваш шаблон синглтона, но только если они действительно этого хотят.
Если ваша будущая работа - написание кода в Sun Microsystems/Oracle, и вам поручено написание кода для ядра Java или других доверенных компонентов, это то, о чем вы должны знать. Беспокойство, однако, просто заставит вас потерять волосы. В любом случае они, вероятно, заставят вас прочитать Руководство по безопасному кодированию вместе с внутренней документацией.
Если вы собираетесь писать Java-апплеты, вам следует знать о структуре безопасности. Вы обнаружите, что неподписанные апплеты, пытающиеся вызвать setAccessible, просто приведут к исключению SecurityException.
setAccessible - не единственное, что обходит обычные проверки целостности. Существует не-API, базовый Java-класс под названием sun.misc.Unsafe, который может делать практически все, что захочет, включая прямой доступ к памяти. Нативный код (JNI) также может обойти этот вид контроля.
В изолированной среде (например, Java Applets, JavaFX) каждый класс имеет набор разрешений и доступ к Unsafe, setAccessible и определения собственных реализаций, которые контролируются SecurityManager.
"Модификаторы доступа Java не предназначены для обеспечения безопасности".
Это очень сильно зависит от того, где выполняется Java-код. Базовые классы Java используют модификаторы доступа в качестве механизма безопасности для обеспечения песочницы.
Каково действительно законное использование для setAccessible?
Базовые классы Java используют его как простой способ доступа к вещам, которые должны оставаться закрытыми по соображениям безопасности. Например, инфраструктура Java Serialization использует ее для вызова конструкторов частных объектов при десериализации объектов. Кто-то упомянул 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? ... Или я во власти того, кто управляет конфигурацией?
Вы не можете, и вы, безусловно, есть.
- Каково действительно законное использование для
setAccessible
?
Модульное тестирование, внутреннее устройство JVM (например, внедрение System.setError(...)
) и так далее.
- Может ли Java быть спроектирована так, чтобы НЕ иметь такую потребность?
- Каковы будут отрицательные последствия (если таковые имеются) такого дизайна?
Многое было бы неосуществимо. Например, различные внедрения персистентности, сериализации и зависимости Java зависят от рефлексии. И почти все, что зависит от соглашений JavaBeans во время выполнения.
- Вы можете ограничить
setAccessible
только для законного использования?- Это только через
SecurityManager
?
Да.
- Как это работает? Белый / черный список, гранулярность и т. Д.?
Это зависит от разрешения, но я считаю, что разрешение на использование setAccessible
является двоичным Если вам нужна гранулярность, вам нужно использовать другой загрузчик классов с другим менеджером безопасности для классов, которые вы хотите ограничить. Я думаю, вы могли бы реализовать собственный менеджер безопасности, который реализует более тонкую логику.
- Это часто приходится настраивать в своих приложениях?
Нет.
- Могу ли я написать свои классы, чтобы быть
setAccessible
-защищенный независимо отSecurityManager
конфигурация?
- Или я во власти того, кто управляет конфигурацией?
Нет, ты не можешь, и да, ты.
Другая альтернатива состоит в том, чтобы "обеспечить" это с помощью инструментов анализа исходного кода; например, на заказ pmd
или же findbugs
правила. Или выборочный просмотр кода, обозначенного (скажем) grep setAccessible ...
,
В ответ на продолжение
Ни у одного из моих классов нет никакого подобия принудительной неприкосновенности частной жизни вообще. Паттерн синглтона (оставляя сомнения в его достоинствах) теперь невозможно применить.
Если это вас беспокоит, то я полагаю, вам нужно беспокоиться. Но на самом деле вы не должны пытаться заставить других программистов уважать ваши дизайнерские решения. Если они достаточно глупы, чтобы создать несколько экземпляров ваших синглетонов (например), они могут жить с последствиями.
И если вы подразумеваете "конфиденциальность", чтобы охватить смысл защиты конфиденциальной информации от разглашения, вы также ошибаетесь в дереве. Способ защиты конфиденциальных данных состоит в том, чтобы не допустить ненадежного кода в изолированную программную среду безопасности, которая работает с конфиденциальными данными. Модификаторы доступа Java не предназначены для обеспечения безопасности.
<Строковый пример> - Я единственный, кто считает, что это ОГРОМНОЕ беспокойство?
Наверное, не единственный:-). Но ИМО, это не проблема. Принято считать, что недоверенный код должен выполняться в песочнице. Если вы доверяете коду / доверяете программисту, делающему подобные вещи, тогда ваши проблемы хуже, чем наличие неожиданно изменяемых строк. (Вспомним логические бомбы...)
Отражение действительно ортогонально безопасности в этой перспективе.
Как мы можем ограничить рефлексию?
У Java есть менеджер безопасности и ClassLoader
в качестве основы для его модели безопасности. В вашем случае, я думаю, вам нужно посмотреть на java.lang.reflect.ReflectPermission
,
Но это не полностью решает проблему отражения. Доступные отражающие возможности должны быть предметом детальной схемы авторизации, что не имеет место в настоящее время. Например, чтобы позволить определенной платформе использовать отражение (например, Hibernate), но не остальную часть вашего кода. Или позволить программе отражать только в режиме только для чтения, для целей отладки.
Одним из подходов, который может стать мейнстримом в будущем, является использование так называемых зеркал для отделения отражательных возможностей от классов. См. Зеркала: Принципы проектирования объектов метауровня. Есть, однако, различные другие исследования, которые занимаются этой проблемой. Но я согласен, что проблема для динамического языка более серьезная, чем для статических.
Должны ли мы беспокоиться о сверхдержаве, которую дает нам отражение? И да и нет.
Да, в том смысле, что платформа Java должна быть защищена Classloader
и менеджер по безопасности. Способность связываться с отражением может рассматриваться как нарушение.
Нет в том смысле, что большинство систем в любом случае не совсем безопасны. Многие классы часто могут быть разделены на подклассы, и вы можете уже злоупотреблять системой именно этим. Конечно, занятия могут быть сделаны final
или запечатаны так, чтобы их нельзя было разделить на подклассы в другой банке. Но только несколько классов защищены (например, строка) в соответствии с этим.
Смотрите этот ответ о выпускном классе для хорошего объяснения. Смотрите также блог Сами Койву, чтобы узнать больше о том, как взломать Java.
Модель безопасности Java может рассматриваться как недостаточная для некоторых целей. В некоторых языках, таких как NewSpeak, применяется еще более радикальный подход к модульности, когда у вас есть доступ только к тому, что вам явно предоставлено путем инверсии зависимостей (по умолчанию ничего).
Также важно отметить, что безопасность в любом случае относительна. На уровне языка, например, вы не можете запретить модульной форме, потребляющей 100% ЦП, или занимать всю память до OutOfMemoryException
, Такие проблемы необходимо решать другими способами. Возможно, мы увидим в будущем Java, расширенный квотами на использование ресурсов, но это не на завтра:)
Я мог бы подробнее остановиться на этом вопросе, но я думаю, что сделал свою точку зрения.