CS 4440 Wiki:
BUNGLE Site Documentation


A startup named BUNGLE is about to launch its first product—a web search engine—but their investors are nervous about security problems. The site is written in Python using the Bottle web framework. Although Bottle has built-in mechanisms that help guard against some common vulnerabilities, the Bunglers have circumvented or ignored these mechanisms in several places. In addition to providing search results, the site accepts logins and tracks users’ search histories. Usernames, passwords, and search histories are stored in a MySQL database.

Site URLs

The website repies to five main URLs: /, /search, /login, /logout, and /create.

Main Page (/)

The main page accepts GET requests and displays a search form. When submitted, this form issues a GET request to /search, sending the search string as parameter q (i.e., /search?q=HelloWorld]).

If no user is logged in, the main page also displays a form that gives the user the option of logging in or creating an account. The form issues POST requests to /login and /create.

The search results page accepts GET requests and prints the search string, supplied in the q query parameter, along with the search results. If the user is logged in, the page also displays the user’s recent search history in a sidebar.

Note: Since actual search is not relevant to this project, you might not receive any results.

Login Handler (/login)

The login handler accepts POST requests and takes plaintext username and password query parameters. It checks the user database to see if a user with those credentials exists. If so, it sets a login cookie and redirects the browser to the main page. The cookie tracks which user is logged in; manipulating or forging it is not part of this project.

Logout Handler (/logout)

The logout handler accepts POST requests. It deletes the login cookie (if one is set) and redirects the browser to the main page.

Account Creation Handler (/create)

The create account handler accepts POST requests and receives plaintext username and password query parameters. It inserts the username and password into the database of users, unless a user with that username already exists. It then logs the user in and redirects the browser to the main page.

Note: The password is neither sent nor stored securely; however, none of the attacks you implement should depend on this behavior. You should choose a password that other groups will not guess, but never use an important password (e.g., your UID password) to test an insecure site!

Defenses

The following shows the source code for every CSRF and XSS defense level available in the site.

CSRF Level 0 (no defense)

class CSRFNone(object):
    @staticmethod
    def init(request, response):
        return None
    @staticmethod
    def formHTML(token):
        return ""
    @staticmethod
    def validate(request, token):
        pass

CSRF Level 1 (token validation)

class CSRFToken(object):
    @staticmethod
    def init(request, response):
        token = request.get_cookie("csrf_token")
        if token is None:
            token = os.urandom(16).encode('hex')
            response.set_cookie("csrf_token", token)
        return token
    @staticmethod
    def formHTML(token):
        return ""
    @staticmethod
    def validate(request, token):
        if request.forms.get('csrf_token') != token:
            raise HTTPError(403, output="CSRF Attack Detected (bad or missing token)")    

XSS Level 0 (no defense)

class XSSNone(object):
    @staticmethod
    def init(response):
        response.set_header("X-XSS-Protection", "0");           
    @staticmethod
    def filter(input):
        return input

XSS Level 1 (remove script tags)

class XSSRemoveScript(object):
    @staticmethod
    def init(response):
        response.set_header("X-XSS-Protection", "0");           
    @staticmethod
    def filter(input):
        return re.sub(r"(?i)script", "", input)

XSS Level 2 (remove several tags)

class XSSRemoveSeveralTags(object):
    @staticmethod
    def init(response):
        response.set_header("X-XSS-Protection", "0");           
    @staticmethod
    def filter(input):
        return re.sub(r"(?i)script|<img|<body|<style|<meta|<embed|<object", "", input)

XSS Level 3 (remove ", ', and ;)

class XSSRemovePunctuation(object):
    @staticmethod
    def init(response):
        response.set_header("X-XSS-Protection", "0");           
    @staticmethod
    def filter(input):
        return re.sub(r"[;'\"]", "", input)

XSS Level 4 (encode < and >)

class XSSEncodeAngles(object):
    @staticmethod
    def init(response):
        response.set_header("X-XSS-Protection", "0");
    @staticmethod
    def filter(input):
        return input.replace("<", "<").replace(">", ">")