跳转至

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