通过Salesforce,我们可以配置或开发出功能强大的网络应用。与此同时,无论作为管理员还是开发者,我们都要面对数据安全的问题。

常见的数据安全隐患有:

  • SQL注入
  • 跨站脚本攻击
  • 跨站请求伪造
  • 点击劫持
  • 重定向攻击

本文将阐述在Salesforce中对于上述隐患的基本防护措施。

SOQL注入

SQL注入是一种常见的攻击方式。Salesforce中使用了类似于SQL的数据库查询语言SOQL,同样存在注入攻击的风险。

实例

假设系统提供给用户的功能是查询所有金额少于某个数值的记录,用户可以输入数值进行查询。

当用户输入数值之后,系统会用类似于下面的SOQL进行查询。

SELECT Id, Name, Amount
FROM Certain_Object__c
WHERE Amount < 1000

其中的1000就是直接来源于用户的输入。

那么当用户输入“1000 LIMIT 1”的时候,查询语句将变为:

SELECT Id, Name, Amount
FROM Certain_Object__c
WHERE Amount < 1000 LIMIT 1

此时,系统总是只能返回一个结果,功能被破坏了。

防护方法

Salesforce提供了多种方法来过滤用户的输入,避免SOQL注入攻击的情况出现。它们多用于Apex代码中。

静态查询和参数绑定

如果在代码中预先设定好了SOQL的语句,并且将之与用户输入拼接形成最终的查询语句,使用静态查询和参数绑定的方法可以避免注入攻击。

比如:

String query = 'SELECT Id, Name FROM Account WHERE Name = \'' + userInputString + '\'';
result = Database.execute(query);

这段查询代码信任了用户的输入,从而有了被注入攻击的风险。

比如用户输入是:“test' LIMIT 1”,“test”后面的单引号就将查询语句里的WHERE部分结束,而将LIMIT部分加入了查询。注入攻击的结果就是每次返回的记录只有一条。

使用静态查询和参数绑定,改写上面的代码:

result = [SELECT Id, Name FROM Account WHERE Name = :userInputString];

这段代码将用户的输入作为参数绑定到查询语句中。当用户输入包含恶意代码时,它并不会拼接到前面的查询中,而是作为用户输入的一部分进行正常查询。

当用户输入是“test' LIMIT 1”时,系统会将其看作一个整体,单引号不会将WHERE部分结束,后面的LIMIT的部分也不会被执行了。

数据类型转换

Salesforce提供了几种数据类型转换函数,可以安全的将用户输入转换为相应的数据类型。

比如:

result = string.valueOf(userInputString)

可以将用户输入userInputString变为一个字符串,以便放入查询语句中使用。

过滤单引号

Salesforce提供了一个函数将字符串中的单引号全部过滤。

result = string.escapeSingleQuotes(userInputString)

这个函数的返回结果是将用户输入userInputString中的所有单引号前面都加上转义符“\”,保证查询语句的安全。

跨站脚本攻击(XSS)

跨站脚本攻击全称Cross-site scripting。攻击者将恶意代码注入网页,当用户浏览网站时,恶意代码就会自动执行。攻击成功后,攻击者将可能获得更高的权限、可以查看session、cookie或其他私密内容。

XSS的恶意代码多数是由于网页上对于用户的输入不加检查而导致。比如攻击者将恶意代码输入表单,在未经检查的情况下被保存到数据库中,等待其他页面读取它,然后执行。

XSS的基本防护措施

XSS的基本防护措施分为两方面:

  • 输入过滤:一般的编程语言都有相应的函数来过滤用户的输入,将可能存在的恶意代码转化为普通字符串,从而使其在被读取的时候无法执行
  • 输出编码:输出编码的目的是防止已经存在于系统中的恶意代码无法被执行。当网页需要输出内容时,已经存在的恶意代码会经过编码转化为普通的字符串,从而无法执行

Salesforce中XSS的防护措施

为了避免将用户的某些输入错误的过滤掉,Salesforce默认对于用户的输入不会进行输入过滤,而在做输出时,总是会执行输出编码用来避免XSS攻击。

HTML自动编码机制

Visualforce中“apex”命名空间下的标签在执行输出时,会将输出的字符串自动进行HTML编码。比如:

<apex:outputText value="{!$CurrentPage.parameters.name}" />

如果调用这个元素的URL中加入了带有“<”和“>”的“script”标签(可能造成JavaScript代码注入),那么Salesforce页面的输出则是:

<script>

而其HTML源码则是:

&lt;script&gt;

在这种情况下,HTML的标签起止符“<”和“>”被替换成了“<”和“>”,从而避免了注入的JavaScript代码的执行。

apex:outputText相关知识

在Visualforce页面中,“apex:outputText”标签可以将其中的内容使用一个“span”标签包括起来,并且对HTML编码进行自动转换。这样就可以对XSS进行防护。

另外,apex:outputText中也可以接受参数。比如:

<apex:outputText value="This is {0} text with {1}">
<apex:param value="the" />
<apex:param value="parameters" />
</apex:outputText>

使用的规则是:

  1. 在“apex:outputText”标签中的value属性中使用大括号和数字来对参数进行占位
  2. 使用“apex:param”标签来定义参数的值,多个参数按照顺序替换value属性中的占位符

上面的代码内容当显示在页面中时,其后台的HTML源码是:

<span>This is the text with parameters.</span>

关于“apex:outputText”更详细的讲解可以参考官方文档

关闭HTML自动编码机制

有时页面中需要输出原始的HTML内容,那么在“apex:outputText”元素中将“escape”属性设置为“false”即可。

<apex:outputText escape="false">
{!$CurrentPage.parameters.htmlStringToOutput}
</apex:outputText>

当然,此时必须有其他对于XSS的防护措施来确保输出的原始HTML内容是安全的。

非HTML内容的防护

Salesforce对于非HTML内容并没有默认的编码机制。比如在JavaScript代码中使用用户输入的数据时,该数据不会被编码。

<script>
var x = '{!$CurrentPage.parameters.stringFromUser}';
</script>

在上面的代码中,用户输入的字符串“stringFromUser”不会被自动编码,从而形成了一个安全漏洞。

Visualforce提供的编码函数

对于非HTML的内容,Visualforce提供了一些编码函数来帮助开发者防止XSS攻击,主要包括:

  • JSENCODE()
  • HTMLENCODE()
  • JSINHTMLENCODE()

JSENCODE()

JSENCODE()函数主要用于JavaScript代码中。它的主要功能是在特殊字符的前面加上转义符“\”。

<script>
var x = '{!JSENCODE($CurrentPage.parameters.stringFromUser)}';
</script>

在上面的代码中,用户输入的字符串“stringFromUser”会被自动编码,避免了恶意代码的自动执行。

假设用户输入的字符串“stringFromUser”是:

dummy';alert("dummy text");

如果不用JSENCODE(),alert函数会被自动执行。

HTMLENCODE()

HTMLENCODE()主要用于将HTML内容编码,用于当Salesforce自动的HTML编码机制被“escape”属性关掉时。

<apex:outputText escape="false">
{!HTMLENCODE($CurrentPage.parameters.htmlStringToOutput)}
</apex:outputText>

在上面的代码中,字符串“htmlStringToOutput”是有可能造成攻击的,需要使用HTMLENCODE()来避免。

JSINHTMLENCODE()

HTMLENCODE()和JSENCODE()可以组合使用,HTMLENCODE(JSENCODE())。这等价于另一个函数:JSINHTMLENCODE()。

当需要同时对HTML和JavaScript编码时,可以使用任意一种方式。

Apex中的编码

Salesforce并不鼓励在Apex代码中对数据内容进行编码,因为数据数据的输入输出应该交由前端来处理,Apex代码并不需要关心数据内容本身。

跨站请求伪造

跨站请求伪造(Cross-site request forgery),简称CSRF或XSRF,是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。

摘自维基百科:

跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。攻击者并不能通过CSRF攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义执行操作。

举个例子:

假如一个银行网站转账请求的URL地址是:(URL-1)

https://www.example.com/transfer?username=alice&amount=1000&to=bob

那么攻击者可以在网上放置这样伪造的URL地址:(URL-2)

https://www.example.com/transfer?username=alice&amount=1000&to=charlie

当一个用户登录了银行网站,其用户名是alice,在尚未登出时访问了攻击者的站点,点击了伪造的URL链接(URL-2),那么其银行账户会自动转账1000给用户名为charlie的用户。

基本防护方法

CSRF的一般防护方法是在URL的参数中加入一个口令(token),口令的值是很长的字符串,并且随着每次请求随机生成。服务器在接受请求时也会验证口令。这样的话攻击者就无法猜到准确的URL,也就无法利用伪造的URL地址来攻击了。

Salesforce中的设置功能自带了关于CSRF攻击的防护设置。在设置界面的“会话设置”连接中,可以进入“会话设置”的界面。在其中可以设置多种安全保护措施。在“跨站请求伪造 (CSRF) 保护”一栏,可以设置是否启用CSRF保护。

在Apex和Visualforce中防护跨站请求伪造

在Visualforce页面中,有一种情况是Salesforce中CSRF防护设置无法保护的,比如下面的Visualforce页面(以下称作“页面1”):

<apex:page controller="ExampleController" action="{!initFunction}">
<!-- ... -->
</apex:page>

与之对应的Apex类:

public class ExampleController {
public void initFunction() {
String id = ApexPages.currentPage().getParameters().get('UserId'); // 使用UserId发送请求
}
}

页面1的功能是直接读取URL中的UserId参数,然后利用该参数发送请求。当在另一个Visualforce页面(以下称作“页面2”)中使用“outputLink”链接到页面1时:

<apex:outputLink value="/apex/Example?UserId={!person.Id}">
Click
</apex:outputLink>

因为函数“initFunction()”是定义在页面1的“apex:page”标签的“action”属性中的,所以该函数会在页面1载入浏览器之前就执行,所以它会跳过在设置界面中设置的CSRF防护措施。

要让CSRF防护措施执行,需要将页面2中的“outputLink”链接变为“commandLink”,即:

<apex:commandLink value="Click" action="{!alternativeInitFunction}">
<apex:param name="uId" value="{!person.Id}" assignTo="{!curUserId}" />
</apex:commandLink>

将页面1中的“initFunction”的功能挪到“commandLink”定义的“alternativeInitFunction()”函数中,并传入相应的参数。然后将页面1中的“apex:page”标签中的“action”属性去掉。

这样做的结果是:本来在页面1中刚开始就要执行的功能和请求被放在了页面2的“commandLink”链接中,而后者因为页面2肯定已经被载入了,所以在执行功能和发送请求时,会调用Salesforce中设置的CSRF防护措施。

这两种流程总结对比一下:

  • 使用outputLink:先重定向到新的页面,在页面载入之前执行outputLink的action属性中定义的函数,在函数中会发送请求,发送的请求无法使用CSRF防护措施,函数执行完毕后才载入页面
  • 使用commandLink:先用Ajax异步执行commandLink的action属性中定义的函数,此时因为当前页面时已经载入了,所以可以使用CSRF防护措施,然后通过链接重定向到新的页面并载入

点击劫持

维基百科上对“点击劫持”是这样定义的:

点击劫持(clickjacking)是一种在网页中将恶意代码等隐藏在看似无害的内容(如按钮)之下,并诱使用户点击的手段。

举个例子:在网页上,攻击者将一个包含恶意链接或其他恶意内容的iframe元素设置透明度为0,并显示在网页正常内容的上方。由于iframe的透明度为0,用户无法看到其中的恶意内容,只能看到网页上的正常内容。当用户在看似正常的内容上面点击时,实际上点击的是包含恶意内容的iframe元素,从而被攻击者利用,遭受损失。

在Salesforce的设置页面中,进入“会话设置”的界面。在其中可以设置是否启用“Clickjack 保护”。

重定向攻击

在浏览网站时,用户需要页面重定向功能。重定向功能往往体现在URL的参数中,比如

https://www.example.com?returnURL=https://www.exampleWebsite.com

当用户使用此URL时,页面会自动重定向到“https://www.exampleWebsite.com”中。

当攻击者将重定向的链接改为了恶意网页,则该URL便成为了网络攻击的工具。

Salesforce中的页面重定向功能

在Salesforce中,提供了标准的页面重定向参数:

  • startURL:当页面被载入时重定向到指定的URL
  • retURL:当用户点击“后退”按钮时重定向到指定的URL
  • saveURL:当用户点击“保存”按钮时重定向到指定的URL
  • cancelURL:当用户点击“取消”按钮时重定向到指定的URL

当这些参数的值可以被攻击者利用时,比如参数的值从用户输入中得到,那么Salesforce的网页就存在被攻击的风险。

重定向攻击防护

在Salesforce中,可以采取以下几种方法进行重定向攻击防护:

对重定向进行硬编码(Hardcode)

通过对重定向进行硬编码,可以完全防止用户更改重定向链接。但是这会损失灵活性。

只重定向到本地

另一种方法是强迫所有重定向链接只能重定向到同一个域名下,比如只能重定向到“xxx.salesforce.com”下,这样的话可以保证无论用户如何更改重定向链接,被重定向的链接总是处于开发者的控制下。

假设在一个Visualforce页面中包含一个“urlToRedirect”参数,它来源于用户的输入,然后该页面会重定向到“urlToRedirect”。那么我们可以用以下代码强制用户的输入重定向到当前的域名下:

PageReference finalPage;

// 得到用户的输入
String urlToRedirect = ApexPages.currentPage().getParameters().get('urlToRedirect'); // 清除用户输入开头的“/”符号
if(urlToRedirect.startsWith('/') {
urlToRedirect = urlToRedirect.replaceFirst('/', '');
}) // 将页面的重定向设定到当前域名下,保证用户转到的页面是当前网站下的某一个页面(或不存在的页面)
finalPage = new PageReference('/' + urlToRedirect);
finalPage.setRedirect(true);

设定域名白名单

如果开发的过程中的确需要重定向到外部网站,则可以在代码中手动建立一个字符串列表,作为可信的域名白名单。当用户的输入包含白名单中的域名时,允许页面重定向到外部网站。

Salesforce中没有预设此类功能或函数,需要开发者自己实现。

总结

以上介绍了在Salesforce中对于常见网络攻击的防护措施,并侧重于在Apex和Visualforce中的实现。

随着技术的发展,这些防护措施可能会过时。所以,追踪数据安全的最新动态、养成时刻考虑数据安全的习惯更加重要。

如果想对数据安全(不光是Salesforce)进行更多的学习和了解,在此推荐OWASP组织的网站。

Salesforce的数据安全防护措施的更多相关文章

  1. Salesforce数据安全简介

    数据安全级别 Salesforce中将数据安全分为若干等级: 组织级别:组织级别的安全设定在整个系统内部都有效.这是最广泛的级别 对象级别:对象级别的安全设定可以限制用户对于对象的权限 字段级别:字段 ...

  2. Salesforce的数据权限机制

    本文主要介绍了 Salesforce 对于系统中数据的访问控制是如何设计的,然后也了解了下 Alfresco 和 Oracle VPD 的数据权限机制.希望对一些业务系统的数据权限的访问控制设计能有所 ...

  3. Salesforce Spring '20新功能集锦系列(二)

    一.使用Data Mask保护沙盒数据 对于Salesforce管理员和开发人员,Data Mask是功能强大的新数据安全资源.管理员可以使用数据掩码自动加密沙盒中的数据,无需手动保护数据和沙盒组织的 ...

  4. Salesforce开发者学习笔记之一:基本知识

    本文介绍了Salesforce开发平台的基本知识, 包括如下内容: Salesforce平台介绍 Salesforce基本术语 定制和扩展Salesforce平台 创建一个简单的应用程序 Salesf ...

  5. salesforce 零基础学习(六十一)apex:component简单使用以及图片轮转播放的实现

    有的时候,我们项目有可能有类似需求:做一个简单的图像轮转播放功能,不同的VF页面调用可以显示不同的图片以及不同的图片描述.这种情况,如果在每个页面单独处理相关的图像轮转播放则显得代码特别冗余,此种情况 ...

  6. salesforce 零基础学习(五十五)java通过SOAP方式定时访问某个文件然后插入到sObject中

    项目源码:https://github.com/zhangyueqidlmu/SOAP-Access-SFDC.git 项目背景:salesforce端相关数据需要其他系统提供,其他系统可以提供相关数 ...

  7. salesforce 零基础学习(五十四)常见异常友好消息提示

    异常或者error code汇总:https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_calls_con ...

  8. salesforce 零基础学习(五十三)多个文件生成一个zip文件(使用git上封装的代码)

    此篇参考git代码:https://github.com/pdalcol/Zippex 学习salesforce可以访问一个朋友的网站:https://www.xgeek.net 首先感谢git上提供 ...

  9. Salesforce的sharing Rule 不支持Lookup型字段解决方案

    Salesforce 中 sharing rule 并不支持Look up 字段 和 formula 字段.但在实际项目中,有时会需要在sharing rule中直接取Look up型字段的值,解决方 ...

随机推荐

  1. SpringBoot跨域小结

    前言:公司的SpringBoot项目出于某种原因,经常样处理一些跨域请求. 一.以前通过查阅相关资料自己写的一个处理跨域的类,如下. 1.1首先定义一个filter(拦截所有请求,包括跨域请求) pu ...

  2. python --第三方登录--微博

    理解第三方登录的流程: 用户向本地应用商城发起请求,我要用微博进行登录 我们的商城凑一个url让用户跳转到第三方应用的url(微博的登录页面) 用户在该界面点击输入用户名密码之后,点击授权. 微博有个 ...

  3. python 牛客网 你的输出为:空。请检查一下你的代码,有没有循环输入处理多个case。问题解决

    你的输出为:空.请检查一下你的代码,有没有循环输入处理多个case.点击查看如何处理多个case 核心:他这个程序测试正确与否的流程是 连续输入多组测试数据进行测试,只有每组数据都对才行 所以必须使用 ...

  4. hive中的子查询改join操作(转)

    这些子查询在oracle和mysql等数据库中都能执行,但是在hive中却不支持,但是我们可以把这些查询语句改为join操作: -- 1.子查询 select * from A a where a.u ...

  5. Jenkins配置AD域认证

    Jenkins配置AD域认证 #检测域控地址ping youad.com指向的IP #如果不是实际域控ip地址,则修改hosts vi /etc/hosts #192.168.100.100替换为实际 ...

  6. Ubuntu/CentOS 系统上安装与配置Nginx

    一.在线安装: Ubuntu:sudo apt-get install nginx CentOS: sudo yum install nginx 二.安装后的位置: 1.服务地址:/etc/init. ...

  7. MD5加密之加密字符串

    public static String encode(String str) { String encodeString = ""; try { MessageDigest md ...

  8. Eureka编程

    在一些场景下,我们需要监听eureka服务中心的一些状态,譬如某个微服务挂掉了,我们希望能监听到,并给管理员发送邮件通知或钉钉告警. 一.Eureka的监听事件,可以用来监控.告警EurekaInst ...

  9. 反射的所有api

    Extension [ extension #17 Reflection version $Id: 1cf65cee164ed57874ce2d29e5c46b82f6139524 $ ] { - C ...

  10. javascript中的iterable

    遍历Array可以采用下标循环,遍历Map和Set就无法使用下标.为了统一集合类型,ES6标准引入了新的iterable类型,Array.Map和Set都属于iterable类型. 具有iterabl ...