Technical Article

Προστασία φύλλου εργασίας XLSX στο Delphi: 15 επιλογές δικαιωμάτων

Δίνετε ένα ολοκληρωμένο βιβλίο εργασίας σε έναν συνάδελφο και του ζητάτε να το φιλτράρει, όχι να το ξαναγράψει. Άρα προστατεύετε το φύλλο. Σε παλαιότερες εκδόσεις του HotXLS, αυτή η κίνηση έγραφε ένα μόνο πράγμα στο αρχείο: <sheetProtection sheet="1" objects="1" scenarios="1"/>, κωδικοποιημένο σταθερά, κάθε φορά. Το φύλλο κλείδωνε, το hash του κωδικού πρόσβασης προσαρτούνταν, και ο χρήστης δεν μπορούσε να κάνει απολύτως τίποτα, ούτε καν την ταξινόμηση και το φιλτράρισμα που θέλατε να μείνουν ανοιχτά. Το ίδιο το παράθυρο διαλόγου του Excel "Protect Sheet" έχει δεκαπέντε πλαίσια ελέγχου ακριβώς γι' αυτόν τον λόγο, και ο μηχανισμός δεν μπορούσε να εκφράσει κανένα από αυτά. Αυτό το κενό είναι που κλείνει το μοντέλο προστασίας v2.91.0

Το HotXLS είναι ένα εγγενές στοιχείο VCL για υπολογιστικά φύλλα για Delphi και C++Builder που διαβάζει και γράφει XLS και XLSX χωρίς εγκατεστημένο το Excel. Αυτό το άρθρο αφορά την πλευρά XLSX της προστασίας φύλλου εργασίας: η νέα TXLSXSheetProtectionOption απαρίθμηση, η AllowOption ιδιότητα που ενεργοποιεί και απενεργοποιεί κάθε δικαίωμα, και ο μοναδικός κανόνας κωδικοποίησης OOXML που μπερδεύει όποιον γράφει ένα <sheetProtection> στοιχείο με το χέρι

Τι πραγματικά προστατεύει η προστασία φύλλου εργασίας

Πρώτα το όριο, επειδή αυτό καθορίζει πόσο πρέπει να εμπιστευτείτε όλα αυτά. Η προστασία φύλλου εργασίας στη μορφή υπολογιστικών φύλλων OOXML (ECMA-376) είναι μια πολιτική αλληλεπίδρασης, όχι κρυπτογράφηση. Λέει σε μια συμβατή εφαρμογή ποιες επεξεργασίες να απορρίπτει όσο το φύλλο είναι προστατευμένο. Οι τιμές των κελιών εξακολουθούν να βρίσκονται στο xl/worksheets/sheetN.xml σε απλό κείμενο· αποσυμπιέστε το .xlsx και είναι ακριβώς εκεί. Ο προαιρετικός κωδικός πρόσβασης αποθηκεύεται ως ένα σύντομο παλαιό hash, όχι ως κλειδί που κρυπτογραφεί κάτι. Όποιος μετονομάσει το αρχείο, ανοίξει το τμήμα και αφαιρέσει τη <sheetProtection> γραμμή διαβάζει και επεξεργάζεται τα πάντα

Άρα η προστασία απαντά στο «σταμάτα τον συνάδελφό μου από το να χαλάσει κατά λάθος έναν τύπο», όχι στο «κράτα αυτά τα δεδομένα μυστικά από κάποιον αποφασισμένο». Αυτά είναι διαφορετικά προβλήματα με διαφορετικά εργαλεία. Αν χρειάζεστε εμπιστευτικότητα, θέλετε την κρυπτογράφηση σε επίπεδο βιβλίου εργασίας που καλύπτεται στο AES-protected XLSX output, το οποίο πράγματι κρυπτογραφεί το πακέτο. Η προστασία φύλλου και η κρυπτογράφηση βιβλίου εργασίας συνδυάζονται καθαρά, αλλά μόνο το δεύτερο είναι κλειδαριά. Κρατήστε αυτή τη γραμμή καθαρή και τα υπόλοιπα αυτής της σελίδας είναι απλώς συνδεσμολογία

Οι δεκαπέντε επιλογές και η ιδιότητα AllowOption

Κάθε φύλλο εργασίας φέρει πλέον ένα σύνολο από TXLSXSheetProtectionOption τιμές που περιγράφουν τι μπορεί ακόμη να κάνει ο χρήστης όσο το φύλλο είναι προστατευμένο. Τα μέλη αντιστοιχούν ένα προς ένα στα χαρακτηριστικά OOXML και στα πλαίσια ελέγχου του διαλόγου του Excel:

  • xlsxSpoEditObjects, xlsxSpoEditScenarios - επεξεργασία σχεδιαστικών αντικειμένων και σεναρίων what-if
  • xlsxSpoFormatCells, xlsxSpoFormatColumns, xlsxSpoFormatRows - αναδιαμόρφωση κελιών, στηλών, γραμμών
  • xlsxSpoInsertColumns, xlsxSpoInsertRows, xlsxSpoInsertHyperlinks - εισαγωγή στηλών, γραμμών, συνδέσμων
  • xlsxSpoDeleteColumns, xlsxSpoDeleteRows - διαγραφή στηλών, γραμμών
  • xlsxSpoSelectLockedCells, xlsxSpoSelectUnlockedCells - μετακίνηση της επιλογής σε κλειδωμένα ή ξεκλείδωτα κελιά
  • xlsxSpoSort, xlsxSpoAutoFilter, xlsxSpoPivotTables - ταξινόμηση περιοχών, χρήση των αναπτυσσόμενων λιστών AutoFilter, εργασία με PivotTables

Διαβάζετε και γράφετε μεμονωμένα bits μέσω της ευρετηριασμένης AllowOption ιδιότητας στο TXLSXWorksheet. AllowOption[Opt] = True σημαίνει ότι η ενέργεια επιτρέπεται· αν το ορίσετε False το απαγορεύει. Ολόκληρο το σύνολο είναι επίσης προσβάσιμο μονομιάς μέσω SheetProtectionOptions, ενός TXLSXSheetProtectionOptions (ένα απλό Pascal set of), ώστε να μπορείτε να το αποθηκεύσετε, να το επαναφέρετε ή να το αντικαταστήσετε ολόκληρο

Η προεπιλογή έχει σημασία και αυτό είναι σκόπιμο: ένα νέο φύλλο εργασίας ξεκινά με κάθε επιλογή επιτρεπτή. Ο constructor αρχικοποιεί SheetProtectionOptions με το πλήρες εύρος, [Low(TXLSXSheetProtectionOption)..High(TXLSXSheetProtectionOption)]. Από εκεί το περιορίζετε αποκλείοντας τις ενέργειες που θέλετε να απαγορεύσετε, αντί να χτίζετε ένα σύνολο δικαιωμάτων από το μηδέν. Αυτή η επιλογή είναι που κάνει τον κανόνα κωδικοποίησης του writer, παρακάτω, να ευθυγραμμίζεται με τη συμπεριφορά του Excel

Προστασία φύλλου αλλά ανοιχτά sort και filter

Ακολουθεί η συνηθισμένη περίπτωση από άκρη σε άκρη: προστατέψτε μια ολοκληρωμένη αναφορά ώστε η διάταξη να μην μπορεί να αναδιαμορφωθεί, αλλά αφήστε τον αναγνώστη να την ταξινομήσει και να τη φιλτράρει. Σημειώστε ότι Protect και οι επιλογές είναι ανεξάρτητες. Protect ενεργοποιεί την προστασία του φύλλου και αποθηκεύει το προαιρετικό hash κωδικού πρόσβασης· δεν αγγίζει το σύνολο επιλογών. Ρυθμίζετε AllowOption ξεχωριστά και οι διακόπτες τίθενται σε ισχύ μόλις το φύλλο προστατευτεί και αποθηκευτεί

var
  wb: TXLSXWorkbook;
  sh: TXLSXWorksheet;
begin
  wb := TXLSXWorkbook.Create;
  try
    sh := wb.Sheets.Add('Protected');
    sh.Cells[1, 1].Value := 'Region'; sh.Cells[1, 2].Value := 'Units';
    sh.Cells[2, 1].Value := 'North';  sh.Cells[2, 2].Value := 120;
    sh.Cells[3, 1].Value := 'South';  sh.Cells[3, 2].Value := 98;

    // Protect with a password. This only sets the protected state + hash;
    // the option set is left at its all-permitted default.
    sh.Protect('HotXLS-2026');

    // Narrow: keep sort + AutoFilter, forbid reshaping and reformatting.
    sh.AllowOption[xlsxSpoSort]          := True;
    sh.AllowOption[xlsxSpoAutoFilter]    := True;
    sh.AllowOption[xlsxSpoFormatCells]   := False;
    sh.AllowOption[xlsxSpoFormatColumns] := False;
    sh.AllowOption[xlsxSpoFormatRows]    := False;
    sh.AllowOption[xlsxSpoInsertRows]    := False;
    sh.AllowOption[xlsxSpoDeleteRows]    := False;

    if wb.SaveAs('protection.xlsx') <> 1 then
      Writeln('SaveAs failed');
  finally
    wb.Free;
  end;
end;

Δύο πράγματα να κρατήσετε από αυτό το απόσπασμα. Οι Sort και AutoFilter γραμμές γράφονται ρητά παρότι και οι δύο προεπιλέγονται σε True; αυτό είναι τεκμηρίωση για τον επόμενο συντηρητή, όχι λειτουργική απαίτηση. Και επειδή οι προεπιλογές είναι επιτρεπτικές, οι μόνες γραμμές που αλλάζουν το αρχείο εξόδου είναι εκείνες που ορίζουν μια επιλογή σε False. Αυτό δεν είναι σύμπτωση αυτού του API, είναι η μορφή μεταφοράς OOXML που αποτυπώνεται, και αυτό είναι η επόμενη ενότητα

Ο κανόνας κωδικοποίησης: παράλειψη σημαίνει επιτρέπεται, attr=0 σημαίνει απαγορεύεται

Αυτό είναι το μοναδικό αντιδιαισθητικό στοιχείο σε όλο το χαρακτηριστικό, και εδώ συνήθως στραβώνει το χειρόγραφο <sheetProtection> συνήθως πάει στραβά. Στο OOXML, κάθε ανά-ενέργεια attribute είναι ένας forbid flag, και η απουσία του σημαίνει άδεια. Ένα γνώρισμα που λείπει σημαίνει ότι η ενέργεια επιτρέπεται. Ένα γνώρισμα γραμμένο ως "0" σημαίνει ότι η ενέργεια απαγορεύεται όσο το φύλλο είναι προστατευμένο. Δεν υπάρχει formatCells="1" σε ένα σωστά διαμορφωμένο αρχείο για να σημαίνει "η μορφοποίηση επιτρέπεται"· απλώς παραλείπετε το γνώρισμα. (Η προεπιλογή για ένα απόν γνώρισμα είναι η προεπιλογή boolean του OOXML, true, και αυτά τα γνωρίσματα ονομάζονται έτσι ώστε το "true" να σημαίνει ότι η αντίστοιχη επεξεργασία επιτρέπεται.)

Ο writer του HotXLS το αντιγράφει ακριβώς. Εκδίδει sheet="1" για να ενεργοποιήσει την προστασία, και μετά διατρέχει το σύνολο επιλογών και γράφει attr="0" μόνο για τις επιλογές που ορίζετε σε False. Οι επιτρεπτές ενέργειες δεν προσθέτουν τίποτα στην έξοδο. Έτσι το workbook από την προηγούμενη ενότητα σειριοποιείται σε κάτι σαν αυτό, μεταφέροντας μόνο τις απαγορευμένες ενέργειες μαζί με το hash του κωδικού πρόσβασης:

// Conceptual output for the snippet above (attributes elided for brevity):
// <sheetProtection sheet="1"
//   formatCells="0" formatColumns="0" formatRows="0"
//   insertRows="0" deleteRows="0"
//   password="...4-hex..."/>
// Note what is NOT there: no sort, no autoFilter, no selectLockedCells.
// Their absence is exactly what tells Excel those actions stay allowed.

Αν προέρχεστε από την παλιά hard-coded συμβολοσειρά και περιμένατε να δείτε κάθε attribute γραμμένο ρητά, αυτό φαίνεται λιτό, σχεδόν λάθος. Είναι σωστό. Ένα αρχείο που ανέφερε sort="1" και autoFilter="1" θα σήμαινε το ίδιο πράγμα σε έναν συμβατό αναγνώστη, αλλά το ίδιο το Excel γράφει τη minimal form μόνο με απαγορεύσεις, και αν την ακολουθείτε κρατάτε τα diffs μικρά και τα round-trips βαρετά. Τα objects και scenarios γνωρίσματα ακολουθούν τον ίδιο ακριβώς κανόνα: είναι επιτρεπτά από προεπιλογή, οπότε εμφανίζονται μόνο ως "0" όταν τα απαγορεύετε, κάτι που είναι το αντίστροφο του παλιού objects="1" scenarios="1" που εκδιδόταν χωρίς όρους

Ανάγνωση προστασίας πίσω: round-trip fidelity

Ένα μοντέλο δικαιωμάτων που μπορείτε να γράψετε αλλά όχι να διαβάσετε είναι μονόδρομος, και το συνηθισμένο σύμπτωμα είναι ένας κύκλος φόρτωσης-επεξεργασίας-αποθήκευσης που διευρύνει σιωπηρά τα δικαιώματα. Το HotXLS το κλείνει αυτό. Όταν ParseWorksheetXml συναντά ένα <sheetProtection> στοιχείο, ορίζει το φύλλο ως προστατευμένο, αποθηκεύει το hash κωδικού πρόσβασης αν υπάρχει και μετά αποκωδικοποιεί κάθε ανά-ενέργεια γνώρισμα πίσω σε AllowOption χρησιμοποιώντας την ίδια λογική αντίστροφα: ένα χαρακτηριστικό που υπάρχει και είναι ίσο με "0" απαγορεύει την ενέργεια; ένα απόν χαρακτηριστικό αφήνει την επιλογή στην επιτρεπόμενη προεπιλογή της

var
  wb: TXLSXWorkbook;
  sh: TXLSXWorksheet;
begin
  wb := TXLSXWorkbook.Create;
  try
    wb.LoadFromFile('protection.xlsx');
    sh := wb.Sheets[1];                  // XLSX sheets are 1-based
    if sh.IsProtected then
    begin
      Writeln('Protected; password hash present: ',
        sh.SheetProtectHash <> '');
      Writeln('Sort allowed:       ', sh.AllowOption[xlsxSpoSort]);
      Writeln('AutoFilter allowed: ', sh.AllowOption[xlsxSpoAutoFilter]);
      Writeln('FormatCells allowed:', sh.AllowOption[xlsxSpoFormatCells]);
    end;
  finally
    wb.Free;
  end;
end;

Φορτώστε το αρχείο που παρήγαγε ο writer και παίρνετε Sort και AutoFilter πίσω ως True, FormatCells ως False - το σύνολο που αποθηκεύσατε, άθικτο. Αυτή η συμμετρία είναι και το ζητούμενο: επεξεργαστείτε ένα κελί σε ένα προστατευμένο, μερικώς επιτρεπόμενο φύλλο και αποθηκεύστε ξανά, και οι δεκατέσσερις άδειες που δεν αγγίξατε επιβιώνουν αντί να επιστρέφουν στο παλιό προεπιλεγμένο μοντέλο όλα ή τίποτα

Πρακτικές σημειώσεις και περιορισμοί

Μερικά πράγματα που αξίζει να γνωρίζετε πριν το εντάξετε σε μια ροή αναφορών:

  • Ο κωδικός πρόσβασης είναι σκόπιμα αδύναμος. Η προστασία φύλλου XLSX αποθηκεύει ένα παλιό hash 16 bit (το ίδιο που χρησιμοποιεί το Excel εδώ και δεκαετίες), διατηρημένο εδώ για λόγους διαλειτουργικότητας. Αποτρέπει τυχαίες αλλαγές; δεν αντιστέκεται σε έναν επιτιθέμενο. Μην το θεωρείτε φύλακα μυστικών. Για πραγματική προστασία, κρυπτογραφήστε το βιβλίο εργασίας.
  • Η ρύθμιση επιλογών πριν από την προστασία είναι απολύτως εντάξει. AllowOption μπορεί να ανατεθεί είτε το φύλλο είναι αυτή τη στιγμή προστατευμένο είτε όχι; οι διακόπτες απλώς περιγράφουν τι θα επιτρέψει η προστασία όταν Protect είναι ενεργή. UnProtect καθαρίζει την προστατευμένη κατάσταση και το hash, αλλά αφήνει το σύνολο επιλογών σας στη θέση του για την επόμενη φορά.
  • Η σημασιολογία των κλειδωμένων κελιών εξακολουθεί να ισχύει. Η προστασία μπλοκάρει μόνο τις αλλαγές σε κελιά των οποίων το Locked χαρακτηριστικό είναι ενεργό (η προεπιλογή του βιβλίου εργασίας). Το να αφήσετε μια περιοχή εισαγωγής επεξεργάσιμη είναι δουλειά του στυλ κελιού, όχι της προστασίας; τα δύο επίπεδα συνδυάζονται όπως ακριβώς στο Excel.
  • Αυτός είναι ο κινητήρας XLSX. Το μοντέλο επιλογών ακολουθεί τα παλαιότερα Allow* properties του κινητήρα XLS, αλλά τα ονόματα enum και property εδώ (xlsxSpo*, AllowOption) ανήκουν στο TXLSXWorksheet στο lxHandleX. Αν επίσης χειρίζεστε τη διάταξη εκτύπωσης στα ίδια φύλλα, ο οδηγός προστασία και διάταξη σελίδας δείχνει πώς αυτές οι ρυθμίσεις συνυπάρχουν με τις περιοχές εκτύπωσης και τις κεφαλίδες, και το επικύρωση δεδομένων, AutoFilter και πίνακες ταιριάζει φυσικά με το να αφήνετε xlsxSpoAutoFilter ανοιχτό σε μια κλειδωμένη αναφορά.

Το λεπτομερές μοντέλο προστασίας και το υπόλοιπο του μηχανισμού ανάγνωσης και εγγραφής XLSX διατίθενται στο HotXLS Component για Delphi και C++Builder; η σελίδα προϊόντος περιλαμβάνει ολόκληρο το API του φύλλου εργασίας, μαζί με την πλήρη αναφορά των επιλογών προστασίας