Чи можна змінити значення String через рефлексію?
Технічно — так, через Reflection API можна змінити внутрішній вміст рядка, але це дуже погана практика.
Junior Level
Технічно — так, через Reflection API можна змінити внутрішній вміст рядка, але це дуже погана практика.
String s = "Hello";
// Отримуємо доступ до приватного поля value
// В Java 8 і раніше поле було char[] value. Починаючи з Java 9 (Compact Strings) — byte[] value.
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
byte[] value = (byte[]) field.get(s);
value[0] = 'J'; // Змінюємо 'H' на 'J'
System.out.println(s); // Виведе "Jello"
Чому це погано:
- Зміниться рядок скрізь, де використовується
"Hello"(пул рядків) - Зламається
hashCode— рядок загубиться вHashMap - Це порушення безпеки JVM
⚠️ УВАГА: Цей прийом НЕМОЖЛИВО використовувати в production-коді. Це порушення безпеки, що ламає JVM. Тільки для освітніх цілей.
Middle Level
Наслідки
-
Поломка String Pool — рядок зміниться в усіх місцях, де використовується цей літерал (логи, конфіги, імена класів)
-
Поломка HashMap —
StringкешуєhashCode. Після зміни масивуvalue, вміст рядка зміниться, а хеш залишиться старим → об’єкт неможливо знайти вHashMap -
Security Bypass — перевірки безпеки (доступ до файлів, URL) базуються на рядках. Зміна рядка “після перевірки” — класична уразливість
Захист у сучасних JDK
У Java 17+ доступ до внутрішніх полів java.base через рефлексію заборонено модульною системою (Project Jigsaw). Ви отримаєте InaccessibleObjectException.
Обійти можна флагами запуску (--add-opens java.base/java.lang=ALL-UNNAMED), але в production це неможливо.
Senior Level
Повний приклад атаки
String s = "admin";
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
byte[] value = (byte[]) valueField.get(s);
// Змінюємо "admin" на "hacker"
value[0] = 'h'; value[1] = 'a'; value[2] = 'c';
value[3] = 'k'; value[4] = 'e'; value[5] = 'r';
Глибинні наслідки
- ClassLoader pollution — імена класів, завантажувачі, шляхи — все може бути скомпрометовано
- Security Manager — якщо використовується, перевірки можуть бути обійдені
- SerialVersionUID — залежить від імені класу, може бути порушений
Еволюція захисту
- Java 8 і раніше:
setAccessible(true)працював без обмежень - Java 9: модульна система обмежила доступ до
java.base - Java 16+: за замовчуванням
InaccessibleObjectException - Java 17+:
--illegal-access=denyза замовчуванням
Резюме для Senior
- Reflection-атака на
String— приклад того, як низькорівневі маніпуляції руйнують високорівневі гарантії - Сучасні JDK (17+) блокують це на рівні модулів
- В production з коректними флагами це неможливо
- Для Senior це ілюстрація важливості інкапсуляції на всіх рівнях
Шпаргалка для інтерв’ю
Обов’язково знати:
- Технічно можливо через
setAccessible(true)на поліvalue, але це порушення безпеки - Наслідки: поломка String Pool (зміниться скрізь), зламається hashCode (втрата в HashMap), Security Bypass
- Java 9+: модульна система обмежила доступ; Java 16+:
InaccessibleObjectExceptionза замовчуванням - Java 17+:
--illegal-access=deny— reflection до java.base заблоковано - Обійти можна флагами (
--add-opens), але в production це неможливо - Це НЕМОЖЛИВО використовувати в production — тільки для освітніх цілей
Часті уточнювальні запитання:
- Працює в Java 17+? — Ні,
InaccessibleObjectExceptionбез--add-opens - Що зламається, якщо змінити рядок? — Усі використання цього літерала, hashCode в HashMap, Security Manager
- ClassLoader pollution — що це? — Імена класів, завантажувачі, шляхи — все може бути скомпрометовано
- SerialVersionUID залежить від цього? — Так, залежить від імені класу, може бути порушений
Червоні прапорці (НЕ говорити):
- «Це нормальний прийом для production» — це порушення безпеки JVM
- «Reflection працює в будь-якій версії» — в Java 16+ заблоковано для java.base
- «Зміниться тільки цей рядок» — зміняться ВСІ екземпляри цього літерала в пулі
- «Можна додати флаг в production» —
--add-opens java.base/java.lang=ALL-UNNAMED— security risk
Пов’язані теми:
- [[4. Чому клас String є незмінним]]
- [[5. Які наслідки незмінності String]]
- [[18. Як працює String pool і як це пов’язано з незмінністю]]
- [[8. Чи достатньо зробити всі поля final для незмінності]]