Which request headers can be used for a browser/client fingerprint? - php

For added security our server keeps track of the browser fingerprint. At the moment we use the following headers:
HTTP_CLIENT_IP, HTTP_X_FORWARDED_FOR, HTTP_X_FORWARDED, HTTP_X_CLUSTER_CLIENT_IP, HTTP_FORWARDED_FOR, HTTP_FORWARDED, REMOTE_ADDR (take the first non-empty as the client-IP)
HTTP_ACCEPT_* headers
HTTP_USER_AGENT
Are there any more (optional) headers that can be used?
What in general is the best 'algorithm' to calculate the client fingerprint?

you can use a unique browser fingerprint (user agent, web browser, canvas, etc) and after get the hash.
/* Generate a fingerprint string for the browser */
function generateFingerprint(){
//Generate a string based on "stable" information taken from the browser
//We call here "stable information", information that normally don't change during the user
//browse the application just after authentication
var fingerprint = [];
//Take plugins
for(var i = 0; i < navigator.plugins.length; i++){
fingerprint.push(navigator.plugins[i].name);
fingerprint.push(navigator.plugins[i].filename);
fingerprint.push(navigator.plugins[i].description);
fingerprint.push(navigator.plugins[i].version);
}
//Take User Agent
fingerprint.push(navigator.userAgent);
//Take Screen resolution
fingerprint.push(screen.availHeight);
fingerprint.push(screen.availWidth);
fingerprint.push(screen.colorDepth);
fingerprint.push(screen.height);
fingerprint.push(screen.pixelDepth);
fingerprint.push(screen.width);
//Take Graphical card info
//See http://output.jsbin.com/ovekor/3/
try {
//Add a Canvas element if the body do not contains one
if ( $("#glcanvas").length == 0 ){
$(document.body).append("<canvas id='glcanvas'></canvas>");
}
//Get ref on Canvas
var canvas = document.getElementById("glcanvas");
//Retrieve Canvas properties
gl = canvas.getContext("experimental-webgl");
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
fingerprint.push(gl.getParameter(gl.VERSION));
fingerprint.push(gl.getParameter(gl.SHADING_LANGUAGE_VERSION));
fingerprint.push(gl.getParameter(gl.VENDOR));
fingerprint.push(gl.getParameter(gl.RENDERER));
fingerprint.push(gl.getSupportedExtensions().join());
} catch (e) {
//Get also error because it's will be stable too..
fingerprint.push(e);
}
//Last and, in order to made this browser unique, generate a random ID that we will store
//in local storage (in order to be persistent after browser close/reopen)
//Add this ID because, in Enterprise, most of the time browser have the same configuration
var browserUniqueID = localStorage.getItem("browserUniqueID");
if (browserUniqueID === null) {
localStorage.setItem("browserUniqueID", CryptoJS.lib.WordArray.random(80));
browserUniqueID = localStorage.getItem("browserUniqueID");
}
fingerprint.push(browserUniqueID);
return fingerprint.join();
}
And finally get the hash and sent to the server.
//Call the fingerprint dedicated function
var fingerprint = generateFingerprint();
//Use CryptoJS library ot generate a hex encoded string of the hash of the fingerprint
var fingerprintHash = CryptoJS.SHA256(fingerprint);
Source: https://www.owasp.org/index.php/JSON_Web_Token_(JWT)_Cheat_Sheet_for_Java#Token_sidejacking
https://browserleaks.com/canvas

To answer the question of which naming for the header:
I used on my side X-Fingerprint but I didn't find any standard way. You can make it more "obscure" so people don't guess that's a field to tamper.
At the end from what I saw headers like X-... are supposed to be free of use (less chance to conflict), but the last years more and more tools use them, and this kind of naming appears to become "standard" or a "convention" (like x-request-id for example).

Related

Get Device ID or unique identifier for mobile apps [duplicate]

Do Android devices have a unique ID, and if so, what is a simple way to access it using Java?
Settings.Secure#ANDROID_ID returns the Android ID as an unique for each user 64-bit hex string.
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
Also read Best practices for unique identifiers: https://developer.android.com/training/articles/user-data-ids
UPDATE: As of recent versions of Android, many of the issues with ANDROID_ID have been resolved, and I believe this approach is no longer necessary. Please take a look at Anthony's answer.
Full disclosure: my app used the below approach originally but no longer uses this approach, and we now use the approach outlined in the Android Developer Blog entry that emmby's answer links to (namely, generating and saving a UUID#randomUUID()).
There are many answers to this question, most of which will only work "some" of the time, and unfortunately, that's not good enough.
Based on my tests of devices (all phones, at least one of which is not activated):
All devices tested returned a value for TelephonyManager.getDeviceId()
All GSM devices (all tested with a SIM) returned a value for TelephonyManager.getSimSerialNumber()
All CDMA devices returned null for getSimSerialNumber() (as expected)
All devices with a Google account added returned a value for ANDROID_ID
All CDMA devices returned the same value (or derivation of the same value) for both ANDROID_ID and TelephonyManager.getDeviceId() -- as long as a Google account has been added during setup.
I did not yet have a chance to test GSM devices with no SIM, a GSM device with no Google account added, or any of the devices in airplane mode.
So if you want something unique to the device itself, TM.getDeviceId() should be sufficient. Obviously, some users are more paranoid than others, so it might be useful to hash 1 or more of these identifiers, so that the string is still virtually unique to the device, but does not explicitly identify the user's actual device. For example, using String.hashCode(), combined with a UUID:
final TelephonyManager tm = (TelephonyManager) getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);
final String tmDevice, tmSerial, androidId;
tmDevice = "" + tm.getDeviceId();
tmSerial = "" + tm.getSimSerialNumber();
androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
UUID deviceUuid = new UUID(androidId.hashCode(), ((long)tmDevice.hashCode() << 32) | tmSerial.hashCode());
String deviceId = deviceUuid.toString();
might result in something like: 00000000-54b3-e7c7-0000-000046bffd97
It works well enough for me.
As Richard mentions below, don't forget that you need permission to read the TelephonyManager properties, so add this to your manifest:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
import libraries
import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;
#Last Updated: 6/2/15
After reading every Stack Overflow post about creating a unique ID, the Google developer blog, and Android documentation, I feel as if the 'Pseudo ID' is the best possible option.
Main Issue: Hardware vs Software
Hardware
Users can change their hardware, Android tablet, or phone, so unique IDs based on hardware are not good ideas for TRACKING USERS
For TRACKING HARDWARE, this is a great idea
Software
Users can wipe/change their ROM if they are rooted
You can track users across platforms (iOS, Android, Windows, and Web)
The best want to TRACK AN INDIVIDUAL USER with their consent is to simply have them login (make this seamless using OAuth)
#Overall breakdown with Android
###- Guarantee uniqueness (include rooted devices) for API >= 9/10 (99.5% of Android devices)
###- No extra permissions
Psuedo code:
if API >= 9/10: (99.5% of devices)
return unique ID containing serial id (rooted devices may be different)
else
return the unique ID of build information (may overlap data - API < 9)
Thanks to #stansult for posting all of our options (in this Stack Overflow question).
##List of options - reasons why/ why not to use them:
User Email - Software
User could change email - HIGHLY unlikely
API 5+ <uses-permission android:name="android.permission.GET_ACCOUNTS" /> or
API 14+ <uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> (How to get the Android device's primary e-mail address)
User Phone Number - Software
Users could change phone numbers - HIGHLY unlikely
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
IMEI - Hardware (only phones, needs android.permission.READ_PHONE_STATE)
Most users hate the fact that it says "Phone Calls" in the permission. Some users give bad ratings because they believe you are simply stealing their personal information when all you really want to do is track device installs. It is obvious that you are collecting data.
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Android ID - Hardware (can be null, can change upon factory reset, can be altered on a rooted device)
Since it can be 'null', we can check for 'null' and change its value, but this means it will no longer be unique.
If you have a user with a factory reset device, the value may have changed or altered on the rooted device so there may be duplicates entries if you are tracking user installs.
WLAN MAC Address - Hardware (needs android.permission.ACCESS_WIFI_STATE)
This could be the second-best option, but you are still collecting and storing a unique identifier that comes directly from a user. This is obvious that you are collecting data.
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
Bluetooth MAC Address - Hardware (devices with Bluetooth, needs android.permission.BLUETOOTH)
Most applications on the market do not use Bluetooth, and so if your application doesn't use Bluetooth and you are including this, the user could become suspicious.
<uses-permission android:name="android.permission.BLUETOOTH "/>
Pseudo-Unique ID - Software (for all Android devices)
Very possible, may contain collisions - See my method posted below!
This allows you to have an 'almost unique' ID from the user without taking anything that is private. You can create your own anonymous ID from device information.
I know there isn't any 'perfect' way of getting a unique ID without using permissions; however, sometimes we only really need to track the device installation. When it comes to creating a unique ID, we can create a 'pseudo unique id' based solely on information that the Android API gives us without using extra permissions. This way, we can show the user respect and try to offer a good user experience as well.
With a pseudo-unique id, you really only run into the fact that there may be duplicates based on the fact that there are similar devices. You can tweak the combined method to make it more unique; however, some developers need to track device installs and this will do the trick or performance based on similar devices.
##API >= 9:
If their Android device is API 9 or over, this is guaranteed to be unique because of the 'Build.SERIAL' field.
REMEMBER, you are technically only missing out on around 0.5% of users who have API < 9. So you can focus on the rest: This is 99.5% of the users!
##API < 9:
If the user's Android device is lower than API 9; hopefully, they have not done a factory reset and their 'Secure.ANDROID_ID' will be preserved or not 'null'. (see http://developer.android.com/about/dashboards/index.html)
##If all else fails:
If all else fails, if the user does have lower than API 9 (lower than Gingerbread), has reset their device, or 'Secure.ANDROID_ID' returns 'null', then simply the ID returned will be solely based on their Android device information. This is where the collisions can happen.
Changes:
Removed 'Android.SECURE_ID' because factory resets could cause the value to change
Edited the code to change on API
Changed the Pseudo
Please take a look at the method below:
/**
* Return pseudo unique ID
* #return ID
*/
public static String getUniquePsuedoID() {
// If all else fails, if the user does have lower than API 9 (lower
// than Gingerbread), has reset their device or 'Secure.ANDROID_ID'
// returns 'null', then simply the ID returned will be solely based
// off their Android device information. This is where the collisions
// can happen.
// Thanks http://www.pocketmagic.net/?p=1662!
// Try not to use DISPLAY, HOST or ID - these items could change.
// If there are collisions, there will be overlapping data
String m_szDevIDShort = "35" + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10) + (Build.PRODUCT.length() % 10);
// Thanks to #Roman SL!
// https://stackoverflow.com/a/4789483/950427
// Only devices with API >= 9 have android.os.Build.SERIAL
// http://developer.android.com/reference/android/os/Build.html#SERIAL
// If a user upgrades software or roots their device, there will be a duplicate entry
String serial = null;
try {
serial = android.os.Build.class.getField("SERIAL").get(null).toString();
// Go ahead and return the serial for api => 9
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
} catch (Exception exception) {
// String needs to be initialized
serial = "serial"; // some value
}
// Thanks #Joe!
// https://stackoverflow.com/a/2853253/950427
// Finally, combine the values we have found by using the UUID class to create a unique identifier
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}
#New (for apps with ads AND Google Play Services):
From the Google Play Developer's console:
Beginning August 1st, 2014, the Google Play Developer Program Policy
requires all-new app uploads and updates to use the advertising ID in
lieu of any other persistent identifiers for any advertising purposes.
Learn more
Implementation:
Permission:
<uses-permission android:name="android.permission.INTERNET" />
Code:
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
import com.google.android.gms.common.GooglePlayServicesAvailabilityException;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import java.io.IOException;
...
// Do not call this function from the main thread. Otherwise,
// an IllegalStateException will be thrown.
public void getIdThread() {
Info adInfo = null;
try {
adInfo = AdvertisingIdClient.getAdvertisingIdInfo(mContext);
} catch (IOException exception) {
// Unrecoverable error connecting to Google Play services (e.g.,
// the old version of the service doesn't support getting AdvertisingId).
} catch (GooglePlayServicesAvailabilityException exception) {
// Encountered a recoverable error connecting to Google Play services.
} catch (GooglePlayServicesNotAvailableException exception) {
// Google Play services is not available entirely.
}
final String id = adInfo.getId();
final boolean isLAT = adInfo.isLimitAdTrackingEnabled();
}
Source/Docs:
http://developer.android.com/google/play-services/id.html
http://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html
##Important:
It is intended that the advertising ID completely replace existing
usage of other identifiers for ads purposes (such as the use of ANDROID_ID
in Settings.Secure) when Google Play Services is available. Cases
where Google Play Services is unavailable are indicated by a
GooglePlayServicesNotAvailableException being thrown by
getAdvertisingIdInfo().
##Warning, users can reset:
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
I have tried to reference every link that I took information from. If you are missing and need to be included, please comment!
Google Player Services InstanceID
https://developers.google.com/instance-id/
As Dave Webb mentions, the Android Developer Blog has an article that covers this. Their preferred solution is to track app installs rather than devices, and that will work well for most use cases. The blog post will show you the necessary code to make that work, and I recommend you check it out.
However, the blog post goes on to discuss solutions if you need a device identifier rather than an app installation identifier. I spoke with someone at Google to get some additional clarification on a few items in the event that you need to do so. Here's what I discovered about device identifiers that's NOT mentioned in the aforementioned blog post:
ANDROID_ID is the preferred device identifier. ANDROID_ID is perfectly reliable on versions of Android <=2.1 or >=2.3. Only 2.2 has the problems mentioned in the post.
Several devices by several manufacturers are affected by the ANDROID_ID bug in 2.2.
As far as I've been able to determine, all affected devices have the same ANDROID_ID, which is 9774d56d682e549c. Which is also the same device id reported by the emulator, btw.
Google believes that OEMs have patched the issue for many or most of their devices, but I was able to verify that as of the beginning of April 2011, at least, it's still quite easy to find devices that have the broken ANDROID_ID.
Based on Google's recommendations, I implemented a class that will generate a unique UUID for each device, using ANDROID_ID as the seed where appropriate, falling back on TelephonyManager.getDeviceId() as necessary, and if that fails, resorting to a randomly generated unique UUID that is persisted across app restarts (but not app re-installations).
Note that for devices that have to fallback on the device ID, the unique ID WILL persist across factory resets. This is something to be aware of. If you need to ensure that a factory reset will reset your unique ID, you may want to consider falling back directly to the random UUID instead of the device ID.
Again, this code is for a device ID, not an app installation ID. For most situations, an app installation ID is probably what you're looking for. But if you do need a device ID, then the following code will probably work for you.
import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import java.io.UnsupportedEncodingException;
import java.util.UUID;
public class DeviceUuidFactory {
protected static final String PREFS_FILE = "device_id.xml";
protected static final String PREFS_DEVICE_ID = "device_id";
protected volatile static UUID uuid;
public DeviceUuidFactory(Context context) {
if (uuid == null) {
synchronized (DeviceUuidFactory.class) {
if (uuid == null) {
final SharedPreferences prefs = context
.getSharedPreferences(PREFS_FILE, 0);
final String id = prefs.getString(PREFS_DEVICE_ID, null);
if (id != null) {
// Use the ids previously computed and stored in the
// prefs file
uuid = UUID.fromString(id);
} else {
final String androidId = Secure.getString(
context.getContentResolver(), Secure.ANDROID_ID);
// Use the Android ID unless it's broken, in which case
// fallback on deviceId,
// unless it's not available, then fallback on a random
// number which we store to a prefs file
try {
if (!"9774d56d682e549c".equals(androidId)) {
uuid = UUID.nameUUIDFromBytes(androidId
.getBytes("utf8"));
} else {
final String deviceId = (
(TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE))
.getDeviceId();
uuid = deviceId != null ? UUID
.nameUUIDFromBytes(deviceId
.getBytes("utf8")) : UUID
.randomUUID();
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
// Write the value out to the prefs file
prefs.edit()
.putString(PREFS_DEVICE_ID, uuid.toString())
.commit();
}
}
}
}
}
/**
* Returns a unique UUID for the current android device. As with all UUIDs,
* this unique ID is "very highly likely" to be unique across all Android
* devices. Much more so than ANDROID_ID is.
*
* The UUID is generated by using ANDROID_ID as the base key if appropriate,
* falling back on TelephonyManager.getDeviceID() if ANDROID_ID is known to
* be incorrect, and finally falling back on a random UUID that's persisted
* to SharedPreferences if getDeviceID() does not return a usable value.
*
* In some rare circumstances, this ID may change. In particular, if the
* device is factory reset a new device ID may be generated. In addition, if
* a user upgrades their phone from certain buggy implementations of Android
* 2.2 to a newer, non-buggy version of Android, the device ID may change.
* Or, if a user uninstalls your app on a device that has neither a proper
* Android ID nor a Device ID, this ID may change on reinstallation.
*
* Note that if the code falls back on using TelephonyManager.getDeviceId(),
* the resulting ID will NOT change after a factory reset. Something to be
* aware of.
*
* Works around a bug in Android 2.2 for many devices when using ANDROID_ID
* directly.
*
* #see http://code.google.com/p/android/issues/detail?id=10603
*
* #return a UUID that may be used to uniquely identify your device for most
* purposes.
*/
public UUID getDeviceUuid() {
return uuid;
}
}
Here is the code that Reto Meier used in the Google I/O presentation this year to get a unique id for the user:
private static String uniqueID = null;
private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
public synchronized static String id(Context context) {
if (uniqueID == null) {
SharedPreferences sharedPrefs = context.getSharedPreferences(
PREF_UNIQUE_ID, Context.MODE_PRIVATE);
uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
if (uniqueID == null) {
uniqueID = UUID.randomUUID().toString();
Editor editor = sharedPrefs.edit();
editor.putString(PREF_UNIQUE_ID, uniqueID);
editor.commit();
}
}
return uniqueID;
}
If you couple this with a backup strategy to send preferences to the cloud (also described in Reto's talk, you should have an id that ties to a user and sticks around after the device has been wiped, or even replaced. I plan to use this in analytics going forward (in other words, I have not done that bit yet :).
It's a simple question, with no simple answer.
Moreover, all of the existing answers here are either out of date or unreliable.
So if you're searching for a solution after 2020.
Here are a few things to keep in mind:
All the hardware-based identifiers (IMEI, MAC, Serial Number, etc.) are unreliable for non-google devices (except Pixels and Nexuses), which are statistically most of the android active devices worldwide. Therefore official Android identifiers best practices clearly states:
Avoid using hardware identifiers, such as IMEI, MAC address, etc...
Which makes most of the answers here invalid. Also due to different android security updates, some of them require newer and stricter runtime permissions, which can be simply denied by the user.
For example CVE-2018-9489 affects all the WIFI based techniques mentioned above.
That makes those identifiers not only unreliable but also inaccessible in many cases.
So in simpler words: don't use those techniques.
Many other answers here are suggesting to use the AdvertisingIdClient, which is also incompatible, as it's by design only for ads profiling. It's also stated in the official reference
Only use an Advertising ID for user profiling or ads use cases
It's not only unreliable for device identification, but you also must follow the user privacy regarding ad tracking policy, which states clearly that users can reset or block it at any moment.
So don't use it either.
Since you cannot have the desired static globally unique and reliable device identifier. Android's official reference suggests:
Use a Firebase installation ID (FID) or a privately stored GUID whenever possible for all other use cases, except for payment fraud prevention and telephony.
It's unique for the application installation on the device, so when the user uninstalls the app - it's wiped out, so it's not 100% reliable, but it's the next best thing.
Note As of today the FirebaseInstanceId is deprecated, you should use FirebaseInstallations instead.
To use FirebaseInstallations add the latest firebase-messaging dependency into your gradle
implementation 'com.google.firebase:firebase-messaging:23.0.0'
And use the code below to get the firebase ID:
FirebaseInstallations.getInstance().getId().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
String firebaseIdentifier = task.getResult();
// Do what you need with firebaseIdentifier
}
});
If you need to store the device identification on your remote server, then don't store it as is (plain text), but a hash with salt.
Today it's not only a best practice, you actually must do it by law according to GDPR - identifiers and similar regulations.
Also you might consider the Wi-Fi adapter's MAC address. Retrieved like this:
WifiManager wm = (WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE);
return wm.getConnectionInfo().getMacAddress();
Requires permission android.permission.ACCESS_WIFI_STATE in the manifest.
Reported to be available even when Wi-Fi is not connected. If Joe from the answer above gives this one a try on his many devices, that'd be nice.
On some devices, it's not available when Wi-Fi is turned off.
NOTE: From Android 6.x, it returns consistent fake mac address: 02:00:00:00:00:00
There’s rather useful info here.
It covers five different ID types:
IMEI (only for Android devices with Phone use; needs android.permission.READ_PHONE_STATE)
Pseudo-Unique ID (for all Android devices)
Android ID (can be null, can change upon factory reset, can be altered on rooted phone)
WLAN MAC Address string (needs android.permission.ACCESS_WIFI_STATE)
BT MAC Address string (devices with Bluetooth, needs android.permission.BLUETOOTH)
The official Android Developers Blog now has a full article just about this very subject, Identifying App Installations.
At Google I/O Reto Meier released a robust answer to how to approach this which should meet most developers needs to track users across installations. Anthony Nolan shows the direction in his answer, but I thought I'd write out the full approach so that others can easily see how to do it (it took me a while to figure out the details).
This approach will give you an anonymous, secure user ID which will be persistent for the user across different devices (based on the primary Google account) and across installs. The basic approach is to generate a random user ID and to store this in the apps' shared preferences. You then use Google's backup agent to store the shared preferences linked to the Google account in the cloud.
Let's go through the full approach. First, we need to create a backup for our SharedPreferences using the Android Backup Service. Start by registering your app via http://developer.android.com/google/backup/signup.html.
Google will give you a backup service key which you need to add to the manifest. You also need to tell the application to use the BackupAgent as follows:
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
...
<meta-data android:name="com.google.android.backup.api_key"
android:value="your_backup_service_key" />
</application>
Then you need to create the backup agent and tell it to use the helper agent for sharedpreferences:
public class MyBackupAgent extends BackupAgentHelper {
// The name of the SharedPreferences file
static final String PREFS = "user_preferences";
// A key to uniquely identify the set of backup data
static final String PREFS_BACKUP_KEY = "prefs";
// Allocate a helper and add it to the backup agent
#Override
public void onCreate() {
SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS);
addHelper(PREFS_BACKUP_KEY, helper);
}
}
To complete the backup you need to create an instance of BackupManager in your main Activity:
BackupManager backupManager = new BackupManager(context);
Finally create a user ID, if it doesn't already exist, and store it in the SharedPreferences:
public static String getUserID(Context context) {
private static String uniqueID = null;
private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
if (uniqueID == null) {
SharedPreferences sharedPrefs = context.getSharedPreferences(
MyBackupAgent.PREFS, Context.MODE_PRIVATE);
uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
if (uniqueID == null) {
uniqueID = UUID.randomUUID().toString();
Editor editor = sharedPrefs.edit();
editor.putString(PREF_UNIQUE_ID, uniqueID);
editor.commit();
//backup the changes
BackupManager mBackupManager = new BackupManager(context);
mBackupManager.dataChanged();
}
}
return uniqueID;
}
This User_ID will now be persistent across installations, even if the user moves device.
For more information on this approach see Reto's talk.
And for full details of how to implement the backup agent see Data Backup. I particularly recommend the section at the bottom on testing as the backup does not happen instantaneously and so to test you have to force the backup.
I think this is sure fire way of building a skeleton for a unique ID... check it out.
Pseudo-Unique ID, that works on all Android devices
Some devices don't have a phone (eg. Tablets) or for some reason, you don't want to include the READ_PHONE_STATE permission. You can still read details like ROM Version, Manufacturer name, CPU type, and other hardware details, that will be well suited if you want to use the ID for a serial key check, or other general purposes. The ID computed in this way won't be unique: it is possible to find two devices with the same ID (based on the same hardware and ROM image) but the changes in real-world applications are negligible. For this purpose you can use the Build class:
String m_szDevIDShort = "35" + //we make this look like a valid IMEI
Build.BOARD.length()%10+ Build.BRAND.length()%10 +
Build.CPU_ABI.length()%10 + Build.DEVICE.length()%10 +
Build.DISPLAY.length()%10 + Build.HOST.length()%10 +
Build.ID.length()%10 + Build.MANUFACTURER.length()%10 +
Build.MODEL.length()%10 + Build.PRODUCT.length()%10 +
Build.TAGS.length()%10 + Build.TYPE.length()%10 +
Build.USER.length()%10 ; //13 digits
Most of the Build members are strings, what we're doing here is to take their length and transform it via modulo in a digit. We have 13 such digits and we are adding two more in front (35) to have the same size ID as the IMEI (15 digits). There are other possibilities here are well, just have a look at these strings.
Returns something like 355715565309247. No special permission is required, making this approach very convenient.
(Extra info: The technique given above was copied from an article on Pocket Magic.)
The following code returns the device serial number using a hidden Android API. But, this code don't works on Samsung Galaxy Tab because "ro.serialno" isn't set on this device.
String serial = null;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
serial = (String) get.invoke(c, "ro.serialno");
}
catch (Exception ignored) {
}
Using the code below, you can get the unique device ID of an Android OS device as a string.
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
One thing I'll add - I have one of those unique situations.
Using:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
Turns out that even though my Viewsonic G Tablet reports a DeviceID that is not Null, every single G Tablet reports the same number.
Makes it interesting playing "Pocket Empires" which gives you instant access to someone's account based on the "unique" DeviceID.
My device does not have a cell radio.
A Serial field was added to the Build class in API level 9 (Android 2.3 - Gingerbread). Documentation says it represents the hardware serial number. Thus it should be unique, if it exists on the device.
I don't know whether it is actually supported (=not null) by all devices with API level >= 9 though.
For detailed instructions on how to get a unique identifier for each Android device your application is installed from, see the official Android Developers Blog posting Identifying App Installations.
It seems the best way is for you to generate one yourself upon installation and subsequently read it when the application is re-launched.
I personally find this acceptable but not ideal. No one identifier provided by Android works in all instances as most are dependent on the phone's radio states (Wi-Fi on/off, cellular on/off, Bluetooth on/off). The others, like Settings.Secure.ANDROID_ID must be implemented by the manufacturer and are not guaranteed to be unique.
The following is an example of writing data to an installation file that would be stored along with any other data the application saves locally.
public class Installation {
private static String sID = null;
private static final String INSTALLATION = "INSTALLATION";
public synchronized static String id(Context context) {
if (sID == null) {
File installation = new File(context.getFilesDir(), INSTALLATION);
try {
if (!installation.exists())
writeInstallationFile(installation);
sID = readInstallationFile(installation);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
return sID;
}
private static String readInstallationFile(File installation) throws IOException {
RandomAccessFile f = new RandomAccessFile(installation, "r");
byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
f.close();
return new String(bytes);
}
private static void writeInstallationFile(File installation) throws IOException {
FileOutputStream out = new FileOutputStream(installation);
String id = UUID.randomUUID().toString();
out.write(id.getBytes());
out.close();
}
}
There are a lot of different approaches to work around those ANDROID_ID issues (may be null sometimes or devices of a specific model always return the same ID) with pros and cons:
Implementing a custom ID generation algorithm (based on device properties that are supposed to be static and won't change -> who knows)
Abusing other IDs like IMEI, serial number, Wi-Fi/Bluetooth-MAC address (they won't exist on all devices or additional permissions become necessary)
I myself prefer using an existing OpenUDID implementation (see https://github.com/ylechelle/OpenUDID) for Android (see https://github.com/vieux/OpenUDID). It is easy to integrate and makes use of the ANDROID_ID with fallbacks for those issues mentioned above.
Add Below code in class file:
final TelephonyManager tm = (TelephonyManager) getBaseContext()
.getSystemService(SplashActivity.TELEPHONY_SERVICE);
final String tmDevice, tmSerial, androidId;
tmDevice = "" + tm.getDeviceId();
Log.v("DeviceIMEI", "" + tmDevice);
tmSerial = "" + tm.getSimSerialNumber();
Log.v("GSM devices Serial Number[simcard] ", "" + tmSerial);
androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(),
android.provider.Settings.Secure.ANDROID_ID);
Log.v("androidId CDMA devices", "" + androidId);
UUID deviceUuid = new UUID(androidId.hashCode(),
((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
String deviceId = deviceUuid.toString();
Log.v("deviceIdUUID universally unique identifier", "" + deviceId);
String deviceModelName = android.os.Build.MODEL;
Log.v("Model Name", "" + deviceModelName);
String deviceUSER = android.os.Build.USER;
Log.v("Name USER", "" + deviceUSER);
String devicePRODUCT = android.os.Build.PRODUCT;
Log.v("PRODUCT", "" + devicePRODUCT);
String deviceHARDWARE = android.os.Build.HARDWARE;
Log.v("HARDWARE", "" + deviceHARDWARE);
String deviceBRAND = android.os.Build.BRAND;
Log.v("BRAND", "" + deviceBRAND);
String myVersion = android.os.Build.VERSION.RELEASE;
Log.v("VERSION.RELEASE", "" + myVersion);
int sdkVersion = android.os.Build.VERSION.SDK_INT;
Log.v("VERSION.SDK_INT", "" + sdkVersion);
Add in AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
My two cents - NB this is for a device (err) unique ID - not the installation one as discussed in the Android developers's blog.
Of note that the solution provided by #emmby falls back in a per application ID as the SharedPreferences are not synchronized across processes (see here and here). So I avoided this altogether.
Instead, I encapsulated the various strategies for getting a (device) ID in an enum - changing the order of the enum constants affects the priority of the various ways of getting the ID. The first non-null ID is returned or an exception is thrown (as per good Java practices of not giving null a meaning). So for instance I have the TELEPHONY one first - but a good default choice would be the ANDROID_ID
beta:
import android.Manifest.permission;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.wifi.WifiManager;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;
// TODO : hash
public final class DeviceIdentifier {
private DeviceIdentifier() {}
/** #see http://code.google.com/p/android/issues/detail?id=10603 */
private static final String ANDROID_ID_BUG_MSG = "The device suffers from "
+ "the Android ID bug - its ID is the emulator ID : "
+ IDs.BUGGY_ANDROID_ID;
private static volatile String uuid; // volatile needed - see EJ item 71
// need lazy initialization to get a context
/**
* Returns a unique identifier for this device. The first (in the order the
* enums constants as defined in the IDs enum) non null identifier is
* returned or a DeviceIDException is thrown. A DeviceIDException is also
* thrown if ignoreBuggyAndroidID is false and the device has the Android ID
* bug
*
* #param ctx
* an Android constant (to retrieve system services)
* #param ignoreBuggyAndroidID
* if false, on a device with the android ID bug, the buggy
* android ID is not returned instead a DeviceIDException is
* thrown
* #return a *device* ID - null is never returned, instead a
* DeviceIDException is thrown
* #throws DeviceIDException
* if none of the enum methods manages to return a device ID
*/
public static String getDeviceIdentifier(Context ctx,
boolean ignoreBuggyAndroidID) throws DeviceIDException {
String result = uuid;
if (result == null) {
synchronized (DeviceIdentifier.class) {
result = uuid;
if (result == null) {
for (IDs id : IDs.values()) {
try {
result = uuid = id.getId(ctx);
} catch (DeviceIDNotUniqueException e) {
if (!ignoreBuggyAndroidID)
throw new DeviceIDException(e);
}
if (result != null) return result;
}
throw new DeviceIDException();
}
}
}
return result;
}
private static enum IDs {
TELEPHONY_ID {
#Override
String getId(Context ctx) {
// TODO : add a SIM based mechanism ? tm.getSimSerialNumber();
final TelephonyManager tm = (TelephonyManager) ctx
.getSystemService(Context.TELEPHONY_SERVICE);
if (tm == null) {
w("Telephony Manager not available");
return null;
}
assertPermission(ctx, permission.READ_PHONE_STATE);
return tm.getDeviceId();
}
},
ANDROID_ID {
#Override
String getId(Context ctx) throws DeviceIDException {
// no permission needed !
final String andoidId = Secure.getString(
ctx.getContentResolver(),
android.provider.Settings.Secure.ANDROID_ID);
if (BUGGY_ANDROID_ID.equals(andoidId)) {
e(ANDROID_ID_BUG_MSG);
throw new DeviceIDNotUniqueException();
}
return andoidId;
}
},
WIFI_MAC {
#Override
String getId(Context ctx) {
WifiManager wm = (WifiManager) ctx
.getSystemService(Context.WIFI_SERVICE);
if (wm == null) {
w("Wifi Manager not available");
return null;
}
assertPermission(ctx, permission.ACCESS_WIFI_STATE); // I guess
// getMacAddress() has no java doc !!!
return wm.getConnectionInfo().getMacAddress();
}
},
BLUETOOTH_MAC {
#Override
String getId(Context ctx) {
BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter();
if (ba == null) {
w("Bluetooth Adapter not available");
return null;
}
assertPermission(ctx, permission.BLUETOOTH);
return ba.getAddress();
}
}
// TODO PSEUDO_ID
// http://www.pocketmagic.net/2011/02/android-unique-device-id/
;
static final String BUGGY_ANDROID_ID = "9774d56d682e549c";
private final static String TAG = IDs.class.getSimpleName();
abstract String getId(Context ctx) throws DeviceIDException;
private static void w(String msg) {
Log.w(TAG, msg);
}
private static void e(String msg) {
Log.e(TAG, msg);
}
}
private static void assertPermission(Context ctx, String perm) {
final int checkPermission = ctx.getPackageManager().checkPermission(
perm, ctx.getPackageName());
if (checkPermission != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Permission " + perm + " is required");
}
}
// =========================================================================
// Exceptions
// =========================================================================
public static class DeviceIDException extends Exception {
private static final long serialVersionUID = -8083699995384519417L;
private static final String NO_ANDROID_ID = "Could not retrieve a "
+ "device ID";
public DeviceIDException(Throwable throwable) {
super(NO_ANDROID_ID, throwable);
}
public DeviceIDException(String detailMessage) {
super(detailMessage);
}
public DeviceIDException() {
super(NO_ANDROID_ID);
}
}
public static final class DeviceIDNotUniqueException extends
DeviceIDException {
private static final long serialVersionUID = -8940090896069484955L;
public DeviceIDNotUniqueException() {
super(ANDROID_ID_BUG_MSG);
}
}
}
There are 30+ answers here and some are same and some are unique. This answer is based on few of those answers. One of them being #Lenn Dolling's answer.
It combines 3 IDs and creates a 32-digit hex string. It has worked very well for me.
3 IDs are:
Pseudo-ID - It is generated based on physical device specifications
ANDROID_ID - Settings.Secure.ANDROID_ID
Bluetooth Address - Bluetooth adapter address
It will return something like this: 551F27C060712A72730B0A0F734064B1
Note: You can always add more IDs to the longId string. For example, Serial #. wifi adapter address. IMEI. This way you are making it more unique per device.
#SuppressWarnings("deprecation")
#SuppressLint("HardwareIds")
public static String generateDeviceIdentifier(Context context) {
String pseudoId = "35" +
Build.BOARD.length() % 10 +
Build.BRAND.length() % 10 +
Build.CPU_ABI.length() % 10 +
Build.DEVICE.length() % 10 +
Build.DISPLAY.length() % 10 +
Build.HOST.length() % 10 +
Build.ID.length() % 10 +
Build.MANUFACTURER.length() % 10 +
Build.MODEL.length() % 10 +
Build.PRODUCT.length() % 10 +
Build.TAGS.length() % 10 +
Build.TYPE.length() % 10 +
Build.USER.length() % 10;
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
String btId = "";
if (bluetoothAdapter != null) {
btId = bluetoothAdapter.getAddress();
}
String longId = pseudoId + androidId + btId;
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(longId.getBytes(), 0, longId.length());
// get md5 bytes
byte md5Bytes[] = messageDigest.digest();
// creating a hex string
String identifier = "";
for (byte md5Byte : md5Bytes) {
int b = (0xFF & md5Byte);
// if it is a single digit, make sure it have 0 in front (proper padding)
if (b <= 0xF) {
identifier += "0";
}
// add number to string
identifier += Integer.toHexString(b);
}
// hex string to uppercase
identifier = identifier.toUpperCase();
return identifier;
} catch (Exception e) {
Log.e("TAG", e.toString());
}
return "";
}
How about the IMEI. That is unique for Android or other mobile devices.
The unique device ID of an Android OS device as String, using TelephonyManager and ANDROID_ID, is obtained by:
String deviceId;
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null) {
deviceId = mTelephony.getDeviceId();
}
else {
deviceId = Secure.getString(
getApplicationContext().getContentResolver(),
Secure.ANDROID_ID);
}
But I strongly recommend a method suggested by Google, see Identifying App Installations.
Here is how I am generating the unique id:
public static String getDeviceId(Context ctx)
{
TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
String tmDevice = tm.getDeviceId();
String androidId = Secure.getString(ctx.getContentResolver(), Secure.ANDROID_ID);
String serial = null;
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) serial = Build.SERIAL;
if(tmDevice != null) return "01" + tmDevice;
if(androidId != null) return "02" + androidId;
if(serial != null) return "03" + serial;
// other alternatives (i.e. Wi-Fi MAC, Bluetooth MAC, etc.)
return null;
}
Another way is to use /sys/class/android_usb/android0/iSerial in an app without any permissions whatsoever.
user#creep:~$ adb shell ls -l /sys/class/android_usb/android0/iSerial
-rw-r--r-- root root 4096 2013-01-10 21:08 iSerial
user#creep:~$ adb shell cat /sys/class/android_usb/android0/iSerial
0A3CXXXXXXXXXX5
To do this in Java one would just use a FileInputStream to open the iSerial file and read out the characters. Just be sure you wrap it in an exception handler, because not all devices have this file.
At least the following devices are known to have this file world-readable:
Galaxy Nexus
Nexus S
Motorola Xoom 3G
Toshiba AT300
HTC One V
Mini MK802
Samsung Galaxy S II
You can also see my blog post Leaking Android hardware serial number to unprivileged apps where I discuss what other files are available for information.
For hardware recognition of a specific Android device you could check the MAC Addresses.
you can do it that way:
in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
now in your code:
List<NetworkInterface> interfacesList = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface interface : interfacesList) {
// This will give you the interface MAC ADDRESS
interface.getHardwareAddress();
}
In every Android device their is at least a "wlan0" Interface witch is the WI-FI chip.
This code works even when WI-FI is not turned on.
P.S.
Their are a bunch of other Interfaces you will get from the list containing MACS But this can change between phones.
I use the following code to get the IMEI or use Secure.ANDROID_ID as an alternative, when the device doesn't have phone capabilities:
String identifier = null;
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE));
if (tm != null)
identifier = tm.getDeviceId();
if (identifier == null || identifier .length() == 0)
identifier = Secure.getString(activity.getContentResolver(),Secure.ANDROID_ID);
TelephonyManger.getDeviceId() Returns the unique device ID, for example, the IMEI for GSM and the MEID or ESN for CDMA phones.
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
String myAndroidDeviceId = mTelephony.getDeviceId();
But i recommend to use:
Settings.Secure.ANDROID_ID that returns the Android ID as an unique 64-bit hex string.
String myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
Sometimes TelephonyManger.getDeviceId() will return null, so to assure an unique id you will use this method:
public String getUniqueID(){
String myAndroidDeviceId = "";
TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null){
myAndroidDeviceId = mTelephony.getDeviceId();
}else{
myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
}
return myAndroidDeviceId;
}
Google Instance ID
Released at I/O 2015; on Android requires play services 7.5.
https://developers.google.com/instance-id/
https://developers.google.com/instance-id/guides/android-implementation
InstanceID iid = InstanceID.getInstance( context ); // Google docs are wrong - this requires context
String id = iid.getId(); // blocking call
It seems that Google intends for this ID to be used to identify installations across Android, Chrome, and iOS.
It identifies an installation rather then a device, but then again, ANDROID_ID (which is the accepted answer) now no longer identifies devices either. With the ARC runtime a new ANDROID_ID is generated for every installation (details here), just like this new instance ID. Also, I think that identifying installations (not devices) is what most of us are actually looking for.
The advantages of instance ID
It appears to me that Google intends for it to be used for this purpose (identifying your installations), it is cross-platform, and can be used for a number of other purposes (see the links above).
If you use GCM, then you will eventually need to use this instance ID because you need it in order to get the GCM token (which replaces the old GCM registration ID).
The disadvantages/issues
In the current implementation (GPS 7.5) the instance ID is retrieved from a server when your app requests it. This means that the call above is a blocking call - in my unscientific testing it takes 1-3 seconds if the device is online, and 0.5 - 1.0 seconds if off-line (presumably this is how long it waits before giving up and generating a random ID). This was tested in North America on Nexus 5 with Android 5.1.1 and GPS 7.5.
If you use the ID for the purposes they intend - eg. app authentication, app identification, GCM - I think this 1-3 seconds could be a nuisance (depending on your app, of course).
Google now has an Advertising ID.
This can also be used, but note that :
The advertising ID is a user-specific, unique, resettable ID
and
enables users to reset their identifier or opt out of interest-based ads within Google Play apps.
So though this id may change, it seems that soon we may not have a choice, depends on the purpose of this id.
More info # develper.android
Copy-paste code here
HTH
To understand the available Unique Ids in Android devices. Use this official guide.
Best practices for unique identifiers:
IMEI, Mac Addresses, Instance Id, GUIDs, SSAID, Advertising Id, Safety Net API to verify devices.
https://developer.android.com/training/articles/user-data-ids

Securely serving files from Amazon S3

I have an app that uploads user files to S3. At the moment, the ACL for the folders and files is set to private.
I have created a db table (called docs) that stores the following info:
id
user_id
file_name (original file as specified by the user)
hash_name (random hash used to save the file on amazon)
So, when a user wants to download a file, I first check in the db table that they have access to file. I'd prefer to not have the file first downloaded to my server and then sent to the user - I'd like them to be able to grab the file directly from Amazon.
Is it OK to rely on a very very long hashname (making it basically impossible for anyone to randomly guess a filename)? In this case, I can set the ACL for each file to public-read.
Or, are there other options that I can use to serve the files whilst keeping them private?
Remember, once the link is out there, nothing prevents a user from sharing that link with others. Then again, nothing prevents the user from saving the file elsewhere and sharing a link to the copy of the file.
The best approach depends on your specific needs.
Option 1 - Time Limited Download URL
If applicable to your scenario, you can also create expiring (time-limited) custom links to the S3 contents. That would allow the user to download content for a limited amount of time, after which they would have to obtain a new link.
http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_QSAuth.html
Option 2 - Obfuscated URL
If you value avoiding running the file through your web server over the risk that a URL, however obscure, might be intentionally shared, then use the hard-to-guess link name. This would allow a link to remain valid "forever", which means the link can be shared "forever".
Option 3 - Download through your server
If you are concerned about the link being shared and certainly want users to authenticate through your website, then serve the content through your website after verifying user credentials.
This option also allows the link to remain valid "forever" but require the user to log in (or perhaps just have an authentication cookie in the browser) to access the link.
I just want to post the PHP solution with code, if anybody has the same problem.
Here's the code I used:
$aws_access_key_id = 'AKIAIOSFODNN7EXAMPLE';
$aws_secret_key = 'YourSecretKey12345';
$aws_bucket = 'bucket';
$file_path = 'directory/image.jpg';
$timeout = '+10 minutes';
// get the URL!
$url = get_public_url($aws_access_key_id,$aws_secret_key,$aws_bucket,$file_path,$timeout);
// print the URL!
echo($url);
function get_public_url($keyID, $s3Key, $bucket, $filepath, $timeout)
{
$expires = strtotime($timeout);
$stringToSign = "GET\n\n\n{$expires}\n/{$aws_bucket}/{$file_path}";
$signature = urlencode(hex2b64(hmacsha1($s3Key, utf8_encode($stringToSign))));
$url = "https://{$bucket}.s3.amazonaws.com/{$file_path}?AWSAccessKeyId={$keyID}&Signature={$signature}&Expires={$expires}";
return $url;
}
function hmacsha1($key,$data)
{
$blocksize=64;
$hashfunc='sha1';
if (strlen($key)>$blocksize)
$key=pack('H*', $hashfunc($key));
$key=str_pad($key,$blocksize,chr(0x00));
$ipad=str_repeat(chr(0x36),$blocksize);
$opad=str_repeat(chr(0x5c),$blocksize);
$hmac = pack(
'H*',$hashfunc(
($key^$opad).pack(
'H*',$hashfunc(
($key^$ipad).$data
)
)
)
);
return bin2hex($hmac);
}
function hex2b64($str)
{
$raw = '';
for ($i=0; $i < strlen($str); $i+=2)
{
$raw .= chr(hexdec(substr($str, $i, 2)));
}
return base64_encode($raw);
}

Is it possible to block cookies from being set using Javascript or PHP?

A lot of you are probably aware of the new EU privacy law, but for those who are not, it basically means no site operated by a company resident in the EU can set cookies classed as 'non-essential to the operation of the website' on a visitors machine unless given express permission to do so.
So, the question becomes how to best deal with this?
Browsers obviously have the ability to block cookies from a specific website built in to them. My question is, is there a way of doing something similar using JS or PHP?
i.e. intercept any cookies that might be trying to be set (including 3rd party cookies like Analytics, or Facebook), and block them unless the user has given consent.
It's obviously possible to delete all cookies once they have been set, but although this amounts to the same thing as not allowing them to be set in the first place, I'm guessing that it's not good enough in this case because it doesn't adhere to the letter of the law.
Ideas?
I'm pretty interested in this answer too. I've accomplished what I need to accomplish in PHP, but the JavaScript component still eludes me.
Here's how I'm doing it in PHP:
$dirty = false;
foreach(headers_list() as $header) {
if($dirty) continue; // I already know it needs to be cleaned
if(preg_match('/Set-Cookie/',$header)) $dirty = true;
}
if($dirty) {
$phpversion = explode('.',phpversion());
if($phpversion[1] >= 3) {
header_remove('Set-Cookie'); // php 5.3
} else {
header('Set-Cookie:'); // php 5.2
}
}
Then I have some additional code that turns this off when the user accepts cookies.
The problem is that there are third party plugins being used in my site that manipulate cookies via javascript and short of scanning through them to determine which ones access document.cookie - they can still set cookies.
It would be convenient if they all used the same framework, so I might be able to override a setCookie function - but they don't.
It would be nice if I could just delete or disable document.cookie so it becomes inaccessible...
EDIT:
It is possible to prevent javascript access to get or set cookies.
document.__defineGetter__("cookie", function() { return '';} );
document.__defineSetter__("cookie", function() {} );
EDIT 2:
For this to work in IE:
if(!document.__defineGetter__) {
Object.defineProperty(document, 'cookie', {
get: function(){return ''},
set: function(){return true},
});
} else {
document.__defineGetter__("cookie", function() { return '';} );
document.__defineSetter__("cookie", function() {} );
}
I adapted Michaels codes from here to come up with this.
Basically it uses the defineGetter and defineSetter methods to set all the cookies on the page and then remove the user specified ones, this role could of course also be reversed if this is what you are aiming for.
I have tested this with third party cookies such as Google Analytics and it appears to work well (excluding the __utmb cookie means I am no longer picked up in Google Analytics), maybe you could use this and adapt it to your specific needs.
I've included the part about if a cookies name is not __utmb for your reference, although you could easily take these values from an array and loop through these that way.
Basically this function will include all cookies except those specified in the part that states if( cookie_name.trim() != '__utmb' ) { all_cookies = all_cookies + cookies[i] + ";"; }
You could add to this using OR or AND filters or pull from an array, database, user input or whatever you like to exclude specific ones (useful for determining between essential and non-essential cookies).
function deleteSpecificCookies() {
var cookies = document.cookie.split(";");
var all_cookies = '';
for (var i = 0; i < cookies.length; i++) {
var cookie_name = cookies[i].split("=")[0];
var cookie_value = cookies[i].split("=")[1];
if( cookie_name.trim() != '__utmb' ) { all_cookies = all_cookies + cookies[i] + ";"; }
}
if(!document.__defineGetter__) {
Object.defineProperty(document, 'cookie', {
get: function(){return all_cookies; },
set: function(){return true},
});
} else {
document.__defineGetter__("cookie", function() { return all_cookies; } );
document.__defineSetter__("cookie", function() { return true; } );
}
}
You can not disable it completely but you can override the default setting with .htaccess
Try
SetEnv session.use_cookies='0';
If it is optional for some users don't use .htaccess
if(!$isAuth)
{
ini_set('session.use_cookies', '0');
}
A little bit old but I think you deserve a answer that works:
Step 1: Don't execute the third party script code.
Step 2: Show the cookie banner.
Step 3: Wait until user accepts, now you can execute the third party script code..
Worked for me.
How about not paying attention to hoaxes?
Aside from the fact that this is old news, the text clearly says that it only applies to cookies that are not essential to the site's function. Meaning session cookies, a shopping basket, or anything that is directly related to making the site work is perfectly fine. Anything else (tracking, stats, etc.) are "not allowed" without permission.

How can i write php page that has an unpredictable url, thus only ones having the link view

I am trying to develop an application on android. The application is basically send the user's location information with the users it specified for a finite amount of time. It will be like Glympse.
I am doing this for a school project so don't have good hosts for the web. I am trying to use free ones. And i can not use their database systems to my own wishes. and inserting and fetching from the database continously will be a burden.
One other option coming to my mind, is to open up php page when the user wants to share hislocation. This page will continously communicate with the gps information. But as this pages should be specific to some people, they must have unique, unpredictable url. like the ones when have password recovery mails. Also i wonder how to make this pages exists for some time and disappears.
The page links will be sent to the shared users lately.
So what about storing any of the URI data is a session array and when a specific key is requested by the user use the received key and replace it by your actual URI stored in the $_SESSION array?
when generating a uri you could have a method store it for you and return a replacing URI
like so (note that you should have session already started that should not be part of this objects scope):
class URIStore{
private $URIstoreID;
private $SUPERSECRETSALT;
private $storeName;
const STORE_EXCEPTION_EXPIRED = 0;
const STORE_EXCEPTION_CSRF = 1;
public function __construct($storename){
//retreive existing store ID so we can built on previous
$this->URIstoreID = isset($_SESSION[$storename]['URIStoreID']) ? $_SESSION[$storename]['URIStoreID'] : 0;
$this->SUPERSECRETSALT = 'jsf098u32rojwe092';//salt could be made random here
$this->storename = $storename;
}
/**
* stored the $uri data in a session for later retrieval through the returned key
* #param mixed $uri - can be anything that you want, like a key value pair array
* #return string
*/
public function store($uri){
$r = md5($this->URIStoreID.$this->SUPERSECRETSALT);
$_SESSION[$this->storename][$r] = $uri;
$this->URIStoreID++;
$_SESSION[$this->storename]['URIStoreID'] = $this->URIStoreID;
return $r;
}
/**
* returns a previously stored URI item
* #param string $key - the key you want to get the URI data for
* #return mixed
* #Throws Exception - when Store or Key doesnt exist
*/
public function retrieve($key){
if(!isset($_SESSION[$this->storename]){
//the initial session has expired
throw new Exception("URIStore [$this->storename] does not exists",self::STORE_EXCEPTION_EXPIRED);
}
elseif(!isset($_SESSION[$this->storename][$key]){
//the requested key does not exist
//could be CSRF attempt?
throw new Exception("URIStore [$this->storename] does not contain this key",self::STORE_EXCEPTION_CSRF);
}
return $_SESSION[$this->storename][$key];
}
}
the use the above you could do the following when building a URL
$store = new URIStore('URIStore');
$URL = SERVER_URL . 'yourscriptthathandlestherequest.php?key=' . $store->store(array('a'=>1,'b'=>23));
and when you retrieve a request you first get the actual data instead of get data
like
$store = new URIStore('URIStore');
try{
$data = $store->retrieve(#$_GET['key']);
}
catch(Exception $e){
switch($e->getCode()){
case URIStore::STORE_EXCEPTION_EXPIRED:
//handle accordingly
break;
case URIStore::STORE_EXCEPTION_CSRF:
//handle accordingly
break;
}
}
note that in your case GET is fine, for CRUD actions I would highly suggest using POST
the Session will expire when no requests are made for a while (depends on server settings)
if you want different expirations for different items (still limited to the session expiration time as a maximum though) you would add another layer to the array that stores the uri and an additional timestamp that you have the URIStore::retrieve method compair against. and finally you could use multiple stores for different things if you wanted. so that you only use the one you expect on a certain page.
You could give the users a link like: script.php?key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx where the x's are an MD5. That MD5 could be the username and a "salt", like sort of a password. In other words, if $_GET['key'] matches md5($username . $super_secret_site_password) then you'll know that person is the real deal.
You need to have some kind of storage anyway, so I'm not goint into that.
The simplest solution is probably to have a unique number (Random unique ID or sequence number), suffix it with something like "read" and "write" and then secure it by building a hash over it and a secret key:
$secret = 'changeme';
$user_id = uniqid();
$public_url = 'page.php?user_id='.$user_id.
'&access=r&check='.sha1($user_id.'r'.$secret);
$owner_url = 'page.php?user_id='.$user_id.
'&access=w&check='.sha1($user_id.'w'.$secret);
Then you can check for them like this:
if ($_GET['check'] == sha1($_GET['user_id'].'r'.$secret))
{
// show location
}
if ($_GET['check'] == sha1($_GET['user_id'].'w'.$secret))
{
// do location update
}
If you don't want the user_id and access type to be visible in the URL you can use phpseclib to encrypt it instead of just securing it by sha1().

how to get browser stored cookies using javascript

I have question regarding accessing all browser cookies using javascript, i was able to do this in shell script but i want to access the cookie information stored in local machine which needs to pass to server.
Regards
You can access them using
document.cookies
You cannot access all browser cookies. Only cookies set for the current domain and not marked 'http-only' (or 'secure' if you are on a non-SSL page).
In javascript use document.cookies
Update
The browser has built in functionality, as a security feature, to prevent you from reading cross domain cookies. As long as the javascript runs in the browser, there is no method to access those cookies, let alone execute a shell command. Google for: same origin policy.
What you basically are looking for has so many security/privacy implications, I don't even know where to start explaining the dangers.
Imagine that was possible. You browse to an arbitrary site that loads third-party ads, a rogue ad reads all your browser cookies and, voilá!, some guy from the Russian Mafia has the "Remember me" cookies and session IDs for all your sites. He can read your e-mail, see your pics on Facebook and retrieve money from your PayPal account.
function getCookie(name) {
// Split cookie string and get all individual name=value pairs in an array
var cookieArr = document.cookie.split(";");
// Loop through the array elements
for (var i = 0; i < cookieArr.length; i++) {
var cookiePair = cookieArr[i].split("=");
/* Removing whitespace at the beginning of the cookie name
and compare it with the given string */
if (name == cookiePair[0].trim()) {
// Decode the cookie value and return
return decodeURIComponent(cookiePair[1]);
}
}
// Return null if not found
return null;
}
function listCookies() {
var theCookies = document.cookie.split(';');
var aString = '';
for (var i = 1 ; i <= theCookies.length; i++) {
aString += i + ' ' + theCookies[i-1] + "\n";
}
return aString;
}

Categories