URL Manipulation
URL 操作
描述
当攻击者获取了应用程序用于获取内容的URL的控制权时,将引入关键的安全风险。通过操纵URL,攻击者可以将应用程序重定向到恶意服务器,或在响应中注入其自己的内容。这可能导致各种安全漏洞和潜在后果。例如:
- 代码执行:攻击者可以更改URL,使其指向其控制的服务器,从而能够向应用程序传递恶意代码。该代码可能会利用应用程序中的漏洞、执行任意命令或在用户的系统上安装恶意软件。
- 数据窃取:通过将应用程序重定向到欺诈性服务器,攻击者可以诱骗用户输入敏感信息(如登录凭据、信用卡详细信息或个人数据),然后将其捕获并用于非法目的。
- 内容操纵:攻击者可以修改应用程序检索到的内容,更改显示的信息或注入恶意脚本。这可能导致各种后果,如显示误导性信息、篡改网页或进行网络钓鱼攻击。
- 供应链攻击:如果应用程序从外部来源(如库或插件)获取内容,控制URL可能会允许攻击者用受损或恶意的版本替换合法资源。这可能会危及整个系统的安全性。
以下是获取受攻击者控制的输入的应用程序的示例代码:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class MainActivity extends StatefulWidget {
final String url;
MainActivity({required this.url});
@override
_MainActivityState createState() => _MainActivityState();
}
class _MainActivityState extends State<MainActivity> {
String? content;
@override
void initState() {
super.initState();
fetchContent(widget.url);
}
Future<void> fetchContent(String url) async {
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
setState(() {
content = response.body;
});
// TODO: Process the fetched content here
} else {
// Handle error response
}
} catch (e) {
// Handle network or API call error
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Fetching URL Content'),
),
body: Center(
child: content != null
? Text(content!)
: CircularProgressIndicator(),
),
);
}
}
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Handle deep link URL if the app was launched from a URL
if let url = launchOptions?[.url] as? URL {
handleDeepLink(url)
}
return true
}
func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
// Handle deep link URL when the app is already running
handleDeepLink(url)
return true
}
private func handleDeepLink(_ url: URL) {
// Check if the URL scheme is "ostorlab"
if url.scheme == "ostorlab" {
// Extract the URL from the deep link
if let deepLinkURL = URL(string: url.absoluteString.replacingOccurrences(of: "ostorlab://", with: "")) {
// Fetch the content from the provided URL
fetchContent(deepLinkURL)
}
}
}
private func fetchContent(_ url: URL) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print("Error fetching content: \(error.localizedDescription)")
return
}
if let data = data, let content = String(data: data, encoding: .utf8) {
// Process the fetched content here
print("Fetched content: \(content)")
}
}.resume()
}
}
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Get the URL from the intent
val intent: Intent = intent
val url: String? = intent.getStringExtra("url")
if (url != null) {
// Fetch the content from the provided URL
GlobalScope.launch(Dispatchers.IO) {
val content: String? = fetchContent(url)
// Process the fetched content as needed
if (content != null) {
// TODO: Process the fetched content here
}
}
}
}
private fun fetchContent(urlString: String): String? {
var connection: HttpURLConnection? = null
var reader: BufferedReader? = null
try {
val url = URL(urlString)
connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "GET"
// Check if the response code is successful
if (connection.responseCode == HttpURLConnection.HTTP_OK) {
reader = BufferedReader(InputStreamReader(connection.inputStream))
val response = StringBuilder()
var line: String?
// Read the response line by line
while (reader.readLine().also { line = it } != null) {
response.append(line)
}
return response.toString()
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
// Close the connection and reader
connection?.disconnect()
reader?.close()
}
return null
}
}
建议
采取积极措施保护您的应用程序免受URL操作攻击至关重要。请考虑以下建议:
- 输入验证:实施严格的输入验证机制,以确保用于获取内容的URL格式正确并符合预期的模式。这可以包括检查有效的URL方案、强制实施预期的域名以及验证查询参数。
- 白名单方法:维护一个应用程序可以从中获取内容的受信任域名或来源的白名单。仅允许向这些受信任的来源发送请求,以将访问恶意或未经授权的内容的风险降至最低。
- 内容完整性检查:实施验证所获取内容完整性的机制。计算并比较接收到的内容的加密哈希或数字签名与预期值,以检测任何修改或篡改。
通过实施这些建议,您可以增强应用程序的安全性,保护用户数据,并缓解与URL操作攻击相关的风险。
以下是实现白名单验证的示例代码:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class MainActivity extends StatefulWidget {
final String url;
final List<String> allowedDomains;
MainActivity({required this.url, required this.allowedDomains});
@override
_MainActivityState createState() => _MainActivityState();
}
class _MainActivityState extends State<MainActivity> {
String? content;
@override
void initState() {
super.initState();
if (isDomainAllowed(widget.url)) {
fetchContent(widget.url);
} else {
// Handle unauthorized domain
// TODO: Implement appropriate action for unauthorized domain
}
}
bool isDomainAllowed(String urlString) {
Uri uri = Uri.parse(urlString);
String domain = uri.host ?? '';
return widget.allowedDomains.contains(domain);
}
Future<void> fetchContent(String url) async {
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
setState(() {
content = response.body;
});
// TODO: Process the fetched content here
} else {
// Handle error response
}
} catch (e) {
// Handle network or API call error
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Fetching URL Content'),
),
body: Center(
child: content != null
? Text(content!)
: CircularProgressIndicator(),
),
);
}
}
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let allowedDomains = ["example.com", "trusteddomain.com"] // Add your trusted domain names here
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Handle deep link URL if the app was launched from a URL
if let url = launchOptions?[.url] as? URL {
handleDeepLink(url)
}
return true
}
func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
// Handle deep link URL when the app is already running
handleDeepLink(url)
return true
}
private func handleDeepLink(_ url: URL) {
// Check if the URL scheme is "ostorlab"
if url.scheme == "ostorlab" {
// Extract the URL from the deep link
if let deepLinkURL = URL(string: url.absoluteString.replacingOccurrences(of: "ostorlab://", with: "")) {
// Verify the domain name against the whitelist
if isDomainAllowed(deepLinkURL) {
// Fetch the content from the provided URL
fetchContent(deepLinkURL)
} else {
// Handle unauthorized domain
// TODO: Implement appropriate action for unauthorized domain
}
}
}
}
private func isDomainAllowed(_ url: URL) -> Bool {
guard let host = url.host else {
return false
}
return allowedDomains.contains(host)
}
private func fetchContent(_ url: URL) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print("Error fetching content: \(error.localizedDescription)")
return
}
if let data = data, let content = String(data: data, encoding: .utf8) {
// Process the fetched content here
print("Fetched content: \(content)")
}
}.resume()
}
}
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
class MainActivity : AppCompatActivity() {
private val allowedDomains = listOf("example.com", "trusteddomain.com") // Add your trusted domain names here
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Get the URL from the intent
val intent: Intent = intent
val url: String? = intent.getStringExtra("url")
if (url != null) {
// Verify the domain name against the whitelist
if (isDomainAllowed(url)) {
// Fetch the content from the provided URL
GlobalScope.launch(Dispatchers.IO) {
val content: String? = fetchContent(url)
// Process the fetched content as needed
if (content != null) {
// TODO: Process the fetched content here
}
}
} else {
// Handle unauthorized domain
// TODO: Implement appropriate action for unauthorized domain
}
}
}
private fun isDomainAllowed(urlString: String): Boolean {
val url = URL(urlString)
val domain = url.host
return allowedDomains.contains(domain)
}
private fun fetchContent(urlString: String): String? {
var connection: HttpURLConnection? = null
var reader: BufferedReader? = null
try {
val url = URL(urlString)
connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "GET"
// Check if the response code is successful
if (connection.responseCode == HttpURLConnection.HTTP_OK) {
reader = BufferedReader(InputStreamReader(connection.inputStream))
val response = StringBuilder()
var line: String?
// Read the response line by line
while (reader.readLine().also { line = it } != null) {
response.append(line)
}
return response.toString()
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
// Close the connection and reader
connection?.disconnect()
reader?.close()
}
return null
}
}
链接
标准
- OWASP_MASVS_L2:
- MSTG_NETWORK_5
- PCI_STANDARDS:
- REQ_6_2
- REQ_6_3
- REQ_11_3
- OWASP_MASVS_v2_1:
- MASVS_CODE_3
- SOC2_CONTROLS:
- CC_2_1
- CC_4_1
- CC_7_1
- CC_7_2
- CC_7_4
- CC_7_5
- HIPAA_CONTROLS:
- SECURITY212
- SECURITY213
- SECURITY221