feat(android): Introduce setSupportMultipleWindows to mitigate CVE-2020-6506 (#1747 by @mrcoinbase and @kelset -- THANK YOU!)
BREAKING CHANGE: This release introduces the `setSupportMultipleWindows` prop for Android. This sets the underlying Android WebView setting `setSupportMultipleWindows`. This prop defaults to `true` (previously `false`), and serves to mitigate the security advisory [CVE-2020-6506](https://github.com/react-native-webview/react-native-webview/security/advisories/GHSA-36j3-xxf7-4pqg). The primary way this new behavior changes existing React Native WebView implementations on Android is that links that open in new tabs/windows (such as `<a target="_blank">`) will now prompt to open in the system browser, rather than re-using the current WebView. If this behavior is not desirable, you can set this new prop to `false`, but be aware that this exposes your app to the security vulnerability listed above. Make sure you have read and understand the whole advisory and relevant links. iOS & Windows are unaffected. ```jsx <WebView // ... setSupportMultipleWindows={true} // default: true /> ``` Thanks to @mrcoinbase, @kelset, and @Titozzz for their work on this.
This commit is contained in:
parent
1b009dd746
commit
194c6a2335
|
@ -13,6 +13,7 @@ import android.net.http.SslError;
|
|||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
@ -187,6 +188,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|||
settings.setBuiltInZoomControls(true);
|
||||
settings.setDisplayZoomControls(false);
|
||||
settings.setDomStorageEnabled(true);
|
||||
settings.setSupportMultipleWindows(true);
|
||||
|
||||
settings.setAllowFileAccess(false);
|
||||
settings.setAllowContentAccess(false);
|
||||
|
@ -252,6 +254,11 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|||
view.getSettings().setJavaScriptEnabled(enabled);
|
||||
}
|
||||
|
||||
@ReactProp(name = "setSupportMultipleWindows")
|
||||
public void setSupportMultipleWindows(WebView view, boolean enabled){
|
||||
view.getSettings().setSupportMultipleWindows(enabled);
|
||||
}
|
||||
|
||||
@ReactProp(name = "showsHorizontalScrollIndicator")
|
||||
public void setShowsHorizontalScrollIndicator(WebView view, boolean enabled) {
|
||||
view.setHorizontalScrollBarEnabled(enabled);
|
||||
|
@ -875,7 +882,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|||
// This is desired behavior. We later use these values to determine whether the request is a top-level navigation or a subresource request.
|
||||
String topWindowUrl = webView.getUrl();
|
||||
String failingUrl = error.getUrl();
|
||||
|
||||
|
||||
// Cancel request after obtaining top-level URL.
|
||||
// If request is cancelled before obtaining top-level URL, undesired behavior may occur.
|
||||
// Undesired behavior: Return value of WebView.getUrl() may be the current URL instead of the failing URL.
|
||||
|
@ -1073,6 +1080,17 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|||
this.mWebView = webView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
|
||||
|
||||
final WebView newWebView = new WebView(view.getContext());
|
||||
final WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
|
||||
transport.setWebView(newWebView);
|
||||
resultMsg.sendToTarget();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConsoleMessage(ConsoleMessage message) {
|
||||
if (ReactBuildConfig.DEBUG) {
|
||||
|
|
|
@ -72,6 +72,7 @@ This document lays out the current public properties and methods for the React N
|
|||
- [`ignoreSilentHardwareSwitch`](Reference.md#ignoreSilentHardwareSwitch)
|
||||
- [`onFileDownload`](Reference.md#onFileDownload)
|
||||
- [`autoManageStatusBarEnabled`](Reference.md#autoManageStatusBarEnabled)
|
||||
- [`setSupportMultipleWindows`](Reference.md#setSupportMultipleWindows)
|
||||
|
||||
## Methods Index
|
||||
|
||||
|
@ -782,7 +783,7 @@ A Boolean value indicating whether JavaScript can open windows without user inte
|
|||
|
||||
### `androidHardwareAccelerationDisabled`[⬆](#props-index)<!-- Link generated with jump2header -->
|
||||
|
||||
**Deprecated.** Use the `androidLayerType` prop instead.
|
||||
**Deprecated.** Use the `androidLayerType` prop instead.
|
||||
|
||||
| Type | Required | Platform |
|
||||
| ---- | -------- | -------- |
|
||||
|
@ -792,7 +793,7 @@ A Boolean value indicating whether JavaScript can open windows without user inte
|
|||
|
||||
### `androidLayerType`[⬆](#props-index)<!-- Link generated with jump2header -->
|
||||
|
||||
Specifies the layer type.
|
||||
Specifies the layer type.
|
||||
|
||||
Possible values for `androidLayerType` are:
|
||||
|
||||
|
@ -1282,6 +1283,21 @@ Example:
|
|||
<WebView autoManageStatusBarEnabled={false} />
|
||||
```
|
||||
|
||||
### `setSupportMultipleWindows`
|
||||
|
||||
Sets whether the WebView supports multiple windows. See [Android documentation]('https://developer.android.com/reference/android/webkit/WebSettings#setSupportMultipleWindows(boolean)') for more information.
|
||||
Setting this to false can expose the application to this [vulnerability](https://alesandroortiz.com/articles/uxss-android-webview-cve-2020-6506/) allowing a malicious iframe to escape into the top layer DOM.
|
||||
|
||||
| Type | Required | Default | Platform |
|
||||
| ------- | -------- | ------- | -------- |
|
||||
| boolean | No | true | Android |
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
<WebView setSupportMultipleWindows={false} />
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
### `extraNativeComponentConfig()`[⬆](#methods-index)<!-- Link generated with jump2header -->
|
||||
|
|
|
@ -18,6 +18,7 @@ import Uploads from './examples/Uploads';
|
|||
import Injection from './examples/Injection';
|
||||
import LocalPageLoad from './examples/LocalPageLoad';
|
||||
import Messaging from './examples/Messaging';
|
||||
import NativeWebpage from './examples/NativeWebpage';
|
||||
|
||||
const TESTS = {
|
||||
Messaging: {
|
||||
|
@ -84,6 +85,14 @@ const TESTS = {
|
|||
return <LocalPageLoad />;
|
||||
},
|
||||
},
|
||||
NativeWebpage: {
|
||||
title: 'NativeWebpage',
|
||||
testId: 'NativeWebpage',
|
||||
description: 'Test to open a new webview with a link',
|
||||
render() {
|
||||
return <NativeWebpage />;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
type Props = {};
|
||||
|
@ -166,6 +175,11 @@ export default class App extends Component<Props, State> {
|
|||
title="Messaging"
|
||||
onPress={() => this._changeTest('Messaging')}
|
||||
/>
|
||||
<Button
|
||||
testID="testType_nativeWebpage"
|
||||
title="NativeWebpage"
|
||||
onPress={() => this._changeTest('NativeWebpage')}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{restarting ? null : (
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import React, {Component} from 'react';
|
||||
import {View} from 'react-native';
|
||||
|
||||
import WebView from 'react-native-webview';
|
||||
|
||||
type Props = {};
|
||||
type State = {};
|
||||
|
||||
export default class NativeWebpage extends Component<Props, State> {
|
||||
state = {};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={{height: 400}}>
|
||||
<WebView
|
||||
source={{uri: 'https://infinite.red'}}
|
||||
style={{width: '100%', height: '100%'}}
|
||||
// setSupportMultipleWindows={false}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -63,6 +63,7 @@ class WebView extends React.Component<AndroidWebViewProps, State> {
|
|||
androidHardwareAccelerationDisabled: false,
|
||||
androidLayerType: 'none',
|
||||
originWhitelist: defaultOriginWhitelist,
|
||||
setSupportMultipleWindows: true,
|
||||
};
|
||||
|
||||
static isFileUploadSupported = async () => {
|
||||
|
|
|
@ -295,6 +295,7 @@ export interface AndroidNativeWebViewProps extends CommonNativeWebViewProps {
|
|||
onRenderProcessGone?: (event: WebViewRenderProcessGoneEvent) => void;
|
||||
overScrollMode?: OverScrollModeType;
|
||||
saveFormDataDisabled?: boolean;
|
||||
setSupportMultipleWindows?: boolean;
|
||||
textZoom?: number;
|
||||
thirdPartyCookiesEnabled?: boolean;
|
||||
messagingModuleName?: string;
|
||||
|
@ -799,6 +800,13 @@ export interface AndroidWebViewProps extends WebViewSharedProps {
|
|||
*/
|
||||
saveFormDataDisabled?: boolean;
|
||||
|
||||
/**
|
||||
* Boolean value to set whether the WebView supports multiple windows. Used on Android only
|
||||
* The default value is `true`.
|
||||
* @platform android
|
||||
*/
|
||||
setSupportMultipleWindows?: boolean;
|
||||
|
||||
/**
|
||||
* Used on Android only, controls whether the given list of URL prefixes should
|
||||
* make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a
|
||||
|
|
Loading…
Reference in New Issue