简体   繁体   中英

Prevent Android phone from connecting to WiFi network unless my app approves it?

I want to develop an app that can prevent connection to a WiFi network unless I approve it. I want to be able to query the MAC address of the access point and compare that to a list of known addresses corresponding to SSIDs. The goal of the app is to protect users from accidentally connecting to malicious access points, such as the types that can be produced with pineapple devices .

I'm not clear from my research how I would achieve this goal. Questions such as How to be notified on wifi network status change? explain how to detect the connection has happened , but for my use case that's already too late.

Neither ConnectivityManager nor WifiManager seem to offer methods for adding listeners that could interrupt a connection in progress.

Some thoughts I've had for a solution:

  • Install myself as a proxy and make the decision as to whether to allow data through. However, this doesn't seem to be an option based on Do Android proxy settings apply to all apps on the device? (hint: the answer is "No").

  • Replace the existing WiFi manager with something of my own creation. However, I've really struggled to find any information in the Android developer guides regarding replacing system components. Consequently, I'm not sure this is possible on non-rooted phones.

  • Store the network passwords within my app and set the passwords in the WiFi manager to nonsense values. Then capture a broadcast message that warns of a failed connection (presumably something like WifiManager.WPS_AUTH_FAILURE ) and selectively decide to reconnect back to that network. Might be a possible (if ugly) solution, but can I set the password back to a nonsense value while the network is still connected, to ensure we don't quietly connect to another SSID of the same name? I'm not sure . It occurs to me that pineapple devices would probably accept any password, thus rendering this approach void.

  • Find some way to prevent Android automatically connecting to known networks (ie networks that have been used before or have a password stored with them). Then I could manage all connections/disconnections from my app. I can't see how to do this manually on my phone, however, so I'm doubtful this is possible programmatically.

Can anyone suggest an approach that would work on a non-rooted phone?

You can't implement a very robust system without rooting the device. Here's the closest you can get, I think:

  1. Use getConfiguredNetworks() to fetch a list of networks currently configured on the user's device
  2. For each WifiConfiguration in the list, set the public field BSSID to the desired "safe" MAC address
  3. Call saveConfiguration() to persist the changes

Alternatively for step (2.), you could call disableNetwork() for each configured network, and selectively enabled them based on the BSSID. Note that MAC addresses can still be spoofed fairly easily.

you can listen to connectivity change of wifi and act on that events to enable disable wifi

private ConnectivityManager connectionManager;
boolean previousConnectivityStatus;
private WifiManager wifiManager;

/* Register Connectivity Receiver */
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
context.registerReceiver(networkBroadcastReceiver, intentFilter);

/* Register Wifi State Listener */
IntentFilter wifiStateIntentFilter = new IntentFilter();
wifiStateIntentFilter.addAction("android.net.wifi.WIFI_STATE_CHANGED");
context.registerReceiver(wifiStateReceiver, wifiStateIntentFilter);

connectionManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

private BroadcastReceiver wifiStateReceiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        Utility.traceM("NetworkController.wifiStateReceiver.new BroadcastReceiver() {...}::onReceive");
        int extraWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
        switch (extraWifiState)
            {
            case WifiManager.WIFI_STATE_DISABLED:
                {
                    Utility.trace("Broadcast Wifi State Disabled");
                    if(isWifiStateEventsEnabled)
                    {
                        EventBus.getDefault().post(new NetworkEvent(NetworkEventType.WIFI_DISABLED));
                    }
                    break;
                }
            case WifiManager.WIFI_STATE_ENABLED:
                {
                    Utility.trace("Broadcast Wifi State Enabled");
                    if(isWifiStateEventsEnabled)
                    {
                        EventBus.getDefault().post(new NetworkEvent(NetworkEventType.WIFI_ENABLED));
                    }
                    break;
                }
            }
    }
};

private BroadcastReceiver networkBroadcastReceiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        Utility.traceM("NetworkController.networkBroadcastReceiver.new BroadcastReceiver() {...}::onReceive");
        boolean connectivityStatus = isInternetConnectivityAvailable();
        if (previousConnectivityStatus != connectivityStatus)
        {
            if (connectivityStatus)
            {
                previousConnectivityStatus = true;
                Utility.trace("Broadcast Internet Available");
                EventBus.getDefault().post(new NetworkEvent(NetworkEventType.INTERNET_CONNECTED));
            }
            else
            {
                previousConnectivityStatus = false;
                Utility.trace("Broadcast Internet Disconnected");
                EventBus.getDefault().post(new NetworkEvent(NetworkEventType.INTERNET_DISCONNECTED));
            }
        }
    }
};

as you know when connecting to the Wifi the sifi manager app displays a hint message under the Wifi name that is connecting,

like connecting, authenticating, obtaining IP ... etc

so i tried to search how can detect those stages of connecting to a Wifi network i came to an answer showing how is this done, it was done using the a receiver to SUPPLICANT_STATE_CHANGED_ACTION

and i tried to implement it adding the code to just disconnect ... and that was success as the Wifi never got connected, the icon did not appear on the notification bar and the logs keep repeating the steps, though some how it say's connected (at logs) but nothing actually appears on the device itself, so maybe it got connected for like (10 MS)

anyhow , below is the code i used:

public class MyNetworkMonitor extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // EXTRA_BSSID
        // SUPPLICANT_STATE_CHANGED_ACTION
        // EXTRA_NEW_STATE

        // Log.i("YAZAN", intent.getAction() + " " +
        // intent.getStringExtra(WifiManager.EXTRA_BSSID));
        // Log.i("YAZAN", intent.getAction() + " "
        // +intent.getStringExtra(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION));
        // Log.i("YAZAN", intent.getAction() + " "
        // +intent.getStringExtra(WifiManager.EXTRA_NEW_STATE));

        //Log.i("YAZAN", intent.getAction() + " " + intent.getStringExtra(WifiManager.EXTRA_BSSID));

        String action  = intent.getAction();
        if(action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)){

             WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
            Log.d("YAZAN", ">>>>SUPPLICANT_STATE_CHANGED_ACTION<<<<<<");
            SupplicantState supl_state=((SupplicantState)intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE));

            switch(supl_state){
            case ASSOCIATED:Log.i("YAZAN", "ASSOCIATED");
                break;
            case ASSOCIATING:
                Log.i("YAZAN", "ASSOCIATING");
                 wifi.disconnect();
                 Log.i("YAZAN", "disconnect()");
                break;
            case AUTHENTICATING:Log.i("YAZAN", "Authenticating...");
            wifi.disconnect();
         Log.i("YAZAN", "disconnect()");
                break;
            case COMPLETED:Log.i("YAZAN", "Connected");
                break;
            case DISCONNECTED:Log.i("YAZAN", "Disconnected");
                break;
            case DORMANT:Log.i("YAZAN", "DORMANT");
            wifi.disconnect();
         Log.i("YAZAN", "disconnect()");
                break;
            case FOUR_WAY_HANDSHAKE:Log.i("YAZAN", "FOUR_WAY_HANDSHAKE");
            wifi.disconnect();
         Log.i("YAZAN", "disconnect()");
                break;
            case GROUP_HANDSHAKE:Log.i("YAZAN", "GROUP_HANDSHAKE");
            wifi.disconnect();
         Log.i("YAZAN", "disconnect()");
                break;
            case INACTIVE:Log.i("YAZAN", "INACTIVE");
                break;
            case INTERFACE_DISABLED:Log.i("YAZAN", "INTERFACE_DISABLED");
                break;
            case INVALID:Log.i("YAZAN", "INVALID");
                break;
            case SCANNING:Log.i("YAZAN", "SCANNING");
                break;
            case UNINITIALIZED:Log.i("YAZAN", "UNINITIALIZED");
                break;
            default:Log.i("YAZAN", "Unknown");
                break;

            }
            int supl_error=intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
            if(supl_error==WifiManager.ERROR_AUTHENTICATING){
                Log.i("YAZAN", "ERROR_AUTHENTICATING!");
            }
        }//if

    }// onReceive()

where ever you find a wifi.disconnect(); thats how i interrupted the connection. what remains here, is to get the network name or mac address to allow or disallow the process to complete

Permissions:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>

adding the broadcast receiver:

<receiver android:name=".MyNetworkMonitor" >
            <intent-filter>
                <action android:name="android.net.wifi.supplicant.STATE_CHANGE" />
                <action android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />

                <action android:name="android.net.wifi.STATE_CHANGE" />

            </intent-filter>
        </receiver>

thanks

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM