Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια
σε

 

Αρχική σελίδα Ιστολόγια Συζητήσεις Εκθέσεις Φωτογραφιών Αρχειοθήκες

Ασφάλεια μέσω Windows Authentication, SSL και Client Certificates

Îåêßíçóå áðü ôï ìÝëïò kkara. Τελευταία δημοσίευση από το μέλος kkara στις 20-10-2005, 10:35. Υπάρχουν 7 απαντήσεις.
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  18-10-2005, 22:42 6247

    Ασφάλεια μέσω Windows Authentication, SSL και Client Certificates

    Καταρχήν συγχωρείστε με για το μέγεθος του post. Ψιλιάζομαι (πριν κάν ξεκινήσω να το γράφω) ότι θα είναι μεγάλο.

    Στόχος μου να δημιουργήσω "ασφαλείς" Web Services. Δεν αναλύω περαιτέρω το τι επιλογές υπάρχουν για την υλοποίηση της ασφάλειας, γιατί είναι ολόκληρο Forum από μόνο του. Αποφασίστηκε πάντως οι υπηρεσίες να διασφαλίζονται μέσω SSL και Client Certificates. Ο IIS δεν θα επιτρέπει Anonymous Access. τουναντίον (πσσσσσσσ, τι είπα πάλι) θα επιβάλλει τη χρήση Client Certificates και θα επιτρέπει μονάχα συγκεκριμένα Certificates. Γνωρίζω ότι δεν είναι ότι κομψότερο, αλλά τα Web Services θα καλούνται από λίγους και σχετικά ελεγχόμενους clients. (Περισσότερες πληροφορίες για τα παραπάνω βρίσκονται εδώ, από εκεί άλλωστε άντλησα το ακριβές HowTo Wink [;)]). Ως έξτρα ασφάλεια (κερασάκι στην τούρτα) θα επιτρέπεται πρόσβαση μόνο σε συγκεκριμένες IP διευθύνσεις.

    Έχει καλώς ως εδώ. Χρειάζεται όμως να γίνει και Authentication των clients. Εφόσον ισχύουν όλα τα παραπάνω, θα ήταν μάλλον υπερβολικό να χρησιμοποιήσω username/password και να τα ελέγχω στη Βάση Δεδομένων. Θέλω επομένως να κάνω map τα Client Certificates σε συγκεκριμένα Windows Accounts και έτσι να παίρνω το ποιός είναι ο client από το Thread.CurrentPrincipal.Identity.Name. Δουλεύει μια χαρά. Το πρόβλημα έγκειται στη χρήση της μεθόδου Thread.CurrentPrincipal.IsInRole(strMachineName + "\\" +
    strRole)
    , όπου strMachineName είναι το όνομα του Server και strRole ένα User Group (π.χ. "Guests"). Κατά την κλήση αυτής της μεθόδου δεν χρησιμοποιείται το Thread.CurrentPrincipal.Identity (όπως θα έπρεπε) αλλά το NT AUTHORITY\NETWORK SERVICE. Για παράδειγμα, δοκιμάστε τον παρακάτω κώδικα :

    [WebMethod]
    public string WhoAmI() {
        string s = String.Empty;
        s += "Running as User : " + Thread.CurrentPrincipal.Identity.Name + "\n";

        s += "Current Windows Identity : " + System.Security.Principal.WindowsIdentity.GetCurrent().Name;

        return s;

    }


    Προσοχή ότι αν ισχύουν όσα προανέφερα θα πρέπει να κληθεί με συγκεκριμένο Client Certificate, να έχουν γίνει σωστά οι ρυθμίσεις στον IIS, κτλ, κτλ. Για ευκολία στην δοκιμή συχνά άφηνα την όλη διαδικασία με Certificates και SSL και χρησιμοποιούσα Integrated Windows Authentication και Basic Authentication. Έχοντας κάνει map ένα certificate στο account μου και εκτελώντας το παραπάνω service, το αποτέλεσμα είναι πάντοτε το εξής :

    Running as User : OLYMPOS\kkara
    Current Windows Identity : NT AUTHORITY\NETWORK SERVICE


    (προφανώς το PC μου έχει Computer Name OLYMPOS, ίσως γι' αυτό να μη δουλεύει τίποτα, φταίνε οι Θεοί του Ολύμπου Smile [:)])
    Αν καταλαβαίνω καλά το πως δουλεύει όλο το σκηνικό, τότε αυτό είναι σωστό αποτέλεσμα αφού ο κώδικας τρέχει με το συγκεκριμένο Account.
    Πάντως η παρακάτω μέθοδος δεν επιστρέφει αυτό που θα ήθελα :

    /// <summary>Επιστρέφει τα User Groups στα οποία ανήκει ο χρήστης που εκτελεί την υπηρεσία</summary>

    [WebMethod]

    public string WhichRolesAmIIn() {

        string s = String.Empty;

        //Βρίσκουμε το DirectoryEntry του μηχανήματος μέσω των DirectoryServices

        DirectoryEntry deMachine = new DirectoryEntry("WinNT://OLYMPOS");

        foreach(DirectoryEntry child in deMachine.Children)

            //Από τα παιδιά του deMachine μας απασχολούν τα Group χρηστών

            if (child.SchemaClassName ==
    "Group" && Thread.CurrentPrincipal.IsInRole("OLYMPOS\\" +
    child.Name))

                s += "User " +
    Thread.CurrentPrincipal.Identity.Name + " belongs in : " + child.Name +
    "\n";



        return s;

    }


    Όταν καλείται η μέθοδος επιστρέφει :

    User OLYMPOS\kkara belongs in : CERTSVC_DCOM_ACCESS

    Ενώ φυσικά ο χρήστης kkara ανήκει στους Administrators, στους Power Users, κτλ κτλ. Ελέγχοντας αυτό το user group διαπίστωσα ότι είναι το μοναδικό στο οποίο οι χρήστες που είναι δηλωμένοι περιγράφονται ως /Everyone. Τελικά ακόμα δεν έχω καταλάβει ποιόν ακριβώς χρήστη χρησιμοποιεί όταν κάνει το ερώτημα IsUserInRole.

    Δοκίμασα να κάνω και Impersonation, μέσω της εντολής ((WindowsIdentity)Thread.CurrentPrincipal.Identity).Impersonate(); (οπότε τότε αλλάζει το WindowsIdentity.GetCurrent() επιστρέφει όντως τον χρήστη kkara) αλλά και πάλι το αποτέλεσμα ήταν το ίδιο. Ακόμα κι όταν δοκίμασα να απλοποιήσω τελείως τα πράγματα και απλά να χρησιμοποιώ Basic Authentication (χωρίς SSL, χωρίς Credentials, χωρίς τίποτα) είχα και πάλι τα ίδια ακριβώς αποτελέσματα.

    Έχει κανείς καμιά ιδέα για το τι συμβαίνει; Τα νεύρα μου κοντεύουν να σπάσουν Crying [:'(]... Θέλω να χρησιμοποιήσω τα Group στα οποία ανήκει ο χρήστης για να περιορίσω την πρόσβαση σε επίπεδο κώδικα (με το PrincipalPermissionAttribute) αλλά οποιοδήποτε τρόπο κι αν μου πείτε για να βρω σε ποιά group ανήκει ο χρήστης, θα είναι ένα βήμα προς την κατάλληλη κατεύθυνση...

    Στερνή μου γνώση να σε είχα πρώτα...
  •  19-10-2005, 00:06 6248 σε απάντηση της 6247

    Απ: Ασφάλεια μέσω Windows Authentication, SSL και Client Certificates

    Από όλα αυτά που λες... κάτι δεν πάει! Το working schema που παρουσιάζεται με το whitepaper που ανέφερες, σίγουρα δουλεύει.

    Μερικά πράγματα παραπάνω για διευρεύνηση:

    1. Το IIS Authentication δεν είναι ASP.NET Authentication. Άλλο το authentication με τα cerificates που το κάνει ο IIS, αλλά δεν είναι υποχρεωμένος να το περάσει στο ASP.NET Authentication, αν το ASP.NET Authentication δεν είναι σε Windows mode. Και αυτό ορίζεται μέσα από το web.config της εφαρμογής/webservice.

    Εσύ αναφέρεις ότι ο χρήστης που βλέπεις είναι ο ΝΤ Authority\Network Service που είναι ο default λογαριασμός που τρέχει το ASP.NET σε Windows 2003 για όλα τα unmanaged calls που γίνονται από το ASP.NET, βλέπε secure connection σε SQL Server, file system access κτλ., άρα μάλλον δεν είναι σε Windows mode το authentication του ASP.NET.

    2. Η κλήση για το Active Directory... Δεν είναι με WinNT:// αλλά με LDAP://. Αυτό που κάνεις με αυτή την κλήση, είναι να καλείς το Domain συμβατό με NT4 για authentication. Αυτό που σου απαντάει είναι κουτό, αν λάβεις υπόψη σου ότι δεν επιστρέφει όλα τα groups που ανήκει ο χρήστης, αλλά το ένα - αν ανήκει σε ένα - και το default - που έχει δωθεί από τον administrator - αν ανήκει σε περισσότερα από ένα, που φαίνεται να είναι και το πρόβλημά σου σε αυτή την περίπτωση.

     

    George J.

     


    George J. Capnias: Χειροπρακτικός Υπολογιστών, Ύψιστος Γκουράρχης της Κουμπουτερολογίας
    w: capnias.org, t: @gcapnias, l: gr.linkedin.com/in/gcapnias
    dotNETZone.gr News
  •  19-10-2005, 00:40 6249 σε απάντηση της 6247

    Απ: Ασφάλεια μέσω Windows Authentication, SSL και Client Certificates

    Νομίζω ότι για να γίνει αυτό που θέλεις χρειάζεσαι impersonate. Ένα αρθράκι που βρήκα πρόχειρα: http://www.dotnetspider.com/Technology/KBPages/403.aspx (Γράψε άκυρο, δεν είδα ότι το έχεις δοκιμάσει)

    Αλλά γιατί επιλέγεις αυτό το μοντέλο authentication; Όχι ότι είναι άσχημο, απλά θα ήθελα να ακούσω λόγους που σε οδήγησαν σε αυτή την κατεύθυνση και αν μπορείς να πεις, και το πραγματικό περιβάλλον στο οποίο κινείσαι.


    Χρήστος Γεωργακόπουλος
  •  19-10-2005, 12:56 6255 σε απάντηση της 6248

    Απ: Ασφάλεια μέσω Windows Authentication, SSL και Client Certificates

     gcapnias wrote:

    Από όλα αυτά που λες... κάτι δεν πάει! Το working schema που παρουσιάζεται με το whitepaper που ανέφερες, σίγουρα δουλεύει.


    Αυτός είναι και ο λόγος που τα έχω πάρει στο κρανίο! Κανονικά θα έπρεπε να δουλεύει. Ακριβώς γι' αυτό το λόγο και αποφάσισα να ακολουθήσω το άρθρο by the book!

     gcapnias wrote:

    1. Το IIS Authentication δεν είναι ASP.NET Authentication. Άλλο το authentication με τα cerificates που το κάνει ο IIS, αλλά δεν είναι υποχρεωμένος να το περάσει στο ASP.NET Authentication, αν το ASP.NET Authentication δεν είναι σε Windows mode. Και αυτό ορίζεται μέσα από το web.config της εφαρμογής/webservice.

    Εσύ αναφέρεις ότι ο χρήστης που βλέπεις είναι ο ΝΤ Authority\Network Service που είναι ο default λογαριασμός που τρέχει το ASP.NET σε Windows 2003 για όλα τα unmanaged calls που γίνονται από το ASP.NET, βλέπε secure connection σε SQL Server, file system access κτλ., άρα μάλλον δεν είναι σε Windows mode το authentication του ASP.NET.


    Το έχω διπλο- και τριπλο- ελέγξει το web.config. Αρχίζω να σκέφτομαι τι μπορεί να υπάρχει που να εμποδίζει το ASP.NET να εκτελέσει τον κώδικα με τον χρήστη που του δίνει ο IIS. Δοκιμάζοντας τον ίδιο ακριβώς κώδικα σε Windows 2000 Advanced Server είχα τα ίδια αποτελέσματα με την εναλλαγή φυσικά του ΝΤ Authority\Network Service με το account του ASPNET. Δοκίμασα μέχρι και να κάνω υποχρεωτικό Impersonation με στο Web.Config με <identity impersonate="true" userName="kkara" password="password" /> και πάλι δεν άλλαξε τίποτα. Φαίνεται δηλαδή ότι για κάποιο λόγο δεν μπορεί να κάνει Impersonate, παρόλο που η κλήση στο Thread.CurrentPrincipal.Identity επιστρέφει τον σωστό χρήστη...

     gcapnias wrote:

    2. Η κλήση για το Active Directory... Δεν είναι με WinNT:// αλλά με LDAP://. Αυτό που κάνεις με αυτή την κλήση, είναι να καλείς το Domain συμβατό με NT4 για authentication. Αυτό που σου απαντάει είναι κουτό, αν λάβεις υπόψη σου ότι δεν επιστρέφει όλα τα groups που ανήκει ο χρήστης, αλλά το ένα - αν ανήκει σε ένα - και το default - που έχει δωθεί από τον administrator - αν ανήκει σε περισσότερα από ένα, που φαίνεται να είναι και το πρόβλημά σου σε αυτή την περίπτωση.

     


    Την κλήση στο Active Directory την έκανα πρόχειρα, μόνο και μόνο για να πάρω τα User Groups. Επιστρέφει όντως όλα τα User Groups, απλά έχω βάλει τον έλεγχο Thread.CurrentPrincipal.IsInRole("OLYMPOS\\" +
    child.Name)
    οπότε γι' αυτό επιστρέφει μόνο τα group στα οποία ανήκει ο χρήστης.

     cgeo wrote:

    Αλλά γιατί επιλέγεις αυτό το μοντέλο authentication; Όχι ότι είναι άσχημο, απλά θα ήθελα να ακούσω λόγους που σε οδήγησαν σε αυτή την κατεύθυνση και αν μπορείς να πεις, και το πραγματικό περιβάλλον στο οποίο κινείσαι.


    Βασικά εγώ το κυριότερο που ήθελα ήταν να έχω όσο μεγαλύτερη ασφάλεια όσον αφορά του ποιός μπορεί να εκτελέσει το service (χωρίς όμως να κινηθώ σε WS-Secure). Γι' αυτό και μπαίνουν στη μέση τα Client Certificates. Το authentication όμως είναι επίσης σημαντικό για την εφαρμογή. Οι Clients ομαδοποιούνται σε groups και κάθε group επιτρέπεται να έχει πρόσβαση μόνο σε δεδομένα που του ανήκουν (δυστυχώς δεν μπορώ να πω πολλά παραπάνω για τη φύση της εφαρμογής, αν και κάποιοι που διαβάζουν έχουν καταλάβει περί τίνος πρόκειται Wink [;)]. Θα μπορούσα βέβαια να έχω ένα πίνακα στον SQL Server με Usernames, Passwords και Groups, αλλά τότε μαζί με τα Client Certificates θα έπρεπε να δίνω και Username/Password που είναι κάπως υπερβολικό. Γι' αυτό και σκέφτηκα να χρησιμοποιήσω Windows Authentication ώστε απλά να φτιάξω τα Group και τα Accounts στον Server. Αυτό επίσης με προστατεύει ώστε στην περίπτωση που γίνουν πολλοί οι Client να μπορέσω να κάνω Many-To-1 mapping στον IIS και έτσι να κρατήσω μόνο τα Groups των χρηστών και ένα χρήστη για κάθε Group, χωρίς να επηρεάσω την εφαρμογή μου.

    Τελικά αν δεν βρω λύση στο εν λόγω πρόβλημα θα αναγκαστώ να κάνω το ανάποδο, θα προσπαθήσω να βρω από το όνομα του χρήστη (το οποίο έχω!) σε ποια group ανήκει. Απλά θα ήταν ωραίο να δουλέψει απ'ευθείας η IsInRole (ΟΠΩΣ ΑΛΛΩΣΤΕ ΘΑ ΕΠΡΕΠΕAngry [:@]). Αν δεν πετύχει και αυτό θα σκίσω τα πτυχία μου και θα ανοίξω σουβλατζίδικο (μάλλον περισσότερα θα βγάζω από ότι τώρα Smile [:)]


    Στερνή μου γνώση να σε είχα πρώτα...
  •  19-10-2005, 14:08 6260 σε απάντηση της 6247

    Απ: Ασφάλεια μέσω Windows Authentication, SSL και Client Certificates

    Αχα!!! Από ότι φαίνεται είναι πρόβλημα της WindowsIdentity.IsInRole, καθώς αν την καλέσω με BuiltIn ρόλο παίζει μια χαρά (π.χ.((WindowsIdentity)Thread.CurrentPrincipal).IsInRole(System.Security.Principal.WindowsBuiltInRole.PowerUser)). Από ότι βλέπω έχουν πολλοί παρόμοιο πρόβλημα (βλ. εδώ, εδώ κι εδώ). Θα συνεχίσω να το ψάχνω και θα σας ενημερώσω για ότι βρω...

    Στερνή μου γνώση να σε είχα πρώτα...
  •  19-10-2005, 14:35 6261 σε απάντηση της 6260

    Απ: Ασφάλεια μέσω Windows Authentication, SSL και Client Certificates

    Ok, το πρόβλημα εντοπίστηκε και τελικά λύθηκε. Δεν είχε σχέση ούτε με Security, ούτε με Impersonation, αλλά ήταν καθαρά θέμα της IsInRole και δύο ιδιαταιροτήτων :

    1. Όταν ελέγχεις με την IsInRole για bultin groups (π.χ. Administrators, Power Users, κτλ), δεν δουλεύει το <MachineName>\<GroupName>, αλλά μόνο το BUILTIN\<GroupName>
    2. Όταν σε ένα group που έχεις φτιάξει εσύ τοποθετήσεις τον χρήστη με τον οποίο είσαι logged in, τότε η τοποθέτηση δεν ισχύει προτού κάνεις logoff και logon
    Λόγω του 1. δεν μπορούσα να δω ότι ο χρήστης ανήκει στα Group που ήξερα ήδη ότι ανήκει και λόγω του 2. όταν τον τοποθετούσα με το χέρι σε καινούριο group, δεν μπορούσα να το δω (δεν έκανα logoff ο άμοιρος).

    Πολύ χρήσιμος κώδικας ήταν ο παρακάτω :

    //Try to get roles from WindowsIdentity instance via reflexion.

    MethodInfo getroles = typeof(WindowsIdentity).GetMethod("GetRoles", BindingFlags.Instance | BindingFlags.NonPublic);
    string[] roles = (string[])getroles.Invoke(windowsPrincipal.Identity, null);


    που μου επέτρεψε να καλέσω την NonPublic μέθοδο GetRoles για να δω σε ποια group ανήκει τελικά ο χρήστης (μα καλά γιατί δεν είναι public αυτή να μας γλύτωνε τον κόπο Wink [;)]).

    Τελικά δεν θα ανοίξω σουβλατζίδικο... κλαψ λυγμ...

    Στερνή μου γνώση να σε είχα πρώτα...
  •  19-10-2005, 21:26 6271 σε απάντηση της 6247

    Απ: Ασφάλεια μέσω Windows Authentication, SSL και Client Certificates

    Ερώτηση 1: Χρήστες εκτός του domain σου έχεις; Αν σου ζητήσουν αύριο να μπορεί να γίνει κλήση των web services και απ' έξω, θα ανοίξεις account για κάθε εξωτερικό χρήστη στο domain; Πως θα το χειριστείς;

    Ερώτηση 2: Μέσα στο κάθε service πως χειρίζεσαι τους ελέγχους για το authentication; Λες πχ, if Not (UserIsInRole("Administrators") orelse UserIsInRole("SuperUsers")) then throw new AuthenticationException;

     

     


    Χρήστος Γεωργακόπουλος
  •  20-10-2005, 10:35 6282 σε απάντηση της 6271

    Απ: Ασφάλεια μέσω Windows Authentication, SSL και Client Certificates

     cgeo wrote:

    Ερώτηση 1: Χρήστες εκτός του domain σου έχεις; Αν σου ζητήσουν αύριο να μπορεί να γίνει κλήση των web services και απ' έξω, θα ανοίξεις account για κάθε εξωτερικό χρήστη στο domain; Πως θα το χειριστείς;


    Στην πραγματικότητα δεν έχω χρήστες εντός του domain. Έχω μόνο εξωτερικούς χρήστες, οι οποίοι όμως είναι λίγοι στο πλήθος. Σκοπεύω να τους οργανώσω σε User Groups του τοπικού μηχανήματος και φυσικά να τους δώσω μόνο τα απαραίτητα δικαιώματα για να εκτελούν την εφαρμογή (πρέπει ακόμα να δω πως θα παίξει με τη Βάση, λογικά δεν θα έχω πρόβλημα γιατί εκεί θα χρησιμοποιήσω SQL Server authentication και όχι Windows καθότι η Βάση θα είναι σε άλλον Server).


     cgeo wrote:

    Ερώτηση 2: Μέσα στο κάθε service πως χειρίζεσαι τους ελέγχους για το authentication; Λες πχ, if Not (UserIsInRole("Administrators") orelse UserIsInRole("SuperUsers")) then throw new AuthenticationException;



    Θα μπορούσα να χρησιμοποιήσω ελέγχους παρόμοιους με αυτούς που αναφέρεις μέσω της Thread.CurrentPrincipal.IsInRole("ServerName\\SampleGroup"), αλλά ανακάλυψα ορισμένα ενδιαφέροντα πραγματάκια και είπα να τα χρησιμοποιήσω... Το άρθρο που ανέφερα σε προηγούμενο post αναφέρει ότι μπορείς να κλειδώσεις μια μέθοδο με το attribute PrincipalPermissionAttribute ως εξής :

    [WebMethod]


    [PrincipalPermissionAttribute(SecurityAction.Demand,Authenticated=true,Role="ServerName\\SampleGroup")]

    public
    string HelloWorld() {

        return "Hello World";

    }


    Με αυτό τον τρόπο μόνο χρήστες του SampleGroup μπορούν να εκτελέσουν την μέθοδο, ειδάλλως γίνεται throw ένα SecurityException. Εμένα δεν μου έκανε αυτό γιατί α) ήθελα να ελέγχω για πολλαπλά group, β) ήθελα να χειρίζομαι το Exception (wrap και μετά throw). Έτσι κατέληξα στο να χρησιμοποιήσω την κλάση PrincipalPermission ως εξής :

    [WebMethod]

    public string HelloWorld() {

        //Φτιάχνουμε το PrincipalPermission που υποδεικνύει ότι ο

        //τρέχων χρήστης (από το τρέχων IPrincipal) ανήκει στο UserGroup1

        PrincipalPermission perm1 = new
    PrincipalPermission("", "UserGroup1", true);

        //Αντίστοιχα για το UserGroup2

        PrincipalPermission perm2 = new
    PrincipalPermission("", "UserGroup2", true);

        //Ενώνω τα PrincipalPermission με Union

        //οπότε να αρκεί να ισχύει ένα από τα δύο

        PrincipalPermission finalPerm = perm1.Union(perm2);



        try {

            finalPerm.Demand();

        }

        catch (SecurityException ex) {

            throw new Exception("Μεγάλε, που πας χωρίς δικαιώματα;", ex);

        }



        return "Hello World";

    }


    Προφανώς τα User Groups τα διαβάζω από κάπου, αλλά ο παραπάνω κώδικας δίνει την κεντρική ιδέα... Δεν έχω αποφασίσει ακόμα αν θα βάλω τον κατάλληλο κώδικα στον Costructor του Web Service ή σε κάθε Web Method αλλά δεν νομίζω να έχει και μεγάλη διαφορά...


    Στερνή μου γνώση να σε είχα πρώτα...
Προβολή Τροφοδοσίας RSS με μορφή XML
Με χρήση του Community Server (Commercial Edition), από την Telligent Systems