Поделиться через


Developing secure apps

[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]

Windows Store apps using JavaScript enable developers to combine the versatility and expressiveness of web technologies alongside a wide range of powerful APIs available in the Windows Runtime. These apps have all the power of conventional native applications and need to be just as secure. Fortunately, the platform was designed with security in mind and includes several security "guardrails" that, when combined with best practices, make it easy to create a secure app.

Common sources of data

Windows Store apps have access to the vast sources of data that exist on the web today. Each of these sources of data requires secure handling by developers and should always be validated to ensure that malicious scripts or other dangerous content can't compromise the app.

You even need to check for and remove malicious code in data that comes from a trusted source, such as a back-end web service. The data might be modified in transit, or the back-end service itself might have been compromised. To ensure the security of the app and to protect its users, check all data that the app receives from the network.

The first step to ensure security is to identify the points where data flows into the app. Here are some common sources of data that could potentially compromise your app:

These are just a few examples of untrusted data sources. The first step in securing your app is to take an inventory of the data that originates from the network.

Security contexts and script filtering

The infrastructure for Windows Store apps using JavaScript was designed to help developers avoid common security issues that can result from the unsafe handling of untrusted data. One new feature that can help improve security is support for local and web contexts. These contexts enable developers to design their apps from the ground up to segregate untrusted content, such as remote JavaScript, from trusted code and data included in the app's package.

Standard frame isolation separates the web and local contexts, and their contexts are determined automatically based on the origin of the content. For example, content referred via the ms-appx:// protocol is automatically loaded into a local context frame, while remote web content loaded by an iframe is always loaded in the web context. The two contexts have different security properties and access levels (described in Features and restrictions by context). While the two contexts have different access levels, convenient intra-frame communication is available using the postMessage infrastructure.

The automatic script filtering of the DOM methods drops any dynamic content that is considered unsafe and prevents it from being injected into the DOM, while allowing benign markup to remain intact. If the content is intended to be added to an existing DOM element, it's safe to use the innerText or outerText properties to add the content.

Security Warning: Note that automatically filter scripting is not applied when you use innerText and outerText to set the text of a script element.

While automatic script filtering is a great "guardrail" that can prevent unexpected security attacks from harming the user, don't let it be your app's only defense. When automatic script filtering blocks content, it throws an error that can affect the user experience. Also, third-party libraries that work with data might use APIs that aren't automatically filtered, which can lead to security issues. It's a best practice to avoid relying on automatic script filtering as a universal defense and instead perform proactive filtering within your code. Performing explicit filtering of untrusted data is the best way to ensure that your app remains safe.

Validate the origin of invokeScriptAsync and postMessage data

The WebView (XAML) and x-ms-webview (HTML) controls have methods similar to the postMessage method for an iframe that allows for communication across contexts. The InvokeScriptAsync (XAML) and invokeScriptAsync (HTML).md) methods are the primary way to transfer data between the local and web contexts in a Windows Store app.

The postMessage method is the primary way to transfer data between the local and web contexts in a Windows Store app using JavaScript app. Instead of using the script element or hacking across domains to create mash-up apps (apps that combine data from different sources on the web), use postMessage. Using frame-based separation between local and remote sources enables developers to include remote content, such as maps or advertisements, while keeping the document and resource access isolated by using the same-origin policy.

To safely use postMessage and invokeScriptAsync methods to pass messages between local and remote documents, check the source origin of a postMessage or invokeScriptAsync response before using the data. This is necessary because many apps include multiple remote iframe or WebView elements, each with a different origin and trust level. Typically, an app uses a single onmessage handler function for each page that handles messages from all the iframe or WebView elements on that page.

This example shows how to call the invokeScriptAsync (HTML) method.

// The application is calling for a specific function inside of the webview
var asyncOp = webView.invokeScriptAsync(“startAnimation”, “500”);
    
asyncOp.oncomplete = function(e){
    // Even though the risk is less with the invokeScript message
    // still make sure not to use eval on the return value.
}

asyncOp.start();

This example shows how to call the InvokeScriptAsync (XAML) method.

async void webview_DOMContentLoaded(WebView sender, WebViewDOMContentLoadedEventArgs args)
{
    var operation = await webview.InvokeScriptAsync("startAnimation", new string[] { "500" });

    if (operation == "success")
    {
        // Handle the message.
    }
} 

To improve the security of the WebView controls, we’ve restricted when the window.external.notify() function can be used from WebView content. These restrictions prevent untrusted content, or content that has been tampered with, from sending messages that are executed without validation to the host. For content to be able to send notifications one of the following conditions must be true:

  • The source of the page is from the local system via NavigateToString, NavigateToStream or ms-appx-web:///.
  • The source of the page is delivered via https:// and the site domain name is listed in the Content URIs section of the app package manifest.

For example:

webview.addEventListener("MSWebViewScriptNotify", handleScriptNotifyEvents);
function handleScriptNotifyEvents(e) {
        if (e.callingUri === "https://msgnotify.example.net/") {
            if(e.value === "msg1")
            {
                // Process the message.);
            }
        }
    }
webview.ScriptNotify += webview_ScriptNotify;
webview.Navigate(new Uri("https://msgnotify.sample.net/"));
void webview_ScriptNotify(object sender, NotifyEventArgs e)
{
    if (e.CallingUri == new Uri("https://msgnotify.sample.net/"))
    {
        if (e.Value == "msg1")
        {
            // Process the message.
        }
    }
}

This example shows two versions of an onmessage handler: the first is not secure and the second one is secure.

// This message handler is not secure because it does not implement an origin check
window.addEventListener('message', function (e) { 
    div.innerHTML = window.toStaticHTML(e.data); 
}, false);

// Secure message handler, validates message domain origin 
window.addEventListener('message', function (e) {
    if (e.origin === 'http://data.contoso.com') { 
        div.innerHTML = window.toStaticHTML(e.data); 
    }
}, false);

In most cases, an app wants to accept messages from one or two iframe elements that send map coordinates or other legitimate mash-up content, while it rejects other message content from untrusted entities, such as advertisements or comment streams. Filtering messages by origin is a great way to reduce the attack surface of an app and ensure that untrusted data is rejected. Make sure to check the origin of your data whenever you handle the onmessage event.

Automatic script filtering

Always perform script filtering and validation on untrusted data sources. How you validate the data depends on what the data is for. To add simple, static data to the DOM, use DOM APIs that ignore dynamic elements. This approach lets you display the content safely, because any script or dynamic elements in the data are displayed as simple text, rather than being interpreted as code. You can use methods such as createTextNode to populate an element with the untrusted data, which can then be added to the document DOM by calling appendChild or importNode. If the content is intended to be added to an existing DOM element, it's safe to use the innerText or outerText properties to add the content.

This example adds dynamic content in a way that isn't secure.

// Do not use this code.
// Avoid adding untrusted dynamic content
// Unsafe method 1
var myDiv = document.createElement("div");
myDiv.innerHTML = xhr.responseText 
document.body.appendChild(myDiv);

// Unsafe method 2
document.writeln(xhr.responseText);

The next example shows the secure way to add the content:

// Forcing untrusted content into static text is safe

// method 1
var myDiv = document.createElement("div");
myDiv.innerText = xhr.responseText 
document.body.appendChild(myDiv);

// method 2
var myData = document.createTextNode(xhr.responseText);
document.body.appendChild(myData);

// method3
var oDiv = document.getElementById("div1");
oDiv.outerText = xhr.responseText;

When the untrusted data contains markup, use the window.toStaticHTML DOM method to filter out unsafe markup while keeping safe data intact.

// The untrusted data contains unsafe dynamic content
var unTrustedData = "<img src='https://www.contoso.com/logo.jpg' on-click='calltoUnsafeCode();'/>";

// Safe dynamic content can be added to the DOM without introducing errors
var safeData = window.toStaticHTML(unTrustedData);

// The content of the data is now 
// "<img src='https://www.contoso.com/logo.jpg'/>" 
// and is safe to add because it was filtered
document.write(safeData);

Bypassing automatic script filtering

DOM APIs that enable you to inject dynamic markup are automatically filtered, but explicit execution inputs, such as eval, are not. You can use eval to bypass automatic filtering and execute dynamic script that you know is safe. Here's a list of script execution methods that the system doesn't automatically filter for unsafe content:

Because the system does provide automatic filtering to these methods, don't use them to execute untrusted data without filtering or encoding it yourself first.

The MSApp.execUnsafeLocalFunction API enables a function to bypass the automatic script filtering that is normally applied to dynamic markup content in the local context. This is helpful when you want to use a markup-generating template library, such as JQuery, that would otherwise be impeded by automatic filtering.

As is the case when using the eval method, using MSApp.execUnsafeLocalFunction to processes untrusted data sources can lead to security issues. Security problems are possible because the template libraries may internally write the untrusted data to the document. When in doubt about the origins of any data, use a safe encoding library or the toStaticHTML method to ensure that data is stripped of unsafe elements and code.

Using secure parsers

Using the JavaScript eval method to generate a local JavaScript object from a server side JavaScript Object Notation (JSON) response is a common, but dangerous, web development practice. This example uses the eval method to process JSON data:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <div id="foo"></div>
    <script type="text/javascript">
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "https://contoso.com/json.js", false);
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if (xhr.status == 200) {

                             // DO NOT USE, UNSAFE
                    var myObject = eval('(' + xhr.responseTxt + ')');
                }
            }
        };
        xhr.send();
     </script>
</body>
</html>

A security issue can occur if an attacker uses active eavesdropping to tamper with the data you pass to the eval method. Instead of using eval, use the JSON.parse DOM method to convert the JSON text to an object. The JSON.parse method rejects any non-JSON script that could introduce an attack.

The next example uses the JSON.parse method instead of eval.

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <div id="foo"></div>
    <script type="text/javascript">
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "http://remote-server.com/json.js", false);
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                if (xhr.status == 200) {
                    var myObject = JSON.parse(xhr.responseText);
                }
            }
        };
        xhr.send();
     </script>
</body>
</html>
</body>
</html>

Protect sensitive data

Make sure that your app handles sensitive data properly. To maintain safe custody of user data, use SSL whenever sensitive data is sent back and forth to remote servers. Security attack tools are widely available. Assume that parties with malicious intent will inevitably seek to attack any app that is not using SSL so that they can intercept private data.

In addition to privacy protection, SSL provides benefits for the integrity of applications. SSL keeps attackers from tampering with JSON or other network data streams. It's critical to protect the integrity of your traffic when you pass network data to Windows Runtime APIs, because these APIs can access the user's system.

Require HTTPS connections

It's easy to force all remote content to be transmitted and received through Secure Hypertext Transfer Protocol (HTTPS). You just add a simple HTML meta tag to your start page, like this:

<meta name="ms-https-connections-only" content="true"/>

Adding this meta-tag causes any non-HTTPS navigation to throw an error and protects the user from connection tampering, such as Domain Name System (DNS) cache poisoning or Address Resolution Protocol (ARP) cache spoofing.

Windows Store apps using JavaScript support private certificate bundling with the application package for those developers that don't have an existing PKI infrastructure.

Using the XDomainRequest

Unlike conventional browsers, local script and markup in the Windows Store apps using JavaScript have unrestricted cross-domain access to web services when using use XMLHttpRequest. By default, XMLHttpRequest makes cookie-authenticated requests to a given site when interacting with a web service or obtaining JSON data. The usage of cookies with cross-domain requests introduces two security vulnerabilities:

  • If the app is compromised, attackers can gain access to the cookies for all sites authenticated to the local context.
  • Any non-SSL XMLHttpRequest traffic might be intercepted.

Because of these issues, only use XMLHttpRequest when you need authenticated access, and be sure to use it over an SSL connection. If you don't need authenticated access, use theXDomainRequest instead. XDomainRequest allows for cross-origin traffic similar to an XMLHttpRequest but doesn't use cookies or referrer headers. This increases both the security and privacy of cross-domain requests, leading to an all-around safer app.

Restrict navigation domains

An app's navigation domain is set by its app manifest, which lives inside the app's package. Restricting the list of possible navigation domains is important to avoid the possibility that attackers might redirect a user’s application session to a domain that they can control, so that they can perform phishing attacks or other harmful activities.

A simple best practice is to avoid using wildcards when including URIs, because many sub-domains of large web services provide the ability to host arbitrary pages or other content that an attacker might take advantage of. For more info, see ApplicationContentUriRules.

Use the WebView control where possible

The WebView control has more security measures in place than a traditional iframe, so utilizing this control for hosting web content will minimize the chances of arbitrary web content compromising app code. The WebView provides the same security features for Windows Store apps using HTML/JavaScript and those using XAML. By using ApplicationContentUriRules in the package manifest, a developer can allow communication between the WebView control and the hosting app. In addition there are ways to communicate with local content. For more information on the communication see What's new in WebView in Windows 8.1, and Blending apps and sites with the HTML x-ms-webview.

Use the HTML5 sandbox attribute where possible

The HTML5 iframesandbox attribute imposes additional restrictions on the content in an iframe. Although the standard Windows Store app using JavaScript environment provides some security protections based on content origin, the sandbox attribute provides an additional layer of defense. This attribute helps developers restrict untrusted content within a page, such as advertisements or other arbitrary content. Adding the sandbox attribute prevents content from:

  • Accessing to DOM of the parent page
  • Executing scripts
  • Embedding forms
  • Reading or writing cookies
  • Accessing local storage
  • Accessing local SQL databases

Using the sandbox attribute to restrict web content's access to your app can greatly increase the security of your app.

Features and restrictions by context

Inspect your gadget

Anti-cross-site scripting library

postMessage

invokeScriptAsync (HTML)

InvokeScriptAsync (XAML)