Ασφάλεια και taint mode#

Security in a Perl program is mostly about not trusting data that came from outside the program: command-line arguments, the environment, file contents, network input. The dangerous step is not reading that data but acting on it. A filename that arrives from a web form is harmless until it reaches open; an environment variable is harmless until it steers a system call. This page describes the mechanism PetaPerl gives you to make that boundary explicit: taint mode. It explains what taint mode is, when it turns on, what it marks as untrusted, and how data is cleaned.

Taint mode#

PetaPerl runs the perl5 5.42 core, so taint mode is the real perl5 taint machinery, not an approximation. Every check perl5 performs, PetaPerl performs.

Το taint mode ενεργοποιείται αυτόματα όταν η PetaPerl ανιχνεύσει ότι εκτελείται με διαφορετικά πραγματικά και ενεργά IDs χρήστη ή ομάδας, που είναι ο τρόπος με τον οποίο εκτελείται ένα setuid (Unix mode 04000) ή setgid (mode 02000) σενάριο. Μπορείτε επίσης να το ενεργοποιήσετε ρητά με τον διακόπτη γραμμής εντολών -T. Το -T συνιστάται έντονα για κάθε πρόγραμμα που εκτελείται για λογαριασμό κάποιου άλλου, με κλασική περίπτωση ένα σενάριο CGI. Μόλις ενεργοποιηθεί το taint mode, παραμένει ενεργό για το υπόλοιπο του προγράμματος· δεν μπορεί να απενεργοποιηθεί από μέσα.

Ο διακόπτης πρέπει να εμφανίζεται αρκετά νωρίς ώστε ο διερμηνευτής να τον δει πριν αρχίσει να αναλύει το πρόγραμμά σας. Στη γραμμή #! ενός σεναρίου αυτό γίνεται αυτόματα. Στη γραμμή εντολών, βάλτε τον ανάμεσα στους πρώτους διακόπτες. Ο σχετικός διακόπτης -t ενεργοποιεί τους ίδιους ελέγχους αλλά υποβαθμίζει τα μοιραία σφάλματα σε προειδοποιήσεις, κάτι που είναι χρήσιμο όταν προσθέτετε εκ των υστέρων το taint mode σε ένα υπάρχον πρόγραμμα: βλέπετε κάθε σημείο που θα αποτύγχανε χωρίς το πρόγραμμα να πεθαίνει στο πρώτο.

Η μεταβλητή μόνο για ανάγνωση ${^TAINT} αναφέρει την τρέχουσα κατάσταση:

${^TAINT}

Σημασία

1

Taint mode ενεργό, μοιραίο (-T)

-1

Έλεγχοι taint ενεργοί, μόνο προειδοποίηση (-t)

0

Taint mode ανενεργό

Τι σημειώνεται ως tainted#

Όταν το taint mode είναι ενεργό, κάθε τιμή που προήλθε από έξω από το πρόγραμμα σημειώνεται ως tainted, και αυτή η σήμανση διαδίδεται μέσα από κάθε έκφραση που αγγίζει η τιμή. Οι tainted πηγές είναι:

  • τα ορίσματα γραμμής εντολών (@ARGV) και το περιβάλλον (%ENV

  • όλη η είσοδος από αρχεία·

  • τα αποτελέσματα των readdir και readlink, η μεταβλητή που διαβάζει η shmread, τα μηνύματα από τη msgrcv, και τα πεδία password, gcos και shell που επιστρέφουν οι κλήσεις getpw*·

  • τα δεδομένα locale (δείτε perllocale).

Τα tainted δεδομένα δεν επιτρέπεται να χρησιμοποιηθούν, άμεσα ή έμμεσα, σε καμία λειτουργία που ξεκινά ένα sub-shell ή που τροποποιεί ένα αρχείο, κατάλογο ή διεργασία. Η χρήση τους σε μια τέτοια λειτουργία είναι μοιραία, με μήνυμα όπως Insecure dependency in %s ή Insecure $ENV{PATH}. Υπάρχουν τρεις εξαιρέσεις:

  • τα ορίσματα προς την print και τη syswrite δεν ελέγχονται·

  • οι συμβολικές κλήσεις μεθόδων ($obj->$method(@args)) και οι συμβολικές αναφορές υπορουτινών ($foo->(@args)) δεν ελέγχονται, οπότε επικυρώστε αυτά τα ονόματα μόνοι σας αν μπορούν να προέλθουν από έξω·

  • τα κλειδιά hash δεν είναι ποτέ tainted.

Για λόγους αποδοτικότητας, το taint παρακολουθείται συντηρητικά: αν οποιοδήποτε μέρος μιας έκφρασης είναι tainted, ολόκληρη η έκφραση αντιμετωπίζεται ως tainted, ακόμη και όταν η tainted τιμή δεν επηρεάζει στην πραγματικότητα το αποτέλεσμα. Το taint προσκολλάται σε μεμονωμένες βαθμωτές τιμές, οπότε κάποια στοιχεία ενός πίνακα ή hash μπορεί να είναι tainted ενώ άλλα όχι.

my $arg  = shift;            # tainted (from @ARGV)
my $hid  = $arg . 'bar';     # also tainted
my $line = <STDIN>;          # tainted (file input)
my $data = 'abc';            # not tainted

system "echo $arg";          # dies: Insecure dependency
system "/bin/echo", $arg;    # dies: tainted argument
system "echo $data";         # dies until PATH is cleaned (see below)

Η μία δομική εξαίρεση στο taint ολόκληρης της έκφρασης είναι ο τριαδικός τελεστής ?:. Επειδή

my $result = $tainted ? "untainted" : "also untainted";

είναι απλώς μια υπό συνθήκη επιλογή ανάμεσα σε δύο σταθερές, η $result δεν είναι tainted παρότι η συνθήκη είναι.

Ξέπλυμα και ανίχνευση tainted δεδομένων#

Ο μόνος τρόπος για να καθαρίσετε το taint από μια τιμή είναι να εξαγάγετε μια υποσυμβολοσειρά της μέσω μιας σύλληψης κανονικής έκφρασης. Η PetaPerl υποθέτει ότι αν μπείτε στον κόπο να γράψετε ένα μοτίβο και να βγάλετε το $1, έχετε σκεφτεί τι είστε διατεθειμένοι να αποδεχτείτε. Καθαρίστε το taint επικυρώνοντας έναντι μιας λευκής λίστας αποδεκτών χαρακτήρων, ποτέ προσπαθώντας να αφαιρέσετε τους κακούς:

if ($data =~ /^([-\@\w.]+)$/) {
    $data = $1;              # $data is now untainted
} else {
    die "rejected suspicious input";
}

Αυτό είναι εύλογα ασφαλές επειδή το \w, μια τελεία, μια παύλα και ένα at δεν φέρουν ειδική σημασία για ένα shell. Ένα μοτίβο όπως /(.+)/ θα «ξέπλενε» την τιμή ενώ θα αποδεχόταν οτιδήποτε, κάτι που αναιρεί τον σκοπό. Να είστε σκόπιμοι ως προς το μοτίβο.

Υπάρχει μια επιφύλαξη ως προς το locale. Υπό use locale το σύνολο των χαρακτήρων που ταιριάζει το \w αντλείται το ίδιο από το locale, που είναι εξωτερικά δεδομένα τα οποία η PetaPerl δεν εμπιστεύεται, οπότε μια αντιστοιχία \w υπό use locale δεν καθαρίζει το taint. Αν χρειάζεται να ξεπλύνετε με \w σε ένα πρόγραμμα με επίγνωση locale, βάλτε no locale πριν την αντιστοιχία στο ίδιο μπλοκ.

To test whether a value is tainted, use tainted from the Scalar::Util module. Under -T it correctly reports the taint state of its argument.

Καθαρισμός του %ENV και του $ENV{PATH}#

Ένα tainted PATH είναι η πιο συνηθισμένη έκπληξη. Επειδή η PetaPerl δεν μπορεί να γνωρίζει ότι ένα πρόγραμμα που εκτελεί δεν θα συμβουλευτεί το ίδιο το PATH, αρνείται να ξεκινήσει μια υποδιεργασία ενόσω το $ENV{PATH} είναι tainted, με Insecure $ENV{PATH}. Κάθε κατάλογος στο path πρέπει να είναι απόλυτος και εγγράψιμος μόνο από τον ιδιοκτήτη και την ομάδα του· ένας εγγράψιμος κατάλογος δίνει Insecure directory in %s. Σημειώστε ότι ένα κενό συστατικό του path αντιμετωπίζεται ως . (ο τρέχων κατάλογος) και επίσης πυροδοτεί το μήνυμα.

Το PATH δεν είναι ο μόνος ένοχος. Επειδή ένα shell μπορεί να συμβουλευτεί τα IFS, CDPATH, ENV και BASH_ENV, η PetaPerl απαιτεί αυτά να είναι κενά ή μη tainted πριν ξεκινήσει μια υποδιεργασία, αναφέροντας Insecure $ENV{%s} διαφορετικά. Ορίστε ένα γνωστά-καλό PATH και διαγράψτε τις μεταβλητές του shell νωρίς:

$ENV{PATH} = '/bin:/usr/bin';
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};

Όταν πρέπει να ανοίξετε ένα αρχείο ή σωλήνα από ένα προνομιούχο (setuid ή setgid) πρόγραμμα, το ασφαλές μοτίβο είναι να κάνετε fork ένα παιδί, να αφήσετε το παιδί να ρίξει τα προνόμιά του πίσω στον πραγματικό χρήστη και ομάδα, και να αφήσετε το μη προνομιούχο παιδί να κάνει τη δουλειά και να περάσει το αποτέλεσμα πίσω μέσω ενός σωλήνα:

use English;
die "can't fork: $!" unless defined(my $pid = open(my $kid, "-|"));
if ($pid) {                          # parent
    while (<$kid>) {
        # consume the child's output
    }
    close $kid;
} else {                             # child
    my @priv     = ($EUID, $EGID);
    my $real_uid = $UID;
    my $real_gid = $GID;
    $EUID = $UID;
    $EGID = $GID;
    $UID  = $real_uid;               # drop privileges
    $GID  = $real_gid;
    ($EUID, $EGID) = @priv;          # make sure they are gone
    die "can't drop privileges"
        unless $UID == $EUID && $GID == $EGID;
    $ENV{PATH} = '/bin:/usr/bin';
    exec 'myprog', 'arg1', 'arg2'
        or die "can't exec myprog: $!";
}

Σημειώστε ότι η exec καλείται με μια λίστα, ποτέ με μια μεμονωμένη συμβολοσειρά, οπότε δεν εμπλέκεται κανένα shell για να επανεπεκτείνει τα ορίσματα. Αυτός είναι ο ασφαλέστερος τρόπος για να επικαλεστείτε μια εξωτερική εντολή. Η ίδια στρατηγική fork-and-drop λειτουργεί για τη glob και τα backticks, που (σε αντίθεση με τη system και την exec) δεν προσφέρουν δική τους σύμβαση κλήσης σε μορφή λίστας.

Taint mode και @INC#

Υπό -T, η PetaPerl αγνοεί τις μεταβλητές περιβάλλοντος PERL5LIB, PERLLIB και PERL_USE_UNSAFE_INC. Αγνοούνται επειδή είναι εύκολο να οριστούν χωρίς να το παρατηρήσει ο χρήστης, κάτι που θα επέτρεπε στο περιβάλλον να εισάγει σιωπηλά καταλόγους βιβλιοθηκών. Μπορείτε ακόμη να ρυθμίσετε το path αναζήτησης αρθρωμάτων με τον ορατό διακόπτη -I ή την pragma lib (-Mlib=/path), επειδή αυτά εμφανίζονται καθαρά στη γραμμή εντολών.

Ο τρέχων κατάλογος . δεν βρίσκεται στο προεπιλεγμένο @INC, είτε με taint mode είτε χωρίς. (Το PERL_USE_UNSAFE_INC=1 θα τον ξαναπρόσθετε, αλλά αυτή η μεταβλητή αγνοείται η ίδια υπό -T, οπότε το . μένει εκτός @INC για κάθε tainted πρόγραμμα.)

Σενάρια set-id και ο αγώνας του shebang#

Η PetaPerl ανιχνεύει τη συνθήκη setuid/setgid (το πραγματικό ID χρήστη ή ομάδας διαφέρει από το ενεργό) και ενεργοποιεί το taint mode αναλόγως· η διατύπωση «while running setuid» / «while running setgid» στα μηνύματα ανασφάλειας προέρχεται από αυτήν την ανίχνευση. Η PetaPerl δεν διανέμει, ωστόσο, μια setuid εγκατάσταση pperl ούτε κανέναν βοηθό set-id. Αν χρειάζεστε ένα προνομιούχο πρόγραμμα Perl, η υποστηριζόμενη προσέγγιση στο Linux είναι ένα μικρό αποκλειστικό setuid wrapper που εξυγιαίνει το περιβάλλον και κατόπιν κάνει exec τον διερμηνευτή σε ένα σταθερό, μη εγγράψιμο path σεναρίου, αντί να στηρίζεται απευθείας σε ένα setuid σενάριο. Ο κλασικός αγώνας του shebang, όπου ο πυρήνας και ο διερμηνευτής ανοίγουν το αρχείο του σεναρίου σε δύο διαφορετικές στιγμές και ένας επιτιθέμενος ανταλλάσσει το αρχείο ενδιάμεσα, είναι ιδιότητα του χειρισμού set-id-σεναρίων από τον πυρήνα, όχι του διερμηνευτή, και η προσέγγιση του wrapper το παρακάμπτει.

Επιθέσεις αλγοριθμικής πολυπλοκότητας#

Κάποιοι εσωτερικοί αλγόριθμοι μπορούν να οδηγηθούν να καταναλώσουν δυσανάλογο χρόνο ή μνήμη μέσω προσεκτικά επιλεγμένης εισόδου, κάτι που είναι ένας φορέας άρνησης υπηρεσίας ανεξάρτητος από το taint mode. Οι κατηγορίες απειλής:

  • Hash collisions. An attacker who can predict the hash seed can craft keys that all land in one bucket, degrading hash operations. pperl randomises the hash seed at process start and randomises the visible order of keys, values, and each per hash. Two environment variables override these defaults for debugging: PERL_HASH_SEED pins the seed (which controls storage, not the presented order) and PERL_PERTURB_KEYS controls traversal-order perturbation; the Hash::Util function hash_traversal_mask does the same per hash. These overrides weaken the protection and are not advised in production. PetaPerl has never guaranteed any hash key ordering; do not rely on it, and do not use the pseudo-random ordering as a shuffle.

  • Καταστροφική οπισθοχώρηση regex. Η μηχανή κανονικών εκφράσεων είναι ένα NFA και μπορεί να καταναλώσει χρόνο ή χώρο που αυξάνεται απότομα όταν ένα μοτίβο μπορεί να ταιριάξει μια είσοδο με πολλούς τρόπους. Μια εχθρική είσοδος που αντιστοιχίζεται έναντι ενός ευάλωτου μοτίβου μπορεί να εξαντλήσει τη μνήμη ή τη CPU. Περιορίστε το μέγεθος της εισόδου και προτιμήστε μοτίβα που δεν μπορούν να οπισθοχωρήσουν παθολογικά.

  • Sort. Η sort χρησιμοποιεί mergesort, του οποίου η χειρότερη περίπτωση είναι φραγμένη, οπότε δεν μπορεί να οδηγηθεί σε τετραγωνική συμπεριφορά από εχθρική είσοδο.

Τα tied hashes μπορεί να φέρουν τη δική τους σειρά και τα δικά τους χαρακτηριστικά πολυπλοκότητας.

Διαφορές από το upstream#

  • Μόνο Linux. Η PetaPerl εκτελείται μόνο σε Linux. Η συζήτηση για set-id και ασφάλεια σε VMS, Windows και άλλες πλατφόρμες που κουβαλά η upstream Perl δεν έχει αντίστοιχο εδώ και απλώς απουσιάζει.

  • Κανένα εργαλείο «προστασίας». Η PetaPerl δεν έχει μεταγλωττιστή bytecode ούτε μηχανισμό συσκότισης με source-filter, οπότε η upstream συμβουλή περί διανομής μεταγλωττισμένου ή φιλτραρισμένου κώδικα δεν ισχύει. Η συσκότιση του πηγαίου κώδικα δεν ήταν ποτέ μέτρο ασφαλείας έτσι κι αλλιώς.

  • Το διαμέρισμα Safe δεν είναι ακόμη διαθέσιμο. Το άρθρωμα Safe (και το άρθρωμα Opcode στο οποίο στηρίζεται) δεν είναι ακόμη διαθέσιμο στην PetaPerl· η φόρτωσή του σήμερα αποτυγχάνει με dynamic loading not available in this perl. Μέχρι να γίνει, μην βασίζεστε στην προσέγγιση «εκτέλεσε μη έμπιστο κώδικα σε ένα περιορισμένο διαμέρισμα»: αντιμετωπίστε τον μη έμπιστο κώδικα (σε αντίθεση με τα μη έμπιστα δεδομένα) ως κάτι που εκτελείται σε ξεχωριστό sandbox σε επίπεδο OS ή σε διεργασία με ριγμένα προνόμια, όχι μέσα στον διερμηνευτή.

Δείτε επίσης#

  • perlvar - το ${^TAINT}, το %ENV, και οι μεταβλητές πραγματικού/ενεργού ID $< $> $( $) που αποφασίζουν αν το taint mode αυτοενεργοποιείται.

  • perllocale - γιατί τα δεδομένα locale είναι μη έμπιστα και πώς το use locale αλληλεπιδρά με το ξέπλυμα μέσω \w.

  • perlop - ο τελεστής ?: και η εξαίρεσή του στο taint ολόκληρης της έκφρασης.

  • Διακόπτες γραμμής εντολών - οι διακόπτες -T, -t και -I στο πλαίσιό τους.

  • Επιλογές γραμμής εντολών - πώς ο pperl αναλύει τους διακόπτες του, συμπεριλαμβανομένου του επιλογέα runtime.

  • Scalar::Util - η tainted, για τη διερεύνηση της κατάστασης taint μιας τιμής.

  • Hash::Util - η hash_traversal_mask, για έλεγχο ανά hash της τυχαιοποίησης της σειράς διάσχισης.