3

I have a simple app that posts simple data as statuses to FB. Unfortunately, I have found that FB does not sanitize the data coming in from their API, or going out of their API. To deal with this, briefly, the answer is to sanitize responses from their API.

In short, what I would like is indicated on line 36 in the following code snippet. (As an example test, once you're logged into FB and have given this app permission to post on your behalf, post the string "<iframe src="http://www.google.com"></iframe>" by pressing enter. This will show up sanitized on your wall within FB, but an iframe will appear in this simple page).

<!DOCTYPE html>
<html>
<head>
    <title>Sanitize my FB Data</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"
        type="text/javascript"></script>
    <script src="../../lib/jquery.min.js" type="text/javascript"></script>
</head>
<body>
<input id="fbPost" type="text">

<div id="fb-root"></div>
<script>
window.fbAsyncInit = function () {
    FB.init({
        appId      : "my_app_id",
        status     : true, 
        cookie     : true,
        xfbml      : true,
        oauth      : true
    });
    $("#fbPost").keyup(function (event) {
        if (event.keyCode == 13) {
            var data = {
                message: $("#fbPost").val()
            };
            FB.api('/me/feed', 'post', data, function (resp) {
                var _index = resp.id.indexOf("_");
                var postId = resp.id.substr(_index + 1);
                FB.api('/' + postId, function (postData) {
                    // This line is insecure!
                    $("body").prepend(postData.message);

                    // Would like: sanitize(postData.message)
                });
            });
        }
    });
};
(function(d){
    var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
    js = d.createElement('script'); js.id = id; js.async = true;
    js.src = "//connect.facebook.net/en_US/all.js";
    d.getElementsByTagName('head')[0].appendChild(js);
}(document));
</script>
<div class="fb-login-button" 
    scope="read_stream,publish_stream">
    Login with Facebook
</div>
</body>
</html>

What I would like to do is, in short, sanitize data in JavaScript. For an app that uses only the JavaScript SDK to read/write FB data, and would prefer remaining hands off to FB user information entirely on the server, this is an obvious problem.

I've checked out this answer, using the internal sanitizer in Caja, but unfortunately, it requires use of the 'html4' global object. I'm unaware of any other input sanitizers in JavaScript.

So: are there any comprehensive input sanitizers available in JavaScript today?

Community
  • 1
  • 1
wyantb
  • 101
  • 1
  • 7
  • What exactly do you want the sanitizing to do? Would turning `` into `<iframe src="http://www.google.com"></iframe>` suffice? It sure sounds that way. – Matt Ball Apr 19 '12 at 03:40
  • Yes, it would, as far as I'm aware. – wyantb Apr 19 '12 at 03:46
  • http://stackoverflow.com/search?q=javascript+escape+HTML+entities – Matt Ball Apr 19 '12 at 04:01
  • First result just manages < and >. Second result is a different problem. Third result is certainly more promising - but if possible, I'd prefer not to build up an entity map of 8000 customized replacements. Fourth result at least has an expanded lexicon. Etc. Yes, the trivial exploit I posted above can work for simple iframes, or simple alert scripts. That's no guarantee to me that the code is protected against more sophisticated exploits. – wyantb Apr 19 '12 at 04:09

2 Answers2

1

Add this function to your script:

function htmlEscape(str) {
    return String(str)
            .replace(/&/g, '&amp;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');
}

Then use:

$("body").prepend(htmlEscape(postData.message));
Community
  • 1
  • 1
SilverlightFox
  • 32,436
  • 11
  • 76
  • 145
0

All that's needed here is to do HTML entity encoding on the data that's being received back. The function in the accepted answer would work, and if you're already using Underscore, the _.escape function provides the same.

wyantb
  • 101
  • 1
  • 7