cloudant_使用Cloudant代理授权

cloudant

SQL数据库具有细粒度的访问控制。 (有关示例,请参阅IBM Knowledge Center中标题为“ 行和列访问控制 ”的文章。)相比之下,Cloudant®数据库中的用户可以具有对整个数据库的读取和/或写入权限,也可以不具有对整个数据库的读取权限。所有。 此限制消除了对数据库中信息的保护措施之一,并且需要应用程序程序员更多的信任。

在本教程中,您将学习如何在Node.js中创建Cloudant代理。 因为您编写了该代理的代码,所以可以将任何所需的安全检查放入其中。

构建应用程序所需的条件

  • IBM Cloud,Node.js,Cloudant和JavaScript的基础知识
  • 一个IBM Cloud Lite帐户(免费!)

运行应用程序 获取代码

样例应用

该示例应用程序是一个银行帐户系统。 有三个帐户:爱丽丝,比尔和颂歌。 每个帐户都有一定的余额。 期望的行为是允许用户相互进行转账,而不是自己进行转账。 通过使用Cloudant和OpenWhisk实现此应用程序。 要学习如何编写这样的应用程序,请参阅“ 为连接的环境构建智能锁 ”的第1、2、4和5部分。 您还可以访问与该教程关联的源代码

请注意,这种类型的策略明确禁止某些操作并隐含地允许其他所有操作,在这里只能接受,因为应用程序是如此简单。 在生产系统中,最佳实践是拥有一个策略,该策略明确允许允许的操作,并隐式拒绝其他所有操作。

步骤1.捕获并中继HTTPS流量

由于某些参数编码在路径名中,因此代理无法作为OpenWhisk API应用程序运行-因此,我选择将其编写为Cloud Foundry Node.js应用程序。 我在文章“ 将您的授权代理添加到第三方应用程序 ”中描述了执行此操作的方法。

该文章中描述的用例与本文中讨论的用例之间存在一个重要区别。 Cloudant使用HTTP基本身份验证。 这意味着身份验证信息是客户端发送到代理的标头的一部分。 在将请求发送到服务器之前,需要从标头中删除该信息:

headers["authorization"] = null;

创建代理请求时,请添加服务器的身份验证信息。 添加的行是第7-10行。

// The options that go in the HTTP header
	var proxiedReqOpts = {
	      host: cloudantCred.host,
	      path: req.path + query,
	      method: req.method,
	      headers: headers,
	      auth: {
	      	type: "basic",
	      	username: cloudantCred.username,
	      	password: cloudantCred.password,	      	
	      }
	};

授权的最简单方法是使用单独的中间件调用。 使用获取HTTP请求的函数,例如app.all("*", function(req, res, next) { … }); 。 第三个参数(此处称为next )是用于调用以返回请求以进行进一步处理的函数。

// The authorization logic
app.all("*", /* @callback */ function(req, res, next) {
.
.
.
	next();
});

如果请求未经授权,则将响应代码设置为401,然后使用Unauthorized响应。

if (   unauthorized   ) {
		res.status(401).send('Unauthorized');
		return ; // No need to continue this function
	}

步骤2.获取相关字段

下一步是识别请求中的字段以做出授权决策。

记录中

拥有一个临时日志以查看请求以及代码如何对其进行解析非常有用。 为此,请有一个日志字符串并在询问时显示它:

var log = "";
app.get("/log", /* @callback */ function(req, res) {
	res.send(log);	
});

任何时候要将日志添加到日志中,都可以将其添加为HTML。 例如,用一行分隔不同的请求很有用:

log += "<hr />";

用户名和密码

用户和密码可用作HTTP标头中的授权参数。 提供它们的方式有些复杂,但是此代码检索它们:

if (req.headers.authorized !== null) {
		var origAuth = new Buffer(req.headers.authorization.replace("Basic ", ""), 'base64').toString('ascii');  	
		var arr = origAuth.split(":");
		user = arr[0];
		password = arr[1];
	}

请求字段

log += "<h2>User: " + user + "</h2>";
	log += req.method + " " + req.path;
	if (req.body !== undefined)
		log += "<h4>Body:</h4>" + req.body;

查看日志 -这些是单个事务的结果:

记录结果

如您所见,应用程序首先读取一个用户的帐户,更新该用户的余额,然后为另一用户重复。 对于GET请求,用户ID在路径中。 对于POST请求,它作为_id属性存在于JSON主体中。

以下代码获取这两种方法的ID和余额(如果有)。 它使用switch构造,并在路径或主体中查找信息。

switch (req.method) {
		case "GET":
			id = req.path.replace(/\/.+\//, "");
			break;
		case "POST":	
			var reqBody = JSON.parse(req.body);
			id = reqBody._id;
			balance = reqBody.balance;
			break;
	}

语境

您要防止用户增加自己的银行余额。 但是,您无法从GETPOST请求中获取该信息。 在POST更改现有余额之前,您需要先了解现有余额。 有两种找出余额的方法:

  • 当您收到POST以修改帐户余额时,从代理提交GET请求。
  • 跟踪作为对GET请求的响应返回到应用程序的余额。 无需查看POST请求,因为任何更新余额的请求都将以GET 。 (请参阅“ 文档版本控制和MVCC” 。)

如果一次仅运行一个代理实例,则第二种方法效率更高。 为此,创建一个空的哈希表作为全局变量:

var knownBalance = {};

在将响应返回给应用程序的代码中,检查是否存在正在报告的余额,如果有,则更新哈希表。 添加的行是第4–9行。

var proxiedReq = http.request(proxiedReqOpts, function(proxiedRes) {		
		proxiedRes.on("data", function(chunk) {retVal += chunk;});
		proxiedRes.on("end", function() {
			var acctInfo = JSON.parse(retVal);
			
			// If we know about a user 
			if (acctInfo._id !== undefined) {
				knownBalance[acctInfo._id] = acctInfo.balance;
			}
			
			res.send(retVal);
		});
		proxiedRes.on("error", function(err) {res.send(JSON.stringify(err) + "<hr />" + retVal);});
	});

步骤3.编写授权码

有了所有这些信息,您现在就可以实际编写授权代码了。 在这种情况下,您需要检查用户是否与要更改的帐户相同。 如果是这样,请检查余额是否增加,如果是,请拒绝该交易:

// The only case where we deny authorization
	if ((id === user) && (balance > knownBalance[id])) {
		res.status(401).send('Unauthorized');
		return ; // No need to continue this function
	}

请注意,这种类型的策略明确禁止某些操作并隐含地允许其他所有操作,在这里可以接受,只是因为应用程序是如此简单。 在生产系统中,最佳实践是拥有一个策略,该策略明确允许允许的操作,并隐式拒绝其他所有操作。

潜在的陷阱:交易回滚

使用此类代理时,重要的考虑因素是应用程序必须能够回滚禁止的操作。 例如,在示例应用程序的情况下,从Bill到Alice的资金转移将代理(以及它之外的数据库)视为两个更改操作:

  1. 从比尔的余额中扣除这笔钱
  2. 将钱加到爱丽丝的余额中

如果第一个操作被允许但第二个操作被拒绝(例如,因为Alice也是用户),那么Bill帐户中的钱就会消失。 这是不良行为,在代理中很难防止。

编写正确的应用程序时假设数据库操作可能会失败,并具有处理它的代码。 在这里, modifyAccount函数具有两个回调:一个在成功的情况下调用,另一个在失败的情况下调用。

// Modify a bank account
var modifyAccount = (user, amount, cloudantUrl, callback, errCallback) => {
    var db = require("cloudant")(cloudantUrl).db.use("accounts");
    
    db.get(user, (err, res) => {
        res.balance += amount;
        db.insert(res, (err, body) => {
            errMsg = JSON.stringify(err);
            if (err === null)
                callback();
            else
                errCallback();
        }); // end of db.insert
    });  // end of db.get
};

调用modifyAccount的函数使用内部调用(后一个)的失败回调来撤消外部调用(首先发生的调用)。

modifyAccount(params.fromUser, -params.amount, cloudantUrl, 
        () => {
            modifyAccount(params.toUser, +params.amount, cloudantUrl, 
                () => { returnHtml(success); }, 
                () => { 
                    // Before reporting the error, undo the outer modifyAccount, which did succeed.
                    modifyAccount(params.fromUser, +params.amount, cloudantUrl, 
                        () => {errorMessage(success);},
                        () => {errorMessage(success);})   // End of undo modifyAccount
                }  // end of failure function for inner modifyAccount
            ); // end of inner modifyAccount call
        },
        () => { errorMessage(success); }
    ); // end of outer modifyAccount call

如果应用程序没有使用这种回滚功能编写,则在代理中提供授权而不导致状态不一致会更加困难。 您可能可以通过缓存与特定交易相关的所有更改来做到这一点,但是要这样做,您将必须找出一种识别单个交易的方法。 这可能是可行的,但这取决于应用程序本身。 没有一般的方法可以做到这一点。

结论

Cloudant代理无法替代应用程序中的安全性,因为它包含的信息少得多,并且与用户进行通信的能力受到更大的限制。 但是,作为“纵深防御”的组成部分,它可能是攻击者需要突破的另一道防线。 万一攻击者闯入了应用程序服务器,该代理还可以提供独立于应用程序的操作日志。


翻译自: https://www.ibm.com/developerworks/security/library/se-authorize-with-cloudant-proxy/index.html

cloudant

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值