Την πρώτη φορά που ένας ελεγκτής ζητήσει το ίχνος ελέγχου σας, μάλλον θα εξάγετε ένα CSV από όποιο σύστημα καταγραφής ήδη λειτουργείτε, και θα φαίνεται εντάξει. Χρονικές σφραγίδες, ονόματα χρηστών, ενέργειες, IDs πόρων, όλα με σειρά. Έπειτα κάποιος κάνει τη μόνη ερώτηση που έχει σημασία: πώς ξέρουμε ότι αυτό το αρχείο ταιριάζει με ό,τι πραγματικά συνέβη; Ένα αμετάβλητο ίχνος ελέγχου που κανείς εκτός της ομάδας σας δεν μπορεί να ελέγξει ανεξάρτητα δεν είναι αποδεικτικό στοιχείο. Είναι ένας ισχυρισμός για τη δική σας συμπεριφορά, παραγμένος από συστήματα που ελέγχετε εσείς, σε μια μορφή που επιλέξατε εσείς. Οι ελεγκτές είναι ευγενικοί για αυτό. Οι ρυθμιστικές αρχές γίνονται λιγότερο.

Το «αμετάβλητο» στις περισσότερες στοίβες υποδομής σημαίνει κάτι πιο αδύναμο από ό,τι υποδηλώνει η λέξη. Συνήθως σημαίνει «μόνο για προσθήκη» κατά σύμβαση - η εφαρμογή μόνο εισάγει, και όλοι συμφωνούν να μην τρέξουν ένα UPDATE. Μερικές φορές σημαίνει μια πολιτική διατήρησης σε ένα object store, ή έναν κάδο WORM, που προστατεύει από διαγραφή αλλά δεν λέει τίποτα για το τι γράφτηκε εξαρχής. Όλα αυτά είναι πραγματικοί έλεγχοι. Κανένας δεν επιτρέπει σε τρίτο να εντοπίσει μια αλλαγή εκ των υστέρων. Αυτό το κενό - ανάμεσα στο «κανείς δεν υποτίθεται ότι το επεξεργάζεται» και το «μπορείτε να αποδείξετε ότι κανείς δεν το έκανε» - είναι όλο το θέμα.

Πώς λειτουργεί ένα ίχνος ελέγχου ανθεκτικό σε παραποίηση

Πάρτε κάθε πεδίο μιας εγγραφής log που έχει σημασία - την ενέργεια, ποιος την έκανε, για ποιον οργανισμό ενεργούσε, τον πόρο, τις αλλαγμένες τιμές, το correlation ID, το επίπεδο κινδύνου, τη διεύθυνση του πελάτη - σειριοποιήστε τα με μια σταθερή, συμφωνημένη σειρά, προσθέστε το hash της προηγούμενης εγγραφής, και τρέξτε SHA-256 πάνω στο αποτέλεσμα. Αποθηκεύστε αυτή τη σύνοψη στη γραμμή ως entry_hash, και αποθηκεύστε τη σύνοψη του προκατόχου ως prev_hash. Η πρώτη γραμμή δεν έχει προκάτοχο, οπότε το prev_hash της είναι null. Ό,τι έρχεται μετά είναι κολλημένο σε ό,τι προηγήθηκε.

Η συνέπεια είναι το χρήσιμο κομμάτι. Αλλάξτε έναν μόνο χαρακτήρα σε μια τρίμηνη εγγραφή και το επανυπολογισμένο hash της δεν ταιριάζει πια με αυτό που είναι αποθηκευμένο στη γραμμή. Διαγράψτε μια γραμμή εντελώς και το prev_hash της επόμενης γραμμής δείχνει σε έναν προκάτοχο που δεν υπάρχει. Εισάγετε μια πλαστή εγγραφή και δεν έχει καθόλου έγκυρη θέση στην ακολουθία. Δεν χρειάζεται να εμπιστευτείτε το επίπεδο αποθήκευσης, τον χειριστή, ή τον προμηθευτή. Χρειάζεστε τις εγγραφές, τους κανόνες κατακερματισμού, και ένα script. Αυτό είναι που το κάνει ίχνος ελέγχου ανθεκτικό σε παραποίηση αντί για ένα τακτοποιημένο log.

Να είστε ακριβείς για το τι δεν σας δίνει αυτό. Μια αλυσίδα hash είναι ανθεκτική σε παραποίηση, όχι απρόσβλητη από παραποίηση. Ένας επιτιθέμενος που κατέχει πρόσβαση εγγραφής στη βάση δεδομένων και γνωρίζει τους κανόνες κατακερματισμού μπορεί να ξαναγράψει μια εγγραφή και μετά να επανυπολογίσει κάθε hash μετά από αυτήν, παράγοντας μια αλυσίδα που επαληθεύεται τέλεια. Αυτό που το ματαιώνει είναι η δημοσίευση της κεφαλής της αλυσίδας κάπου που ο επιτιθέμενος δεν ελέγχει: υπογράφοντας το τρέχον entry_hash με ένα κλειδί κρατημένο εκτός της βάσης δεδομένων, εξάγοντάς το, στέλνοντάς το σε έναν ελεγκτή, γράφοντάς το σε ξεχωριστό σύστημα. Οποιοδήποτε από αυτά «παγώνει» την ιστορία μέχρι εκείνη τη στιγμή.

Γιατί ένας μοναδικός writer έχει μεγαλύτερη σημασία απ' όσο νομίζετε

Μια αλυσίδα hash έχει ακριβώς μία κεφαλή, και κάθε νέα εγγραφή πρέπει να τη διαβάσει πριν μπορέσει να συνδεθεί σε αυτήν. Τρέξτε δύο writers πάνω στον ίδιο πίνακα και θα ανταγωνιστούν - και οι δύο διαβάζουν την ίδια κεφαλή, και οι δύο συνδέονται σε αυτήν, και ο ένας από τους δύο είναι τώρα λάθος. Η αλυσίδα αναφέρει τον εαυτό της σπασμένο, και ξοδεύετε μια μέρα ψάχνοντας για έναν επιτιθέμενο που δεν υπάρχει. Το αντιμετωπίζουμε ως αναλλοίωτο αντί για προτίμηση: μία διεργασία καταναλώνει την ουρά ελέγχου και γράφει τον πίνακα, και η ενεργοποίηση δεύτερου writer είναι μια σκόπιμη μετάβαση, όχι ένα κουμπί κλιμάκωσης.

Οι αποτυχίες επαλήθευσης πρέπει επίσης να είναι κατανοητές, γιατί οι χειριστές αντιδρούν στην πρώτη λέξη που διαβάζουν. Ο επαληθευτής μας αναφέρει τρεις διακριτούς λόγους. Το chain_broken σημαίνει ότι η σύνδεση είναι λάθος, ότι το prev_hash δεν ταιριάζει με τον προκάτοχο. Το content_tampered σημαίνει ότι η σύνδεση είναι άθικτη αλλά ο επανυπολογισμός του hash από τα αποθηκευμένα πεδία παράγει κάτι διαφορετικό. Το entry_never_hashed σημαίνει ότι η γραμμή γράφτηκε χωρίς hash καθόλου. Μόνο το δεύτερο είναι αποδεικτικό στοιχείο παραποίησης, και η συμπύκνωση των τριών σε ένα μήνυμα είναι το πώς ξεκινάτε ένα περιστατικό ασφαλείας για ένα bug σε ένα test.

Αυτή η τρίτη περίπτωση δεν είναι υποθετική, και αξίζει να το πούμε για τους εαυτούς μας. Μια χούφτα γραμμές στη δική μας αλυσίδα γράφτηκαν από ένα test που εισήγαγε απευθείας στον πίνακα με raw SQL, παρακάμπτοντας τον writer, αφήνοντας και τις δύο στήλες hash κενές. Τα triggers της βάσης δεδομένων που κάνουν τον πίνακα μόνο-για-προσθήκη αρνούνται διαγραφές όσο και ενημερώσεις, κάτι που σημαίνει ότι αυτές οι γραμμές είναι μόνιμα μη επιδιορθώσιμες. Θα κάθονται στην αλυσίδα για πάντα, και κάθε τρέξιμο επαλήθευσης τις φτάνει. Η διόρθωση ήταν ένας helper που τα tests υποχρεούνται να χρησιμοποιούν, συν ένα guard test που αποτυγχάνει τη στιγμή που κάποιος ξαναφτάσει σε raw SQL.

Τι πρέπει πραγματικά να λάβει ένας ελεγκτής

Το παραδοτέο δεν είναι ένα screenshot ενός dashboard που λέει «η αλυσίδα είναι έγκυρη». Είναι ένα CSV που περιέχει τις ίδιες τις εγγραφές με και τις δύο στήλες hash - id, ενέργεια, δράστη, οργανισμό, εύρος πελάτη, τύπο και ID πόρου, επίπεδο κινδύνου, correlation ID, διεύθυνση, user agent, prev_hash, entry_hash και χρονική σφραγίδα - συν μια γραπτή προδιαγραφή ακριβώς ποια πεδία μπαίνουν στη σύνοψη και με ποια σειρά. Με αυτά τα δύο πράγματα ένας ελεγκτής γράφει είκοσι γραμμές Python και ελέγχει τη δουλειά σας χωρίς να σας ζητήσει τίποτα άλλο. Αυτό είναι αποδεικτικό στοιχείο: κάτι που μπορείτε να παραδώσετε και μετά να χάσετε τον έλεγχό του.

Μια μικρή λεπτομέρεια με πραγματικές συνέπειες: τα εξαγόμενα πεδία ελέγχου είναι strings που επηρεάζονται από τον επιτιθέμενο, και τα spreadsheets θα εκτελέσουν οτιδήποτε ξεκινά με ίσον, συν, πλην ή σύμβολο at. Μια εξαγωγή ελέγχου που ανοίγει ένα shell στο laptop του ελεγκτή είναι ένας αξέχαστος τρόπος να αποτύχετε έναν έλεγχο. Κάθε πεδίο στην εξαγωγή μας γίνεται escape και οι τιμές με πρόθεμα τύπου φόρμουλας εξουδετερώνονται πριν φτάσουν στο αρχείο. Είναι το είδος ελέγχου που μοιάζει με σχολαστικότητα μέχρι το πρώτο άτομο να ανοίξει το πακέτο αποδεικτικών στοιχείων στο Excel.

Κάτω από όλα αυτά, ο ίδιος ο πίνακας πρέπει να αρνείται τις επικίνδυνες λειτουργίες. Το «μόνο για προσθήκη» επιβαλλόμενο σε κώδικα εφαρμογής είναι μια υπόσχεση που επιβιώνει ακριβώς όσο κανείς δεν γράφει ένα migration, ένα script καθαρισμού, ή μια καλοπροαίρετη διόρθωση στις δύο το πρωί. Τα triggers βάσης δεδομένων που απορρίπτουν UPDATE και DELETE στον πίνακα ελέγχου μετατρέπουν αυτή την υπόσχεση σε περιορισμό. Σημαίνει επίσης ότι η εφαρμογή και η αποθήκευση μπορούν να διαφωνήσουν μόνο προς μία κατεύθυνση: η βάση δεδομένων μπορεί να αρνηθεί μια εγγραφή που ήθελε η εφαρμογή, αλλά η εφαρμογή δεν μπορεί ποτέ να ξαναγράψει σιωπηλά τη βάση δεδομένων.

Επαληθεύστε το αμετάβλητο ίχνος ελέγχου σας σε πρόγραμμα

Οι περισσότερες ομάδες που έχουν ένα endpoint επαλήθευσης το καλούν μία φορά, κατά τη διάρκεια της επίδειξης. Αυτός είναι ο λάθος ρυθμός. Ένα σπάσιμο αλυσίδας που ανακαλύπτεται όταν ρωτήσει ο ελεγκτής είναι ένα εγκληματολογικό πρόβλημα που καλύπτει όσους μήνες έχουν περάσει· το ίδιο σπάσιμο ανακαλυπτόμενο από ένα προγραμματισμένο τρέξιμο είναι μια αναφορά σφάλματος με χρονική σφραγίδα. Η επαλήθευση πάνω σε έναν μεγάλο πίνακα πρέπει να είναι χτισμένη για αυτό. Η δική μας διατρέχει τις εγγραφές σε ταξινομημένες σελίδες αντί να φορτώνει τα πάντα στη μνήμη, γιατί ο πίνακας μεγαλώνει με κάθε mutation στην πλατφόρμα, και ένας επαληθευτής που δουλεύει μόνο σε μικρούς πίνακες είναι ένας επαληθευτής που θα σταματήσει να λειτουργεί.

Η άλλη συνήθεια είναι η κάλυψη. Είναι εύκολο να βάλετε την καταγραφή ελέγχου στο HTTP middleware, να παρακολουθήσετε κάθε mutation API να εμφανίζεται στο ίχνος, και να το δηλώσετε ολοκληρωμένο. Έπειτα μια cron εργασία αρχειοθετεί τα δεδομένα ενός πελάτη, ένας consumer ουράς ανακαλεί έναν agent, ένας background worker περιστρέφει ένα credential, και τίποτα από αυτά δεν είναι στο ίχνος, γιατί τίποτα από αυτά δεν ήταν αίτημα HTTP. Αυτές οι διαδρομές πρέπει να καταγράφουν ρητά. Η δυσάρεστη αλήθεια είναι ότι οι ενέργειες που είναι λιγότερο πιθανό να καταγραφούν είναι ακριβώς αυτές που ένας ερευνητής θέλει περισσότερο να δει.

Το NIS2 και ο DORA ωθούν προς την ίδια κατεύθυνση: ελέγχους που μπορείτε να αποδείξετε αντί να ισχυριστείτε. Κανείς δεν πρόκειται να σας δώσει πιστοποιητικό για τη χρήση SHA-256, και δεν ισχυριζόμαστε κάτι τέτοιο. Αλλά όταν κάποιος ρωτήσει ποιος άλλαξε τον κανόνα firewall στις τρεις Μαρτίου, υπάρχει μεγάλη διαφορά ανάμεσα σε ένα αποτέλεσμα ερωτήματος και ένα αποτέλεσμα ερωτήματος του οποίου την ακεραιότητα μπορεί να ελέγξει ένας άγνωστος. Τρεις ερωτήσεις που αξίζει να κάνετε σε όποιον σας πουλά ένα αμετάβλητο ίχνος ελέγχου: μπορώ να εξάγω τα hashes, μπορώ να τα υπολογίσω ξανά χωρίς το λογισμικό σας, και τι συμβαίνει όταν η αλυσίδα αναφέρει τον εαυτό της σπασμένο;