Security Considerations
This help topic describes how to avoid potential security vulnerabilities when you use DevExtreme components.
See Also
HTML Encoding
HTML encoding is a simple technique that helps protect your web application from cross-site scripting (XSS) attacks. In an XSS attack, the attacker injects a malicious script into your web application. Every time a user visits the infected part of the application, this script runs. To prevent the code injection, user input must always be encoded (converted to plain text).
encodeHtml
encodeHtml is a Boolean property that you can set for a column in the DataGrid and TreeList components. Its default value is true, which means that column values are encoded. If you set it to false, the encoding is disabled, and malicious code can be executed. We recommend that you keep this property set to true.
Open the following example to learn how disabling the encodeHtml property can affect your application: HTML Encoding in DataGrid. In this example, malicious code is saved in the data source:
const products = [{ "ProductID": 1, "ProductName": "<img src=1 onerror=alert('XSS') \/>", // ... }, { "ProductID": 2, "ProductName": "<script>alert('XSS')<\/script>", // ... }, // ... ];
When encodeHtml is true, the DataGrid interprets this code as text and simply displays it:
If you set encodeHtml to false, the malicious code will be interpreted as script, and you will see an alert pop-up window:
html
Items in collection UI components (List, SelectBox, Toolbar) can apply appearance based on data source fields (see the Default Templates article). html is one of such fields that specifies item markup. This field's values are not encoded, so ensure that they do not contain malicious code. Alternatively, you can use the text field. Unlike html values, text values are encoded.
The following example illustrates how the html field can lead to a potential vulnerability: HTML Encoding in List. In this example, both text and html values contain unsafe HTML, but html lines are commented out:
const products = [{ "id": 1, "text": "<img src=1 onerror=alert('XSS') \/>", // "html": "<img src=1 onerror=alert('XSS') \/>" }, { "ID": 2, "text": "<script>alert('XSS')<\/script>", // "html": "<script>alert('XSS')<\/script>" }, { "id": 3, "text": "Product 1" // "html": "Product 1" }];
When html is commented out, text applies. You can see that its values are interpreted as text and simply displayed:
Uncomment the html lines, and you will see an alert pop-up window. This is because unsafe HTML was interpreted as script and executed:
messageHtml
DevExtreme Dialog UI methods accept an HTML string as a dialog message. This string is not encoded. You can use the encodeHtml
utility method to encode the HTML string before it is passed to a Dialog UI method. The following example illustrates this technique: HTML Encoding in a Dialog UI Method.
Encoding in Templates
Angular, Vue, and React always encode values interpolated in templates. Without these frameworks, use the encodeHtml
utility method to encode the interpolated values:
$(function() { const encodeHtml = DevExpress.utils.string.encodeHtml; $("#tabs").dxTabs({ dataSource: tabs, width: 600, itemTemplate: function (itemData) { return encodeHtml(itemData.content); } }); }); const tabs = [{ id: 0, content: "<img src=1 onerror=alert('XSS') \/>" }, { id: 1, content: "<script>alert('XSS')<\/script>" }, { id: 2, content: "Tab content" }];
When you insert unencoded content, it can open your application to XSS attacks:
The encoded content is interpreted and displayed as text:
CSV Injection
If you export data from DataGrid in CSV format, you should take the threat of a CSV Injection Attack (also known as a formula injection attack) into account. Such attacks involve an injection of a malicious character sequence that is interpreted as a formula and executed within a computer network. Cell values that start with the =, +, -, and @ characters can initiate an injection attack.
When executed, malicious code in a formula can tamper with user data, or allow unauthorized access to data and internal resources.
You can encode CSV files to prevent execution of potentially harmful code within them. Pass the encodeExecutableContent option as an argument of the configuration object of the exportDataGrid function.
jQuery
$(function() { $("#dataGridContainer").dxDataGrid({ // ... export: { enabled: true, formats: ['csv'] }, onExporting(e) { const workbook = new ExcelJS.Workbook(); const worksheet = workbook.addWorksheet('Employees'); DevExpress.excelExporter.exportDataGrid({ component: e.component, worksheet: worksheet, encodeExecutableContent: true, }).then(() => { workbook.csv.writeBuffer().then((buffer) => { saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.csv'); }); }); e.cancel = true; } }); });
<head> <!-- ... --> <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.4.0/polyfill.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/exceljs/3.3.1/exceljs.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js"></script> <!-- reference the DevExtreme sources here --> </head>
Angular
<dx-data-grid ... (onExporting)="onExporting($event)"> <dxo-export [enabled]="true" [formats]="['csv']" ></dxo-export> </dx-data-grid>
import { Component } from '@angular/core'; import { exportDataGrid } from 'devextreme/excel_exporter'; import { Workbook } from 'exceljs'; import saveAs from 'file-saver'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { onExporting(e) { const workbook = new Workbook(); const worksheet = workbook.addWorksheet('Employees'); exportDataGrid({ component: e.component, worksheet: worksheet, encodeExecutableContent: true, }).then(() => { workbook.csv.writeBuffer().then((buffer) => { saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.csv'); }); }); e.cancel = true; } }
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { DxDataGridModule } from 'devextreme-angular'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, DxDataGridModule ], providers: [ ], bootstrap: [AppComponent] }) export class AppModule { }
Vue
<template> <DxDataGrid ... @exporting="onExporting"> <DxExport :enabled="true" :formats="['csv']" /> </DxDataGrid> </template> <script> // ... import { DxDataGrid, DxExport } from 'devextreme-vue/data-grid'; import { exportDataGrid } from 'devextreme/excel_exporter'; import { Workbook } from 'exceljs'; import saveAs from 'file-saver'; export default { components: { DxDataGrid, DxExport }, methods: { onExporting(e) { const workbook = new Workbook(); const worksheet = workbook.addWorksheet('Employees'); exportDataGrid({ component: e.component, worksheet: worksheet, encodeExecutableContent: true, }).then(function() { workbook.csv.writeBuffer().then((buffer) => { saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.csv'); }); }); e.cancel = true; } } } </script>
React
import React, { useCallback } from 'react'; // ... import DataGrid, { Export } from 'devextreme-react/data-grid'; import { Workbook } from 'exceljs'; import saveAs from 'file-saver'; import { exportDataGrid } from 'devextreme/excel_exporter'; const App = () => { const onExporting = useCallback((e) => { const workbook = new Workbook(); const worksheet = workbook.addWorksheet('Employees'); exportDataGrid({ component: e.component, worksheet: worksheet, encodeExecutableContent: true, }).then(function() { workbook.csv.writeBuffer().then((buffer) => { saveAs(new Blob([buffer], { type: 'application/octet-stream' }), 'DataGrid.csv'); }); }); e.cancel = true; }, []); return ( <DataGrid ... onExporting={onExporting}> <Export enabled={true} formats={['csv']} /> </DataGrid> ); } export default App;
If you have technical questions, please create a support ticket in the DevExpress Support Center.
We appreciate your feedback.