Bezpieczeństwo 1. Podstawowe usługi bezpieczeństwa. 2. Użytkownicy i role. przydzielanie uprawnie ń metodom, role komponentów, korzystanie i konfiguracja mechanizmów bezpieczeństwa w Jboss 3. Java Authentication and Authorization Service. uwierzytelnianie, autoryzacja. 1
Bezpieczeństwo Aplikacje biznesowe zwykle musz ą zapewnić odpowiedni poziom bezpieczeństwa, korzystającym z nich użytkownikom. Specyfikacje JEE i EJB udostępniaj ą podstawowy zakres usług bezpieczeństwa: uwierzytelnianie (authentication) sprawdzenie tożsamości użytkownika, autoryzacja (authorization) określenie praw użytkownika do realizacji odpowiednich zada ń i korzystania z określonych zasobów, poufność (confidentality) ochrona przed podsłuchem i podmian ą przekazywanych informacji. 2
Uwierzytelnianie Specyfikacja EJB nie określa w jaki sposób ma si ę odbywać uwierzytelnianie, pomimo i ż specyfikuje propagacj ę tych informacji od klienta do serwera. Z tego powodu producenci serwerów aplikacji często stosuj ą określone technologie do realizacji tego zadania (np. JAAS). Często uwierzytelniania dokonuje si ę w momencie uzyskiwania dostępu do zasobów poprzez usług ę JNDI. properties.put(context.security_principal, username); properties.put(context.security_credentials, userpassword); InitialContext ctx = new InitialContext(properties); Object ref = jndicontext.lookup("travelagent"); TravelAgentRemote remote = (TravelAgentRemote) PortableRemoteObject.narrow(ref, TravelAgentRemote.class) 3
Autoryzacja: użytkownicy i role użytkownicy: Tomasz role: Administrator Alicja Joanna Menadżer Janusz Michał Pracownik Użytkownicy s ą przypisani do jednej lub kilku ról. Uprawnienia s ą określone na poziomie ról za pomoc ą adnotacji lub deskryptora wdrożenia: ejb-jar.xml. 4
Przydzielanie uprawnie ń metodom... import javax.annotation.security.*; @Stateless @RolesAllowed("AUTHORIZED_MERCHANT")// domyślna dla // wszystkich metod public class ProcessPaymentBean implements ProcessPaymentRemote, ProcessPaymentLocal {... @PermitAll // wszyscy uwierzytelnieni public boolean bycash(customer customer, double amount) throws PaymentException{... @RolesAllowed("CHECK_FRAUD_ENABLED") // zaufani agenci public boolean bycheck(customer customer, CheckDO check, double amount) throws PaymentException{... // zautoryzowani agenci - domyślnie public boolean bycredit(customer customer, CreditCardDO card, double amount) throws PaymentException{...... 5
Przydzielanie uprawnie ń metodom <ejb-jar version="3.0"> <assembly-descriptor> <security-role> <description>zautoryzowani</description> <role-name>authorized_merchant</role-name> </security-role> <security-role> <description>zaufani</descripton> <role-name>check_fraud_enabled</role-name> </security-role> <method-permission> <role-name>authorized_merchant</role-name> <method> <ejb-name>processpaymentbean</ejb-name> <method-name>bycredit</method-name> </method> </method-permission> 6
Przydzielanie uprawnie ń metodom <method-permission> <role-name>check_fraud_enabled</role-name> <method> <ejb-name>processpaymentbean</ejb-name> <method-name>bycheck</method-name> </method> </method-permission> <method-permission> <unchecked/> <method> <ejb-name>processpaymentbean</ejb-name> <method-name>bycash</method-name> </method> </method-permission> </assembly-descriptor> </ejb-jar> 7
Identyfikowanie metod w XML'u Określając metody możemy używać wyraże ń wieloznacznych, np: <method> <ejb-name>processpaymentbean</ejb-name> <method-name>*</method-name> </method> oznacza wszystkie metody w komponencie ProcessPaymentBean. <method> <ejb-name>processpaymentbean</ejb-name> <method-name>bycheck</method-name> </method> oznacza wszystkie metody w komponencie ProcessPaymentBean o nazwie bycheck. @Local public interface ProcessPaymentLocal { boolean bycheck(customer cust, CheckDO check, double amount); boolean bycheck(double[] amounts); boolean bycheck( ); 8
Identyfikowanie metod w XML'u - parametry Precyzyjne określenie powyższych metod uzyskujemy korzystając z elementu <method-params>: <method> <ejb-name>processpaymentbean</ejb-name> <method-name>bycheck</method-name> <method-params> <method-param>com.titan.domain.customer</method-param> <method-param>com.titan.processpayment.checkdo</method-param> <method-param>double</method-param> </method-params> </method> <method> <ejb-name>processpaymentbean</ejb-name> <method-name>bycheck</method-name> <method-params></method-params> </method> <method> <ejb-name>processpaymentbean</ejb-name> <method-name>bycheck</method-name> <method-params>double[]</method-params> </method> 9
Identyfikowanie metod w XML'u - interfejsy Interfejs zdalny/lokalny określamy następująco: <method> <ejb-name>processpaymentbean</ejb-name> <method-name>*</method-name> <method-intf>remote</method_intf> </method> Wewną trz <method-intf> dopuszczalne s ą następujące wartości: Remote, Home, LocalHome, Local, ServiceEndpoint. Aby wykluczyć dostęp do metody można uż yć adnotacji @DenyAll lub wpisać j ą na list ę <exclude-list>: <assembly-descriptor> <exclude-list> <method> <ejb-name>processpaymentbean</ejb-name> <method-name>bycash</method-name> </method> </exclude-list> </assembly-descriptor> 10
Role komponentów Komponenty EJB równie ż mog ą występować w określonych rolach. Aby zdefiniować takie zachowanie korzystamy z adnotacji @RunAs. import javax.annotation.security.*; @Stateful @RunAs("AUTHORIZED_MERCHANT") public class TravelAgentBean implements TravelAgentRemote{... lub z deskryptora wdrorzenia: <ejb-jar version="3.0"> <enterprise-beans> <session> <ejb-name>travelagentbean</ejb-name> <security-identity> <run-as> <role-name>authorized_merchant</role-name> </run-as> </security-identity> </session> </enterprise-beans> </ejb-jar> 11
Role komponentów Komponenty sesyjne stanowe mog ą dodatkowo używać roli korzystającego z nich klienta. <enterprise-beans> <entity> <ejb-name>employeeservice</ejb-name> <security-identity> <user-caller-identity/> </security-identity> </entity> </enterprise-beans> 12
Korzystanie z mechanizmów bezpieczeństwa Komponenty EJB posiadaj ą dostęp do informacji o uprawnieniach klienta. Przykładem mog ą być metody interfejsu EJBContext. getcallerprincipal() - zwraca obiekt implementujący interfejs java.security.principal opisujący klienta: Principal caller = ctx.getcallerprincipal( ); String travelagent = caller.getname( ); iscallerinrole(string) sprawdza, czy klientowi jest przypisana odpowiednia rola. if (amount > maximumjuniortrade && ctx.iscallerinrole("junior_travel_agent")) throw new PaymentException("Brak uprawnien"); Rola wykorzystywana w kodzie komponentu musi być wcześniej zadeklarowana. 13
Korzystanie z mechanizmów bezpieczeństwa... import javax.ejb.*; import javax.annotation.*; import javax.annotation.security.*; @Stateless @DeclareRoles("JUNIOR_TRAVEL_AGENT") public class ProcessPaymentBean implements ProcessPaymentLocal { @Resource SessionContext ctx;... private boolean process(...) throws PaymentException { if (ctx.iscallerinrole("junior_travel_agent")) throw new PaymentException("Brak uprawnien");... 14
Korzystanie z mechanizmów bezpieczeństwa Alternatywnie, rola może zostać zadeklarowana w deskryptorze wdrożenia: <ejb-jar version="3.0"> <enterprise-beans> <session> <ejb-name>processpaymentbean</ejb-name> <security-role-ref> <role-name>junior_travel_agent</role-name> </security-role-ref> </session> </enterprise-beans> </ejb-jar> 15
Konfiguracja bezpieczeństwa w JBoss Na wstępie należy utworzyć domen ę bezpieczeństwa ( security domain). JBoss obsługuje trzy sposoby konfiguracji domen: LDAP, bazy danych oraz plik conf/login-config.xml. <application-policy name="titanidentitydb"> <authentication> <login-module code="org.jboss.security.auth.spi.usersrolesloginmodule" flag = "required"> <module-option name="usersproperties"> users-titan.properties </module-option> <module-option name="rolesproperties"> roles-titan.properties </module-option> </login-module> </authentication> </application-policy> 16
Konfiguracja bezpieczeństwa Pliki users-titan.properties i roles-titan.properties musz ą znajdować si ę na scieżce klass aplikacji. Przykładowa zawartość: users-titan.properties michal=alamakota tomasz=tomasz123 alicja=rty!#@ad4 roles-titan.properties michal=user tomasz=user, ADMINISTRATOR alicja=manager, ADMINISTRATOR w JBoss fragment aplikacji klienckiej (autoryzacja poprzez JNDI): public Context getinitialcontext( ) throws Exception { Properties env = new Properties( ); env.setproperty(context.security_principal, user); env.setproperty(context.security_credentials, password); env.setproperty(context.initial_context_factory, "org.jboss.security.jndi.jndilogininitialcontextfactory"); return new InitialContext(env); 17
JBoss i JAAS JAAS (Java Authentication and Authorization Service) jest specyfikacj ą umożliwiając ą zarządzanie bezpieczeństwem z poziomu Języka Java. Od wersji 1.4 JAAS zostało włączone do j2sdk (Java 2 Standard Developer Kit). Serwer JBoss podczas przeprowadzenia uwierzytelniania i autoryzacji korzysta z infrastruktury omówionej w ramach JAAS. 18
JAAS - przykład package sample; import java.io.*; import java.util.*; import javax.security.auth.login.*; import javax.security.auth.*; import javax.security.auth.callback.*; public class SampleAcn { public static void main(string[] args) { LoginContext lc = null; try { lc = new LoginContext("Sample", new MyCallbackHandler()); catch (Exception ex){ex.printstacktrace();system.exit(-1); try { lc.login(); catch (Exception ex){ex.printstacktrace();system.exit(-1); System.out.println("Authentication succeeded!"); 19
JAAS kontekst logowania Kontekst logowania potrzebuje informacji o: - klasie odpowiedzialnej za komunikacj ę z użytkownikiem, implementującej interfejs CallbackHandler (MyCallbackHandler). Sun dostarcza dwie klasy, które można wykorzystać we własnych aplikacjach: TextCallbackHandler i DialogCallbackHandler. - klasie implementującej wymagany sposób uwierzytelniania implementującej sample_jaas.config): LoginModule (plik konfiguracyjny Sample { sample.module.sampleloginmodule required debug=true; ; 20
JAAS - CallbackHandler class MyCallbackHandler implements CallbackHandler { public void handle(callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof TextOutputCallback) { // display the message according to the specified type TextOutputCallback toc = (TextOutputCallback)callbacks[i]; switch (toc.getmessagetype()) { case TextOutputCallback.INFORMATION: System.out.println(toc.getMessage()); break; case TextOutputCallback.ERROR: System.out.println("ERROR: " + toc.getmessage()); break; case TextOutputCallback.WARNING: System.out.println("WARNING: " + toc.getmessage()); break; default: throw new IOException("Unsupported message type: " + toc.getmessagetype()); 21
JAAS - CallbackHandler else if (callbacks[i] instanceof NameCallback) { NameCallback nc = (NameCallback)callbacks[i]; System.err.print(nc.getPrompt()); System.err.flush(); nc.setname((new BufferedReader (new InputStreamReader(System.in))).readLine()); else if (callbacks[i] instanceof PasswordCallback) { // prompt the user for sensitive information PasswordCallback pc = (PasswordCallback)callbacks[i]; System.err.print(pc.getPrompt()); System.err.flush(); pc.setpassword(readpassword(system.in)); else { throw new UnsupportedCallbackException (callbacks[i], "Unrecognized Callback"); // Czyta haslo z podanego strumienia private char[] readpassword(inputstream in) throws IOException {... 22
JAAS - LoginModule Kontekst logowania tworzy nowy pusty obiekt Subject reprezentujący uwierzytelnianego użytkownika lub usług ę, oraz moduł logujący LoginModule. Wywołanie metody login() powoduje uwierzytelnienie, utworzenie jednego lub wielu obiektów implementujących Principal (prawa dostępu) oraz dodanie ich do obiektu Subject. Gotowy zbiór modułów logowania (com.sun.security.auth.module): JndiLoginModule uwierzytelnianie poprzez JNDI, KeyStoreLoginModule alias zapisany w keystore, Krb5LoginModule uwierzytelnianie za pomoc ą protokołu Kerberos, NTLoginModule uwierzytelnianie poprzez Windows NT UnixLoginModule systemy UNIX 23
JAAS - LoginModule package sample.module; import... public class SampleLoginModule implements LoginModule { // stan początkowy private Subject subject; private CallbackHandler callbackhandler; private Map sharedstate; private Map options; // opcje private boolean debug = false; // status uwierzytelniania private boolean succeeded = false; private boolean commitsucceeded = false; // użytkownik i haslo private String username; private char[] pass // przykładowy Principal private SamplePrincipal userprincipal; 24
JAAS - LoginModule public void initialize(subject subject, CallbackHandler callbackhandler, Map sharedstate, Map options) { this.subject = subject; this.callbackhandler = callbackhandler; this.sharedstate = sharedstate; this.options = options; // initialize any configured options debug = "true".equalsignorecase( (String)options.get("debug")); public boolean login() throws LoginException { // przygotowanie - prośba o login i hasło if (callbackhandler == null) throw new LoginException("brak CallbackHandler'a "); Callback[] callbacks = new Callback[2]; callbacks[0] = new NameCallback("login: "); callbacks[1] = new PasswordCallback("haslo: ", false); 25
JAAS - LoginModule try { callbackhandler.handle(callbacks); username = ((NameCallback)callbacks[0]).getName(); char[] tmppass = ((PasswordCallback)callbacks[1]).getPassword(); if (tmppass == null) tmppass = new char[0]; pass = new char[tmppass.length]; System.arraycopy(tmpPass, 0, pass, 0, tmppass.length); ((PasswordCallback)callbacks[1]).clearPassword(); catch (Exception ex) { throw new LoginException(ex.toString()); boolean buser = false, bpass = false; if (username.equals("testuser")) buser = true; if (buser && pass.length==12 && pass[0]=='t' &&... ){ bpass = true; succeeded = true; return true; else { // zły login/hasło succeeded = false; username = null; password = null; if (!buser) throw new FailedLoginException("Zły login"); else throw new FailedLoginException("Złe hasło"); 26
JAAS - LoginModule public boolean commit() throws LoginException { if (succeeded == false) return false; else { // przypisujemy użytkownikowi obiekt Principal userprincipal = new SamplePrincipal(username); if (!subject.getprincipals().contains(userprincipal)) subject.getprincipals().add(userprincipal); // porządki username = null; password = null; commitsucceeded = true; return true; public boolean abort() throws LoginException { if (succeeded == false) return false; else if (succeeded == true && commitsucceeded == false){ // logowanie pomyślne ale brak uwierzytelnienia succeeded = false; username = null; password = null; userprincipal = null; else { // inny problem logout(); return true; 27
JAAS - LoginModule public boolean logout() throws LoginException { subject.getprincipals().remove(userprincipal); succeeded = false; succeeded = commitsucceeded; username = null; password = null; userprincipal = null; return true; Metody initialize(), login(), commit(), abort() i logout() nale żą do interfejsu LoginModule. 28
JAAS - Autoryzacja Kolejnym etapem po sprawdzeniu tożsamości użytkownika (uwierzytelnieniu) jest autoryzacja (określenie i nadanie właściwych uprawnie ń). W tym celu należy: określić uprawnienia dla odpowiednich obiektów Principal, związać obiekt Subject z aktualnym kontekstem kontroli dostępu. 29
JAAS - Autoryzacja package sample; import... public class SampleAzn { public static void main(string[] args) { LoginContext lc = null; try { lc = new LoginContext("Sample", new MyCallbackHandler()); lc.login(); catch (Exception ex) {... Subject mysubject = lc.getsubject();... // wywołanie przykładowej akcji PrivilegedAction action = new SampleAction(); Subject.doAsPrivileged(mySubject, action, null); System.exit(0); 30
JAAS przykładowa akcja package sample; import java.io.file; import java.security.privilegedaction; public class SampleAction implements PrivilegedAction { public Object run() { System.out.println("\njava.home = " + System.getProperty("java.home")); System.out.println("\nuser.home = " + System.getProperty("user.home")); File f = new File("plik.txt"); System.out.print("\nplik.txt "); if (!f.exists()) System.out.print("nie "); System.out.println("istnieje."); return null; 31
JAAS polisa /* prawa dla LoginModule */ grant codebase "file:./samplelm.jar" { permission javax.security.auth.authpermission "modifyprincipals"; ; grant codebase "file:./sampleazn.jar" { permission javax.security.auth.authpermission "createlogincontext.sample"; permission javax.security.auth.authpermission "doasprivileged"; ; /* Określenie prawd dostępu do SampleAction */ grant codebase "file:./sampleaction.jar", Principal sample.principal.sampleprincipal "testuser" { ; permission java.util.propertypermission "java.home", "read"; permission java.util.propertypermission "user.home", "read"; permission java.io.filepermission "foo.txt", "read"; 32
JAAS przygotowanie i uruchomienie jar -cvf SampleAzn.jar sample/sampleazn.class sample/mycallbackhandler.class jar -cvf SampleAction.jar sample/sampleaction.class jar -cvf SampleLM.jar sample/module/sampleloginmodule.class sample/principal/sampleprincipal.class java -classpath SampleAzn.jar:SampleAction.jar:SampleLM.jar -Djava.security.manager -Djava.security.policy==sampleazn.policy -Djava.security.auth.login.config==sample_jaas.config sample.sampleazn 33
Podsumowanie Specyfikacja JEE określa sposób zarządzania bezpieczeństwem poprzez określenia uprawnie ń dla ról, do których mog ą być przypisywani użytkownicy. Sposób uwierzytelniania jest zależny od kontenera EJB. Najpopularniejsz ą usług ą realizując ą te zadania w świecie Javy jest JAAS. 34