When we discussed the Principle of Accountability, we observed three kinds of mechanisms for accountability: authorization, authentication, and audit. We now take a deeper look at audit—mechanisms that record and review actions and the principals who take them.
There are two main tasks that are necessary for audit:
Audit has several uses in computer security:
In theory, a system could log every single instruction it executes, input it receives, and output it produces. Such a log would contain sufficient information but would be exceedingly difficult to review and might be difficult—if not impossible, in the face of continuous streams of big data—to implement. So the art of determining what to log depends on striking a balance between recording too much information and too little.
Logging states means taking a snapshot of all the key data of a system, possibly including data in memory, data on disk, and data in transit on the network. With this kind of log, it becomes possible to recover when a system fails because of a power failure, crash, or attack. Taking a consistent snapshot of a distributed system can be difficult. (Take CS 5414 to learn more.)
For security purposes, though, it's more common to log events. Any action taken by the system or by a user of the system is entered into the log. For example, the Windows security log records events corresponding to account login, access to operating system resources, change to security policies, and exercise of heightened user privileges. With this kind of log, it becomes possible to review what actions were taken and whether those might be the cause of security policy violations.
Recall that a security requirement is a constraint on a functional requirement that specifies what should happen in a particular situation to accomplish a security goal. Also, recall that security requirements should be testable.
Many security requirements therefore specify a condition that must be satisfied for an action to be permitted. For example (continued from the requirements lecture), given functional requirement "allowing people to cash checks" and security goal "prevent loss of revenue through bad checks", we invented two security requirements:
Both requirements stipulate a testable condition that suffices for the action "check cashing" to be permitted.
Such conditions are exactly the kind of information that should be logged for later review, because they identify why the system believed that an action was (or was not) authorized. Likewise, any requirements related to authentication identify information that should be logged, because they identify why a system believed that an action should (or should not) be associated with a principal.
More generally, any event associated with authorization or authentication is a natural candidate for being logged. If you identify an event worth being logged that does not correspond to a security requirement, ask yourself whether you might be missing a requirement.
The Orange Book stipulates the following minimal (level C2) logging requirements:
The TCB shall be able to record the following types of events: use of identification and authentication mechanisms, introduction of objects into a user's address space (e.g., file open, program initiation), deletion of objects, and actions taken by computer operators and system administrators and/or system security officers, and other security relevant events. For each recorded event, the audit record shall identify: date and time of the event, user, type of event, and success or failure of the event. For identification/authentication events the origin of request (e.g., terminal ID) shall be included in the audit record. For events that introduce an object into a user's address space and for object deletion events the audit record shall include the name of the object. The [system] administrator shall be able to selectively audit the actions of any one or more users based on individual identity.
Some information might be necessary to log for the sake of accountability, yet impossible to log for the sake of confidentiality. For example, including plaintext passwords in a log might seem desirable (to review the kind of passwords that attackers guess during an online attack), but doing so would violate a security policy requiring passwords never be stored in plaintext (a good policy). Real systems usually make the choice to preserve confidentiality at the expense of limited review capability, hence do not store plaintext passwords in a log.
In other cases, it might be reasonable to store confidential information in a log but sanitize the log before releasing it for review. Sanitization might occur before or after information is written to the log, depending on whether system administrators are trusted with log contents.
A sanitizer might simply delete information from log entries, thus preventing reconstruction of that information in the future. Or the sanitizer might provide a means to recover that information. For example, the sanitizer could replace usernames with pseudonyms, and keep a separate file that maps pseudonyms back to usernames. If that file is stored in plaintext on the same system, administrators will be able to reconstruct the unsanitized log. If administrators are not trusted with log contents, some stronger mechanism (e.g., storage under a public key whose private key is secret shared amongst a group of administrators) is needed to store the pseudonym map.
Log entries should be unambiguous. A good entry contains enough context to determine what the information in the log entry means. Timestamps are often included in an entry, but bear in mind that a timestamp is only as trustworthy as the clock at the host.
Logs typically have some size limit, even if only the free space on disk. What should a system do if the log overflows? There are generally two choices:
Both choices introduce new availability vulnerabilities. A third choice is to simply stop logging, but this is probably the least acceptable option.
There is no widespread agreement on standard formats for logs—not even whether logs should be kept in binary or in text format. Unfortunately, this makes correlation between logs difficult.
One standard format for logging is syslog, which originates from the Unix sendmail system. Each syslog entry is a plain text string containing at least the following fields:
Logs generated locally at a host might need to be transferred to log servers that aggregate and manage logs on behalf of an organization. Issues of concern here include
Audit data must be protected from modification and destruction. Good practices include:
There are limits to how tamperproof a log can be made. Once an attacker has gained control over a host, no log on that host is completely safe from being read, modified or deleted. Standard cryptographic techniques typically fall short, because there is nowhere to store keys that the attacker cannot access through the normal logging facility.
It is possible, however, to protect log entries made before the attacker compromises the host:
By a technique called iterated hashing it's also possible to protect logs on a host that has only periodic access to the network, such that attackers who compromise the host cannot read, modify, nor insert past log entries. (Deletion of some log entries remains possible, though.) An iterated hash is a successive hash H(...H(H(H(v)) of a value v. Schneier and Kelsey (1999) describe a tamperproof log protocol based on iterated hashing, which we summarize in a highly simplified form, next.
Machine M maintains a local log that it periodically synchs over the network to a trustworthy log server. In addition to the log, M maintains an authentication key ak that is used to authenticate log entries.
To initialize the protocol, M must secretly communicate the initial value of ak to the log server. The key must be randomly generated.
To record message m in its log, M does the following:
- ek = H("encrypt", ak) // hash authentication key to derive encryption key for this entry
- c = Enc(m; ek) // encrypt the message in this entry
- erase ek // forget the encryption key
- t = MAC(c; ak) // create a tag for the new link
- entry = c, t // the log entry contains the encrypted message and the tag
- record entry in log
- ak = H("iterate", ak) // iterated hash of authentication key to derive new authentication key for the next entry
If an attacker gains control of M, he learns the current authentication key. But because the hash function is one-way, the attacker cannot determine any of the previous authentication keys. Thus he cannot replace any previous entries with a forged entry, or insert a new entry. Further, since the encryption key for each entry is derived from the authentication key, he cannot determine any of the previous encryption keys, hence cannot read any of the previous entries. The attacker could successfully truncate the log, thus deleting messages. (But any messages previously synched to the log server will not be lost.) The attacker could not truncate then add new messages—again because he does not know the previous authentication keys. The attacker could also successfully add new messages to the log, but there's nothing that can be done to defend against that, since the attacker controls the host.
Logging is pointless unless someone reviews the log. Auditors might review logs manually or with automated tools. An automated analyzer automatically processes a log and, if configured to do so, notifies another system component of an event or problem. For example, the analyzer might cause a system administrator to be paged. Manual analysis is performed by using tools to extract information from a log file. Such tools might range from simple grep to sophisticated visual analysis engines. Building graphical tools is a human–computer interaction problem: how can the tool enable the human to easily formulate hypotheses and deduce consequences?