UC San Diego UC San Diego Previously Published Works
Total Page:16
File Type:pdf, Size:1020Kb
UC San Diego UC San Diego Previously Published Works Title Liquid information flow control Permalink https://escholarship.org/uc/item/0t20j69d Journal Proceedings of the ACM on Programming Languages, 4(ICFP) ISSN 2475-1421 Authors Polikarpova, Nadia Stefan, Deian Yang, Jean et al. Publication Date 2020-08-02 DOI 10.1145/3408987 Peer reviewed eScholarship.org Powered by the California Digital Library University of California Type-Driven Repair for Information Flow Security Nadia Polikarpova Jean Yang Shachar Itzhaky Massachusetts Institute of Technology Carnegie Mellon University Armando Solar-Lezama [email protected] [email protected] Massachusetts Institute of Technology shachari,[email protected] Abstract We present LIFTY, a language that uses type-driven program repair to enforce information flow policies. In LIFTY, the programmer Policies specifies a policy by annotating the source of sensitive data with a refinement type, and the system automatically inserts access checks Program Program Verify necessary to enforce this policy across the code. This is a significant with Repair with Program improvement over current practice, where programmers manually holes checks implement access checks, and any missing check can cause an information leak. To support this programming model, we have developed (1) an encoding of information flow security in terms of decidable refine- ment types that enables fully automatic verification and (2) a pro- gram repair algorithm that localizes unsafe accesses to sensitive data and replaces them with provably secure alternatives. We for- ✓ ✗ malize the encoding and prove its noninterference guarantee. Our experience using LIFTY to implement a conference management Specification Core program Program hole system shows that it decreases policy burden and is able to effi- ciently synthesize all necessary access checks, including those re- Policy check Tool component quired to prevent a set of reported real-world information leaks. Figure 1. Type-driven repair for policy-agnostic programs. 1. Introduction Programs that compute over sensitive data are becoming more com- programmer burden. In prior work on policy-agnostic program- plex. In addition to directly displaying sensitive values, applications ming [7, 52, 53], the programmer implements each information often support functionality such as search and aggregation. To pro- flow policy once, associated with data definitions, rather than as tect users’ privacy and prevent information from being disclosed to checks across the program. The remainder of the program may be unauthorized users, programmers must implement access checks free of checks and the language runtime is responsible for steer- across the program. Any missing check may result in an informa- ing dynamic behavior to adhere to policies. While this program- tion leak. ming model makes it impossible to leak information through miss- Traditional approaches to information flow control can detect ing checks, the dynamic solution unfortunately involves potentially leaks that result from missing access checks, but the amount of pro- prohibitive overheads and unpredictable runtime behavior. grammer effort required to write secure code remains high. Tech- We take a static approach to avoid the pitfalls of runtime tech- niques for language-based information flow security [43] statically niques for policy-agnostic programming. Generating information verify the absence of such leaks, but they require the program- flow checks seems like a perfect application for program synthesis, arXiv:1607.03445v1 [cs.PL] 12 Jul 2016 mer to first correctly implement information flow checks across the as what we want is to synthesize small code snippets corresponding program and then additionally provide specifications of permitted to policy checks. To make the synthesis problem tractable, however, flows. While dynamic approaches [9, 27, 54] decrease the anno- we need to be able to synthesize each check independently. Thus, tation burden, the programmer remains responsible for correctly we need a way to decompose the global repair problem into local implementing checks in order to avoid exceptions or silent failures. synthesis problems. This is challenging because information flow We take a policy-agnostic approach [7, 52], factoring out in- checks may depend on both sources and sinks for sensitive data formation flow policies from the rest of the program to mitigate and there may be complex computations in between. To address these challenges, we propose a type-driven solu- tion for policy-agnostic programming. At the core of our tech- nique is the insight that we can use refinement types—types dec- orated with decidable predicates [41, 49]—to statically enforce a policy-compliant semantics. Refinement types have the advantages that(1) they support expressive policies, (2) type-checking is de- cidable, and (3) type inference can provide error localization while reducing annotation burden. In our solution, the programmer imple- ments each information flow policy only once, as a type annotation, [Copyright notice will appear here once ’preprint’ option is removed.] rather than as repeated checks across the program; the type checker 1 2016/7/13 is responsible for identifying unsafe flows, and a subsequent repair phase prevents these flows by automatically inserting conditional showPaper w pid = expressions implementing the policy checks into the code. The ap- let u = getCurrentUser w out = do title getPaperTitle w pid proach is fully static and requires no runtime analysis. authors getPaperAuthors w pid In this paper, we present(1) a policy-agnostic, security-typed return (title + + ": " + + show authors) language called LIFTY (Liquid Information Flow TYpes) and (2) a in print w u out compiler that repairs LIFTY programs by inserting information flow checks. In Fig. 1 we show an overview of LIFTY and its com- Figure 2. Excerpt from a conference management server code. pilation process. In LIFTY, the programmer specifies information flow policies as refinement type associated with sources of sensi- IFTY tive data. L ’s verifier uses type inference techniques inspired and reviewers. The function showPaper in Fig. 2 gets as argument a by liquid types [41] to produce a program with holes, where each paper ID pid, retrieves the paper’s title and author information from hole corresponds to an unsafe data access paired with a local pol- the persistent store, and displays it to the current user1. Note that the icy specification. The repair phase builds on an existing type-based function has both read effects (retrieval from the store) and write program synthesis technique [39] to produce a policy check for effects (output to user). For simplicity, we capture these effects by each hole. We formalize a core language for LIFTY, prove a non- propagating a single additional argument w (of type World) through interference property, and demonstrate the practical promise of our the code. We assume that a World value encapsulates both the state approach using an implementation of a LIFTY-to-Haskell compiler. of the persistent store and the observations made by the users. This paper makes the following contributions: The body of showPaper accesses potentially sensitive data • Static, type-based approach to policy-agnostic program- using accessor functions getCurrentUser, getPaperTitle, and ming. We present a programming model that supports the im- getPaperAuthors. For example, implementing double-blind review plementation of information flow policies as refinement types, requires hiding the list of paper authors from ordinary reviewers separately from other functionality. The compiler, rather than and making it visible only to the program chair. One way to en- the programmer, becomes responsible for generating code to force this policy using conventional programming paradigms, is to implement the policies. guard the call to getPaperAuthors by a conditional that checks if the session user is allowed to see the author list, and alternatively • Verification of expressive type-based policies. In order to sup- returns a constant default value. Note that the check is specific not port policy-agnostic programming, we developed a verification only to the data element being read, but also to the eventual viewer technique for information flow security based on liquid type in- of the result. (In this case it happens to be the session user, but this ference. The technique is sound, has minimal annotation over- is not always the case.) Because of this, the problem of checking head, and supports expressive policies and program constructs cannot be solved simply by delegating the checks to accessor func- (such as recursion and higher-order functions). tions. Instead, a potentially different access check has to appear in • Formalization and proof of security guarantee. We formalize every computation that involves getPaperAuthors, which quickly our verification approach in terms of refinement types [41, 49] becomes tedious and error-prone. and prove a non-interference property. Our proof technique uses LIFTY obviates the need for writing policy checks: as long as a phantom encoding to reduce the problem of proving non- the programmer correctly annotates getPaperAuthors with the de- interference to the problem of proving type safety, within the sired policy, the compiler will automatically guard each invocation same language. of this function with an appropriate policy check. To this end, the compiler has to propagate the information about where a sensitive • Multistage program