SetAccessible'ı yalnızca “meşru” kullanımlarla nasıl sınırlandırılır?


100

Gücü hakkında java.lang.reflect.AccessibleObject.setAccessiblene kadar çok şey öğrenirsem, yapabileceklerine o kadar çok şaşırıyorum. Bu soruya verdiğim yanıttan uyarlanmıştır ( Birim testi için statik nihai File.separatorChar'ı değiştirmek için yansıma kullanma ).

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"
   }
}

Gerçekten acayip şeyler yapabilirsiniz:

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"
   }
}

Muhtemelen API tasarımcıları ne kadar kötüye setAccessiblekullanılabileceğinin farkındadır , ancak bunu sağlamak için meşru kullanımları olduğunu kabul etmiş olmalıdır. Yani sorularım:

  • Gerçekten meşru kullanım alanları setAccessiblenelerdir?
    • Java ilk etapta bu ihtiyaca sahip OLMAYACAK şekilde tasarlanmış olabilir mi?
    • Böyle bir tasarımın (varsa) olumsuz sonuçları ne olur?
  • setAccessibleYalnızca meşru kullanımlarla sınırlayabilir misiniz?
    • Sadece içinden SecurityManagermi?
      • O nasıl çalışır? Beyaz liste / kara liste, ayrıntı düzeyi vb.
      • Uygulamalarınızda yapılandırmak zorunda olmak yaygın mı?
    • Sınıflarımı konfigürasyondan setAccessiblebağımsız olarak yazabilir miyim SecurityManager?
      • Yoksa konfigürasyonu yönetenin insafına mı kaldım?

Sanırım bir önemli soru daha var: BU KONUDA DÜŞÜNMEM GEREKİYOR MU?

Sınıflarımdan hiçbiri, ne olursa olsun, uygulanabilir mahremiyete sahip değil. Singleton modelini (yararları hakkındaki şüpheleri bir kenara bırakarak) uygulamak artık imkansızdır. Yukarıdaki parçacıklarımın gösterdiği gibi, Java temelinin nasıl çalıştığına dair bazı temel varsayımlar bile garanti edilmeye yakın bile değil.

BU SORUNLAR GERÇEK DEĞİL Mİ ???


Tamam, şimdi onayladım: teşekkürler setAccessible, Java dizeleri değişmez DEĞİLDİR .

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!"
   }
}

Bunun BÜYÜK bir endişe olduğunu düşünen tek kişi ben miyim?


30
C ++ 'da neler yapabileceklerini görmelisiniz! (Oh, ve muhtemelen C #.)
Tom Hawtin - tackline

Yanıtlar:


105

BU KONUDA DÜŞÜNMEM GEREKİYOR MU ???

Bu tamamen ne tür programlar yazdığınıza ve ne tür bir mimari için yazdığınıza bağlıdır.

Foo.jar adlı bir yazılım bileşenini dünyadaki insanlara dağıtıyorsanız, yine de tamamen onların insafına kalmışsınızdır. .Jar'ınızın içindeki sınıf tanımlarını değiştirebilirler (tersine mühendislik veya doğrudan bayt kodu manipülasyonu yoluyla). Kodunuzu kendi JVM'lerinde çalıştırabilirler. Bu durumda endişelenmenin size bir faydası olmaz.

Yalnızca HTTP aracılığıyla insanlarla ve sistemlerle arabirim oluşturan bir web uygulaması yazıyorsanız ve uygulama sunucusunu kontrol ediyorsanız, bu da bir sorun değildir. Şirketinizdeki diğer kodlayıcıların tekli kalıbınızı kıran kod oluşturabileceğinden emin olun, ancak bunu gerçekten istiyorlarsa.

Gelecekteki işiniz Sun Microsystems / Oracle'da kod yazmaksa ve Java çekirdeği veya diğer güvenilir bileşenler için kod yazmakla görevlendirildiyseniz, bu bilmeniz gereken bir şeydir. Ancak endişelenmek sadece saçınızı kaybetmenize neden olur. Her durumda, muhtemelen dahili dokümantasyonla birlikte Güvenli Kodlama Kılavuzunu okumanızı sağlayacaklardır .

Java uygulamaları yazacaksanız, güvenlik çerçevesi bilmeniz gereken bir şeydir. SetAccessible'ı çağırmaya çalışan imzasız uygulamaların sadece bir SecurityException ile sonuçlanacağını göreceksiniz.

setAccessible, geleneksel bütünlük kontrollerinden geçen tek şey değildir. Doğrudan belleğe erişim de dahil olmak üzere istediği her şeyi yapabilen, API olmayan, sun.misc.Unsafe adında bir çekirdek Java sınıfı var. Yerel kod (JNI) da bu tür bir kontrolün etrafından dolaşabilir.

Korumalı bir ortamda (örneğin Java Uygulamaları, JavaFX), her sınıfın bir dizi izni vardır ve Güvenli Olmayan, setAccessible'a erişim ve tanımlayıcı yerel uygulamalar SecurityManager tarafından kontrol edilir.

"Java erişim değiştiricilerinin bir güvenlik mekanizması olması amaçlanmamıştır."

Bu büyük ölçüde Java kodunun nerede çalıştırıldığına bağlıdır. Çekirdek Java sınıfları, korumalı alanı zorlamak için bir güvenlik mekanizması olarak erişim değiştiricileri kullanır.

SetAccessible için gerçekten meşru kullanımlar nelerdir?

Java çekirdek sınıfları, güvenlik nedenleriyle özel kalması gereken şeylere erişmenin kolay bir yolu olarak kullanır. Örnek olarak, Java Serileştirme çerçevesi, nesneleri seriyi kaldırırken özel nesne oluşturucularını çağırmak için kullanır. Birisi System.setErr'den bahsetti ve bu iyi bir örnek olurdu, ancak merakla, System sınıfı yöntemleri setOut / setErr / setIn, son alanın değerini ayarlamak için yerel kodu kullanır.

Diğer bir bariz meşru kullanım, nesnelerin içine göz atması gereken çerçevelerdir (kalıcılık, web çerçeveleri, enjeksiyon).

Bence hata ayıklayıcılar, normalde aynı JVM sürecinde çalışmadıkları için bu kategoriye girmezler, bunun yerine JVM ile diğer araçları kullanan arayüz (JPDA).

Java ilk etapta bu ihtiyaca sahip OLMAYACAK şekilde tasarlanmış olabilir mi?

Bu, cevaplanması gereken oldukça derin bir soru. Evet olduğunu hayal ediyorum, ancak o kadar da tercih edilemeyebilecek başka mekanizmalar eklemeniz gerekir.

SetAccessible'ı yalnızca yasal kullanımlarla sınırlayabilir misiniz?

Uygulayabileceğiniz en basit OOTB kısıtlaması, bir SecurityManager'a sahip olmak ve setAccessible'a yalnızca belirli kaynaklardan gelen kod için izin vermektir. Java'nın zaten yaptığı budur - JAVA_HOME'unuzdan gelen standart Java sınıflarının setAccessible yapmasına izin verilirken, foo.com'daki imzasız uygulama sınıflarının setAccessible yapmasına izin verilmez. Daha önce de söylendiği gibi, bu izin, birinin sahip olup olmaması anlamında ikilidir. SetAccessible'ın belirli alanları / yöntemleri değiştirmesine izin verirken diğerlerine izin vermenin açık bir yolu yoktur. Bununla birlikte, SecurityManager'ı kullanarak, sınıfların belirli paketlere yansıma olsun veya olmasın tamamen başvurmasına izin vermeyebilirsiniz.

SecurityManager yapılandırmasından bağımsız olarak sınıflarımı setAccessible-proof olacak şekilde yazabilir miyim? ... Yoksa yapılandırmayı yönetenin insafına mı kaldım?

Yapamazsın ve kesinlikle öylesin.


"Foo.jar adlı bir yazılım bileşenini dünyadaki insanlara dağıtıyorsanız, yine de tamamen onların insafına kalırsınız. .Jar'ınızın içindeki sınıf tanımlarını değiştirebilirler (tersine mühendislik veya doğrudan bayt kodu manipülasyonu yoluyla). kodunuzu kendi JVM'lerinde çalıştırabilir, vb. Bu durumda endişelenmenin size bir faydası olmaz. " Hala durum bu mu? Yazılım kurcalamayı zorlaştırma konusunda herhangi bir tavsiye var mı (Umarım önemli ölçüde)? Bundan dolayı java masaüstü uygulamalarını pencereden atmak zordur.
Sero

@naaz Hiçbir şeyin değişmediğini söylemeliyim. Sadece mimari size karşı çalışıyor. Makinemde çalışan ve üzerinde tam kontrole sahip olduğum herhangi bir yazılım parçasını değiştirebilirim. En güçlü caydırıcı yasal bir caydırıcı olabilir, ancak bunun tüm senaryolarda işe yarayacağını sanmıyorum. Bence Java ikili dosyaları tersine çevirmesi en kolay olanlardan biri, ancak deneyime sahip insanlar her şeyi değiştirecek. Gizleme, büyük değişiklikler yapmayı zorlaştırarak yardımcı olur, ancak mantıksal herhangi bir şey (telif hakkı kontrolü gibi) yine de atlamak için önemsizdir. Bunun yerine önemli parçaları bir sunucuya koymayı deneyebilirsiniz.
Sami Koivu

Denuvo'ya ne dersin?
Sero

15
  • Gerçekten meşru kullanım alanları setAccessiblenelerdir?

Birim testi, JVM'nin dahili bileşenleri (örn. Uygulama System.setError(...)) vb.

  • Java ilk etapta bu ihtiyaca sahip OLMAYACAK şekilde tasarlanmış olabilir mi?
  • Böyle bir tasarımın (varsa) olumsuz sonuçları ne olur?

Pek çok şey uygulanamaz. Örneğin, çeşitli Java kalıcılığı, serileştirme ve bağımlılık enjeksiyonları yansımaya bağlıdır. Ve çalışma zamanında JavaBeans kurallarına dayanan hemen hemen her şey.

  • setAccessibleYalnızca meşru kullanımlarla sınırlayabilir misiniz?
  • Sadece içinden SecurityManagermi?

Evet.

  • O nasıl çalışır? Beyaz liste / kara liste, ayrıntı düzeyi vb.

İzne bağlı, ancak kullanım izninin setAccessibleikili olduğuna inanıyorum . Parçalılık istiyorsanız, kısıtlamak istediğiniz sınıflar için farklı bir güvenlik yöneticisine sahip farklı bir sınıf yükleyici kullanmanız gerekir. Sanırım, daha ince taneli bir mantık uygulayan özel bir güvenlik yöneticisi uygulayabilirsiniz.

  • Uygulamalarınızda yapılandırmak zorunda olmak yaygın mı?

Hayır.

  • Sınıflarımı konfigürasyondan setAccessiblebağımsız olarak yazabilir miyim SecurityManager?
    • Yoksa konfigürasyonu yönetenin insafına mı kaldım?

Hayır yapamazsın ve evet öylesin.

Diğer alternatif ise bunu kaynak kodu analiz araçlarıyla "uygulamaktır"; ör. özel pmdveya findbugskurallar. Veya (diyelim) ile tanımlanan kodun seçici kod incelemesi grep setAccessible ....

Takibe yanıt olarak

Sınıflarımdan hiçbiri, ne olursa olsun, uygulanabilir mahremiyete sahip değil. Singleton modelini (yararları hakkındaki şüpheleri bir kenara bırakarak) uygulamak artık imkansızdır.

Bu seni endişelendiriyorsa, sanırım endişelenmen gerek. Ama gerçekten de diğer programcıları tasarım kararlarınıza saygı duymaya zorlamamalısınız . İnsanlar, tekliğinizin birden çok örneğini (örneğin) karşılıksız olarak yaratmak için yansımayı kullanacak kadar aptalsa, sonuçlarla yaşayabilirler.

Öte yandan, hassas bilgileri ifşa edilmekten korumanın anlamını kapsayacak şekilde "mahremiyet" i kastediyorsanız, yanlış ağaca havlıyorsunuz demektir. Bir Java uygulamasında hassas verileri korumanın yolu, güvenilmeyen kodların hassas verilerle ilgilenen güvenlik sanal alanına girmesine izin vermemektir. Java erişim değiştiricilerinin bir güvenlik mekanizması olması amaçlanmamıştır.

<Dize örneği> - Bunun BÜYÜK bir endişe olduğunu düşünen tek kişi ben miyim?

Muhtemelen tek değil :-). Ancak IMO, bu bir endişe değil. Güvenilmeyen kodun bir sandbox içerisinde çalıştırılması gerektiği kabul edilmektedir. Koda güvendiğiniz / böyle şeyler yapan güvenilir bir programcınız varsa, sorunlarınız beklenmedik şekilde değiştirilebilen Dizelerden daha kötüdür. (Mantık bombalarını, gizli kanallardan veri sızmasını vb. Düşünün )

Geliştirme veya operasyon ekibinizdeki "kötü aktör" sorununu çözmenin (veya hafifletmenin) yolları vardır. Ancak maliyetli ve kısıtlayıcıdırlar ... ve çoğu kullanım durumu için aşırıdırlar.


1
Kalıcılık uygulamaları gibi şeyler için de kullanışlıdır. Yansımalar, hata ayıklayıcılar için gerçekten yararlı değildir (başlangıçta olması amaçlanmış olmasına rağmen). Bunun için bunu yararlı bir şekilde değiştiremezsiniz (alt sınıf) SecurityManager, çünkü tüm elde ettiğiniz izin kontrolüdür - gerçekten yapabileceğiniz tek şey acc'ye bakmak ve belki de yığını yürümek, geçerli iş parçacığını kontrol etmek). grep java.lang.reflect.
Tom Hawtin - tackline

@Stephen C: +1 şimdiden, ancak eklediğim bir soruya daha fazla katkınız için minnettarım. Şimdiden teşekkürler.
poligenel yağlayıcılar

Grep'in kötü bir fikir olduğunu unutmayın. Java'da kodu dinamik olarak çalıştırmanın o kadar çok yolu vardır ki, neredeyse kesinlikle bir tanesini kaçıracaksınız. Kara liste kodu genel olarak başarısızlığın reçetesidir.
Antimony

"Java erişim değiştiricilerinin bir güvenlik mekanizması olması amaçlanmamıştır." - aslında evet öyleler. Java, güvenilir ve güvenilmeyen kodun aynı sanal makinede bir arada bulunmasına izin verecek şekilde tasarlanmıştır - bu, en başından beri temel gereksinimlerinin bir parçasıydı. Ancak yalnızca sistem kilitlenmiş bir SecurityManager ile çalışırken. Korumalı alan kodu yeteneği, tamamen erişim belirticilerinin uygulanmasına bağlıdır.
Jules

@Jules - Çoğu Java uzmanı buna katılmaz; ör. stackoverflow.com/questions/9201603/…
Stephen C

11

Bu perspektifte yansıma, gerçekten de emniyet / emniyetle ortogonaldir.

Yansımayı nasıl sınırlayabiliriz?

Java'nın güvenlik yöneticisi vardır ve ClassLoadergüvenlik modelinin temelleri olarak. Senin durumunda, sanırım bakman gerekiyor java.lang.reflect.ReflectPermission.

Ancak bu, yansıtma sorununu tamamen çözmez. Mevcut olan yansıtıcı yetenekler, şu anda böyle olmayan, ayrıntılı bir yetkilendirme şemasına tabi olmalıdır. Örneğin, belirli çerçevenin yansımayı kullanmasına izin vermek (örneğin Hazırda Bekletme), ancak kodunuzun geri kalanını kullanmamak. Veya bir programın hata ayıklama amacıyla yalnızca salt okunur bir şekilde yansıtmasına izin vermek için.

Gelecekte ana akım haline gelebilecek bir yaklaşım, yansıtma yeteneklerini sınıflardan ayırmak için aynaların kullanılmasıdır . Aynalar: Meta Seviyesi Tesisler için Tasarım İlkelerine bakın . Bununla birlikte , bu konuyu ele alan çeşitli başka araştırmalar var. Ancak dinamik dil için sorunun statik dillerden daha ciddi olduğunu kabul ediyorum.

Yansımanın bize verdiği süper güçten endişelenmeli miyiz? Evet ve hayır.

Evet , Java platformunun bir Classloadergüvenlik yöneticisi ile güvence altına alınması gerektiği için . Yansıma ile uğraşma yeteneği bir ihlal olarak görülebilir.

Hayır , çoğu sistem zaten tamamen güvenli değildir. Pek çok sınıf sıklıkla alt sınıflara ayrılabilir ve bununla sistemi zaten kötüye kullanma olasılığı vardır. Elbette , başka bir kavanozda alt sınıflara ayrılamayacak şekilde sınıflar yapılabilir finalveya mühürlenebilir . Ancak buna göre yalnızca birkaç sınıf doğru bir şekilde (örn. String) güvence altına alınmıştır.

Güzel bir açıklama için son ders hakkındaki bu cevaba bakın . Güvenlik konusunda daha fazla java hacklemek için Sami Koivu'nun bloguna da bakın .

Java'nın güvenlik modeli bazı açılardan yetersiz olarak görülebilir. NewSpeak gibi bazı diller modülerliğe daha da radikal bir yaklaşım benimsiyor, burada sadece size bağımlılık ters çevirme yoluyla açıkça verilene erişebiliyorsunuz (varsayılan olarak hiçbir şey).

Güvenliğin her halükarda göreceli olduğuna dikkat etmek de önemlidir . Dil düzeyinde, örneğin bir modül formunun CPU'nun% 100'ünü tüketmesini veya tüm belleği bir OutOfMemoryException. Bu tür endişelerin başka yollarla ele alınması gerekir. Gelecekte Java'nın kaynak kullanım kotalarıyla genişlediğini görebiliriz, ancak yarın için değil :)

Konuyu daha fazla genişletebilirim ama sanırım amacımı anladım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.