NSTP Content Security

Submission Deadline:

The goals of this assignment are to:

  1. Implement a simple client-side execution environment
  2. Isolate untrusted code from the local system
  3. Separate code and data from untrusted origins

Overview

In this assignment, we will switch focus from network servers to clients by examining security issues that arise in modern networked application execution environments—such as web browsers—when executing untrusted code from multiple origins.

No changes to the NSTPv4 protocol have been made for this assignment. However, an open-source reference implementation of an NSTPv4 client is available to build upon for this assignment.1 In addition, a binary-only NSTPv4 server implementation is available as a container image that can be pulled from gcr.io/netsec/assignments/04-content_security-server:latest. You can use this image to run servers and a status server, assuming a shared directory called data:

# Create a dedicated virtual network so we can assign IP addresses
$ docker network create --subnet 10.1.1.0/24 nstp

# Create an NSTP server at 10.1.1.10
$ docker run -itd \
    --name nstpd-00 \
    --network nstp \
    --ip 10.1.1.10 \
    -v $(pwd)/data:/data \
    gcr.io/netsec/assignments/04-content_security-server \
    nstp_server \
        --protocol_version 4 \
        -u /data/password_hashes \
        -t /data/trusted_certs.db \
        -c /data/server.crt \
        -k /data/server.key

# Create another NSTP server at 10.1.1.11
$ docker run -itd \
    --name nstpd-01 \
    --network nstp \
    --ip 10.1.1.11 \
    -v $(pwd)/data:/data \
    gcr.io/netsec/assignments/04-content_security-server \
    nstp_server \
        --protocol_version 4 \
        -u /data/password_hashes \
        -t /data/trusted_certs.db \
        -c /data/server.crt \
        -k /data/server.key

# Create an NSTP status server at 10.1.1.101
$ docker run -itd \
    --name nstp-statusd \
    --network nstp \
    --ip 10.1.1.101 \
    -v $(pwd)/data:/data \
    gcr.io/netsec/assignments/04-content_security-server \
    nstp_status_server \
        -s /data/status.crt \
        -k /data/status.key \
        -v /data/server.crt \
        -v /data/client.crt

See the usage output using --help for more information.

Client-side Code

In this assignment, NSTP clients distinguish between types of values stored at NSTP servers. In particular, clients consider keys with a suffix of .js to refer to JavaScript programs that can execute in an isolated client environment. Interpretation of all other content is left to a script’s discretion.

The client-side execution environment consists of an isolated JavaScript interpreter context where the context is labeled with the origin of the executed program. An origin in this context is the two-tuple consisting of the remote host or IP address and port the content was loaded from. Code must respect the same-origin policy as understood in the context of web security. That is, code must not be able to access code or data labeled with a different origin. In addition, code must not affect the underlying system except as needed to implement the functionality described below.

You may use any suitable JavaScript implementation for your chosen development language and tools; pyduktape is suggested for the reference implementation.

The environment must provide one global variable, document. This variable must present an API with the following properties and functions.

document.client

client is a JavaScript object that provides several values.

  • userAgent: A string containing the name of the client user agent
  • majorVersion: An integer indicating the client major version
  • minorVersion: An integer indicating the client minor version

document.log(message)

log is a function that prints message to the client “console,” i.e., to stdout.

document.remoteLoad(nstpUrl)

remoteLoad is a function that takes an NSTP URL string and returns the contents of that remote resource, or else an exception if an error occurred. NSTP URLs take the following form:

nstp://${remote_host}:${remote_port}/${key}

where remote_host is the domain name or IP address where the NSTP server is running, remote_port is the TCP port it is bound to, and key is the key to return.

For example, executing:

var value = document.remoteLoad("nstp://127.0.0.1:22300/example");

would store the contents of the key example at the NSTP server running at 127.0.0.1 on TCP port 22300 in the variable value. remoteLoad only operates in the public keyspace.

If remoteLoad accesses another JavaScript resource, it must automatically be executed in the current context prior to returning control to the calling script.

document.remoteStore(nstpUrl, value)

remoteStore is a function that takes an NSTP URL string that refers to a remote resource and a value as a byte array. The function stores that value at the remote resource indicated by the URL, or else throws an exception if the store fails.

As with remoteLoad, this function only operates in the public keyspace.

document.localStorage

localStorage is a special JavaScript object that presents an interface to durable origin-specific data. That is, scripts can store arbitrary JavaScript data as properties on localStorage that (a) persist across script executions and (b) are only accessible to code from that origin. For example:

// Loaded from origin A.
document.localStorage.x = "hi!";

// Loaded in a separate context from origin B != A.
document.localStorage.x = "hello!";

// Loaded in third context from origin A, this prints "x = hi!".  But, if
// loaded from origin B, this prints "x = hello!".
document.log("x = " + document.localStorage.x);

Evaluation Criteria

Your implementation will be evaluated to determine whether it correctly implements the execution environment and document API. In particular, your implementation will be tested against the origin isolation security properties described above.

Your submission must contain a Dockerfile in the repository root that builds a container for your client when the following sequence of commands is performed:

$ git clone ${your_repo_url} submission
$ cd submission
$ docker build -t submission .

Your server should be invoked by default when running the resulting container image (use the ENTRYPOINT Dockerfile directive). Grading of your solution will take place using an automated test suite that will expect your client to use the following command-line interface to contact, authenticate to, load, and evaluate a key from an NSTP server:

$ docker run -d -v ${shared_dir}:${shared_dir} submission \
    -t ${shared_dir}/${trust_store} \
    -c ${shared_dir}/${client_certificate} \
    -p ${shared_dir}/${client_private_key} \
    -s ${server_address} \          # e.g., 127.0.0.1:22300
    -v ${status_server_address} \   # e.g., 127.0.0.1:22301
    -k ${nstp_key}

Submission Instructions

Push the source code for your server to a git repository on the project server at ~/assignments/04-content_security.

# On the project server
$ mkdir -p ~/assignments/04-content_security
$ cd ~/assignments/04-content_security
$ git init --bare

# On your development machine
$ cd ${repo_path}
$ git remote add submission ${user}@netsec.seclab.ccs.neu.edu:assignments/04-content_security
$ git push -u submission --all
$ git push -u submission --tags

You may tag the commit you wish to be considered for grading with the tag solution; otherwise, the latest commit on master will be treated as such.


  1. The client is written in Python 3 using asyncio. Its use is optional; feel free to build upon your own implementation if desired.↩︎