I found code on project imas to calculate the integrity of an app and have the problem that I can not set the sha256_placeholder so that the comparison at the end passes. With every run it seems that the calculated hash is different. Do you see how to fix this?
My approach would be to check the integrity with an import of an array of possible app sizes and hashes from my backend and compare it to the calculated app size and hash.
Here is my code:
#import <Foundation/Foundation.h>
#import "IntegrityCheck.h"
#import <CommonCrypto/CommonDigest.h>
unsigned char sha256_placeholder[] =
{ 0x16, 0x13, 0x13, 0x19, 0x14, 0x48, 0xbe, 0xd2, 0x9d, 0x3d, 0x27, 0x45, 0x0b, 0x86, 0x51, 0xde, 0x58, 0x6d,0x39, 0xb2};
unsigned char file_size_placeholder[] = { 0x80, 0x04, 0x67, 0x02 };
NSData *get_sha256() {
return [NSData dataWithBytes:sha256_placeholder length:32];
}
NSData *get_fileSize() {
return[NSData dataWithBytes:file_size_placeholder length:8];
}
#ifdef FAIL
NSString *AppName = #"MyAppiOS_fail";
#else
NSString *AppName = #"MyAppiOS";
#endif
int doAppIntegrity() {
int ret = 0;
//** read my APPS executable
NSFileHandle *inFile;
NSFileManager *fileMgr;
NSString *filePath;
fileMgr = [NSFileManager defaultManager];
//** open and read APP file into a data block
filePath = [[NSBundle mainBundle] pathForResource:AppName ofType:0 ];
if ( [fileMgr fileExistsAtPath:filePath] == NO ) {
NSLog(#"File does not exist!");
ret = -1;
}
//** FILE SIZE
inFile = [NSFileHandle fileHandleForReadingAtPath: filePath];
NSData *plain_txt = [ inFile readDataToEndOfFile];
unsigned int app_file_size = (CC_LONG)[plain_txt length];
NSLog(#"AS-IS - APP file size: %d", app_file_size);
[inFile closeFile];
//** SHA256bit HASH
unsigned char hash[CC_SHA256_DIGEST_LENGTH];
CC_SHA256([plain_txt bytes], (CC_LONG)[plain_txt length], hash);
NSData *app_sig = [NSData dataWithBytes:hash length:CC_SHA1_DIGEST_LENGTH];
NSLog(#"AS-IS - sha_hash_val 20 bytes: %#", app_sig);
NSLog(#"app_sig_len:%lu", (unsigned long)[app_sig length]);
NSData *trusted_app_sig = [NSData dataWithBytes:sha256_placeholder length:CC_SHA1_DIGEST_LENGTH];
NSLog(#"trusted app sig:%#", trusted_app_sig);
NSLog(#"trusted app sig len:%lu", (unsigned long)[trusted_app_sig length]);
NSData *trusted_app_size_data = [NSData dataWithBytes:file_size_placeholder length:4];
unsigned int trusted_app_size;
[trusted_app_size_data getBytes:&trusted_app_size length:sizeof(trusted_app_size)];
NSLog(#"trusted app size hex:%#", trusted_app_size_data);
NSLog(#"trusted app size:%d", trusted_app_size);
// compare computed sha hash to passed in value
if (8004672 != app_file_size) {
NSLog(#"App Integrity FAIL - file size MISMATCH");
ret = -1;
}
else {
NSLog(#"App Integrity PASS - file size MATCH");
}
if ([trusted_app_sig isEqualToData:app_sig]){
NSLog(#"App Integrity PASS - signature MATCH");
}
else {
NSLog(#"App Integrity FAIL - signature MISMATCH");
ret = -1;
}
return ret;
}
I found code on project imas to calculate the integrity of an app and have the problem that I can not set the sha256_placeholder so that the comparison at the end passes. With every run it seems that the calculated hash is different. Do you see how to fix this?
While I cannot help you with validating your code I can alert you for the fact that this approach is easily bypassed by hooking an instrumentation framework, like Frida or xPosed, into the call to doAppIntegrity(), and then change its return value to be always a valid integrity check.
Frida
Inject your own scripts into black box processes. Hook any function, spy on crypto APIs or trace private application code, no source code needed. Edit, hit save, and instantly see the results. All without compilation steps or program restarts.
xPosed
Xposed is a framework for modules that can change the behavior of the system and apps without touching any APKs. That's great because it means that modules can work for different versions and even ROMs without any changes (as long as the original code was not changed too much). It's also easy to undo.
Just to have a notion of the power of instrumentation frameworks, you can watch this video to see how xPosed is being used to bypass certificate pinning during run-time.
So what I want to convey here is that any decisions you make in the client side can be bypassed by using the already mentioned instrumentation frameworks, but if you really want to know if your mobile app have been modified or not, you need to make that decision outside the device. In an high level overview you will need to have an external server that challenges the mobile app to determine its integrity, and not use the result of this integrity check directly on the mobile app, because you know, instrumentation frameworks can hook on the code that uses the integrity check result to decide if it can present or not some content, and modify it to always show the content. This means that the API server must be one that will use the integrity check result to decide to send or not the content to the mobile app. This concept have a name, Mobile App Attestation.
Before I dive into the Mobile APP Attestation concept I would like to first clear a misconception about WHO and WHAT is accessing an API server.
The Difference Between WHO and WHAT is Accessing the API Server
To better understand the differences between the WHO and the WHAT are accessing an API server, let’s use this picture:
The Intended Communication Channel represents the mobile app being used as you expected, by a legit user without any malicious intentions, using an untampered version of the mobile app, and communicating directly with the API server without being man in the middle attacked.
The actual channel may represent several different scenarios, like a legit user with malicious intentions that may be using a repackaged version of the mobile app, a hacker using the genuine version of the mobile app, while man in the middle attacking it, to understand how the communication between the mobile app and the API server is being done in order to be able to automate attacks against your API. Many other scenarios are possible, but we will not enumerate each one here.
I hope that by now you may already have a clue why the WHO and the WHAT are not the same, but if not it will become clear in a moment.
The WHO is the user of the mobile app that we can authenticate, authorize and identify in several ways, like using OpenID Connect or OAUTH2 flows.
OAUTH
Generally, OAuth provides to clients a "secure delegated access" to server resources on behalf of a resource owner. It specifies a process for resource owners to authorize third-party access to their server resources without sharing their credentials. Designed specifically to work with Hypertext Transfer Protocol (HTTP), OAuth essentially allows access tokens to be issued to third-party clients by an authorization server, with the approval of the resource owner. The third party then uses the access token to access the protected resources hosted by the resource server.
OpenID Connect
OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.
While user authentication may let the API server know WHO is using the API, it cannot guarantee that the requests have originated from WHAT you expect, the original version of the mobile app.
Now we need a way to identify WHAT is calling the API server, and here things become more tricky than most developers may think. The WHAT is the thing making the request to the API server. Is it really a genuine instance of the mobile app, or is a bot, an automated script or an attacker manually poking around with the API server, using a tool like Postman?
For your surprise you may end up discovering that It can be one of the legit users using a repackaged version of the mobile app or an automated script that is trying to gamify and take advantage of the service provided by the application.
Well, to identify the WHAT, developers tend to resort to an API key that usually they hard-code in the code of their mobile app. Some developers go the extra mile and compute the key at run-time in the mobile app, thus it becomes a runtime secret as opposed to the former approach when a static secret is embedded in the code.
Mobile App Attestation
The role of a Mobile App Attestation solution is to detect at run-time that your mobile app was not tampered with, is not running in a rooted device, not being instrumented by a framework like xPosed or Frida, not being MitM attacked, and this is achieved by running an SDK in the background. The service running in the cloud will challenge the app, and based on the responses it will attest the integrity of the mobile app and device is running on, thus the SDK will never be responsible for any decisions.
On successful attestation of the mobile app integrity a short time lived JWT token is issued and signed with a secret that only the API server and the Mobile App Attestation service in the cloud are aware. In the case of failure on the mobile app attestation the JWT token is signed with a secret that the API server does not know.
Now the App must sent with every API call the JWT token in the headers of the request. This will allow the API server to only serve requests when it can verify the signature and expiration time in the JWT token and refuse them when it fails the verification.
Once the secret used by the Mobile App Attestation service is not known by the mobile app, is not possible to reverse engineer it at run-time even when the App is tampered, running in a rooted device or communicating over a connection that is being the target of a Man in the Middle Attack.
Summary
While in app integrity checks can be used to male it hard for an attacker to mount an attack in the device, they are not harder to bypass, even for script kids or seasonal attackers.
A best approach is to make the mobile app integrity checks decisions outside of the device, and at same time let the API server know them in order it can decide when to provide or not the content to the mobile app, because decisions made on the client side, aka the mobile app, can always be bypassed with instrumentation frameworks.
In the end, the solution to use in order to protect your mobile app and API server must be chosen in accordance with the value of what you are trying to protect and the legal requirements for that type of data, like the GDPR regulations in Europe.
Do you want to go the Extra Mile?
OWASP Mobile Security Project - Top 10 risks
The OWASP Mobile Security Project is a centralized resource intended to give developers and security teams the resources they need to build and maintain secure mobile applications. Through the project, our goal is to classify mobile security risks and provide developmental controls to reduce their impact or likelihood of exploitation.
Related
Background: why use cookies with Lambdas?
OWASP is very clear that cookies are the best option for session management:
.. cookies .. are one of the most extensively used session ID exchange mechanisms, offering advanced capabilities not available in other methods.
However AWS's API Gateway literature often talks about using JWTs for authentication rather than cookies. While some tech blogs out there seem to think it's ok to use JWTs in this way, there are definitely recognised issues with JWTs. Two issues of particaular note are:
(a) you can't easily invalidate a JWT. At best you can keep a server-side database of blocked JWTs and make sure any service validating JWTs also implements a check against this block list. That sounds a lot like implementing regular old sessions, largely defeating the point of using JWTs.
(b) if your want to use JWTs for authorization as well as authentication, you'll run into issues when you need to update the authorization and it's not a change being driven by the enduser themselves. Scenarios in this category include: a system administrator or account manager changes the user's access level; a trial/contract is ended by a cron job; a webhook is triggered by a 3rd party SaaS integration (e.g. Stripe). You might say, "in that case use a separate mechanism for authorization", but then again you're back to good old sessions.
To be clear, I understand the value of JWTs in letting one server communicate its trust in a user's identity to another server, but that's a very different purpose to session management.
Session management in Node
All roads seem to lead to express-session as the most battle-tested implementation of sessions in Node. It offers a wide range of storage options to choose from*.
In the context of Lambdas, in principle you could try and use express-session as though it were just a function factory, for functions with the signature (req,res,next)=>void, that's rather hacky, and in no way recommended by express-sessions. It's also not entirely clear how best to match that call signature to the objects you get in an AWS Lambda, nor which storage mechanisms are optimised for lambdas (which are ephemeral and need to start quickly).
I would really like a lightweight node module that lets you do something like the following:
import {Sessions} from 'sessions';
// configure session management. Should be super lightweight for use in Lambda.
const sessions = new Sessions({
/* ..basic cookie & expiration config, */
secret: "something", // extra security recommended by express
store: { // object with following interface:
createSession(sessionId, metadata),
getFromSessionId(sessionId),
updateSession(sessionId, metadata),
customIndexedProperties: ['userId'], // in addition to sessionId
getSessionIdsFromIndexedProperty(propertyName, propertyValue),
}
});
// create session. Note the api is not opinionated about response header mechanics.
response.headers['set-cookie'] = await sessions.createCookieForSession({
userId: 'user1',
/*...other user info */
});
// get user's session. Again not opinionated about where cookie comes from.
const userSessionInfo = await sessions.getSessionFromCookie(request.header['cookies']);
// update a user's session, but not initiated by the user themselves
const sessionIds = await sessions.getSessionIdsFromIndexedProperty('userId', 'user1');
await sessions.updateSession(sessionIds[0], {something: 'has change'});
Questions
Is my above thinking reasonable?
Are there any node packages I've not encountered that might be helpful.
If not, why not? I have come across a few people with closely related problems, but it must be a fairly common issue when working with serverless.
If I were to implement a module to my own liking how do I get any confidence that I've done a good job security-wise? I could use pieces of express-session that are relevant, but that's not a great long-term solution for good security.
Related to 4, if I were to try and hook into express-sessions, but just build my own store that does what I need, how would I get confidence in the security? Also, I haven't managed to find any docs on what the official api is for an express-session store, which I find amazing given that express-sessions seems to be the go to for sessions in Node.
Any help would be massively appreciated, thanks!
P.S. I appreciate a lot of what I'm discussing relates to open source projects that are often poorly funded. However, I was very surprised to have reached the above conclusions about the state of the ecosystem and wondered if I was missing something.
*Annoyingly, the suggested DynamoDb store package isn't great. Two of the features we'd want from it are not support, indeed PRs seem to have been opened back in 2019 but never looked at by the maintainer. Technically we don't absolutely have to use DynamoDb as our store, but it is does offer a lot of features we like.
We are developing an SAP Fiori App to be used on the Launchpad and as an offline-enabled hybrid app as well using the SAP SDK and its Kapsel Plug Ins. One issue we are facing at the moment is the ODATA message handling.
On the Gateway, we are using the Message Manager to add additional information to the response
" ABAP snippet, random Gateway entity method
[...]
DATA(lo_message_container) = me->mo_context->get_message_container( ).
lo_message_container->add_message(
iv_msg_type = /iwbep/cl_cos_logger=>warning
iv_msg_number = '123'
iv_msg_id = 'ZFOO'
).
" optional, only used for 'true' errors
RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
message_container = lo_message_container.
In the Fiori app, we can directly access those data from the message manager. The data can be applied to a MessageView control.
// Fiori part (Desktop, online)
var aMessageData = sap.ui.getCore().getMessageManager().getMessageModel().getData();
However, our offline app always has an empty message model. After a sync or flush, the message model is always empty - even after triggering message generating methods in the backend.
The only way to get some kind of messages is to raise a /iwbep/cx_mgw_busi_exception and pass the message container. The messages can be found, in an unparsed state, in the /ErrorArchive entity and be read for further use.
// Hybrid App part, offline, after sync and flush
this.getModel().read("/ErrorArchive", { success: .... })
This approach limits us to negative, "exception worthy", messages only. We also have to code some parts of our app twice (Desktop vs. Offlne App).
So: Is there a "proper" to access those messages after an offline sync and flush?
For analyzing the issue, you might use the tool ILOData as seen in this blog:
Step by Step with the SAP Cloud Platform SDK for Android — Part 6c — Using ILOData
Note, ILOData is part of the Kapsel SDK, so while the blog above was part of a series on the SAP Cloud Platform SDK for Android, it also applies to Kapsel apps.
ILOData is a command line based tool that lets you execute OData requests and queries against an offline store.
It functions as an offline OData client, without the need for an application.
Therefore, it’s a good tool to use to test data from the backend system, as well as verify app behavior.
If a client has a problem with some entries on their device, the offline store from the device can be retrieved using the sendStore method and then ILOData can be used to query the database.
This blog about Kapsel Offline OData plugin might also be helpful.
When a user want to register his device, the relying party provide some parameters which are :
a challenge,
appID,
Version of protocol
The user performs then a "user presence test" by touching the button on his device sending those informations :
dictionary RegisterResponse {
DOMString registrationData;
DOMString clientData;
};
Relying party do what he has to do with those informations and the process is finished !
But I do not understand the following part. Based on the specifications of U2F protocol :
Registration Request Message - U2F_REGISTER
This message is used to initiate a U2F token registration. The FIDO Client first contacts the relying party to obtain a challenge, and then constructs the registration request message. The registration request message has two parts:
The challenge parameter is the SHA-256 hash of the Client Data, a stringified JSON data structure that the FIDO Client prepares. Among other things, the Client Data contains the challenge from the relying party (hence the name of the parameter).
The application parameter [32 bytes]. The application parameter is the SHA-256 hash of the application identity of the application requesting the registration. (See [FIDOAppIDAndFacets] in bibliography for details.)
https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html
At which step this part is run ?
Thank you in advance !
You are talking about registration so linking a key to an account. To register a key:
The user types in name/password and posts to the server.
The server replies with RegisterRequestData (created with the server side u2f library).
The client side uses library function u2f.register which gives the request to the U2F device and gets back a RegisterResponse (json with the device registration info). This is send back to the server.
The server gives the reply to the serverside u2f library and saves the DeviceRegistration.
Authentication/login is similar but with the server sending DeviceRegistration + challenge to the client which uses u2f.sign and returns the DeviceResponse.
A schema that makes it clear I think:
https://developers.yubico.com/U2F/Libraries/Using_a_library.html
We are switching our notifications infrastructure to use new GCM 3.0 registration mechanism using Instance ID API. Previously we were using old mechanism using GCMRegistrar.register() method.
The problem we have is that we have noticed that if device was registered with old GCMRegistrar, after update and registering with new Instance ID API, both registration tokens are valid and can receive notifications.
I expected old registration token to be deactivated and that our push server would receive canonical registration ID when sending notification to old token (as described here GCM registering with two different working registration ids), especially that application version has changed, but such case seems not to happen.
Is this correct behavior of GCM? Is there any way we can detect on push server that device received new token (without unregistering from app)?
I have finally received an answer from Google support regarding my issue. It turned out that this was intended behavior:
What you observed is in the intended behavior due to the need to support backward compatible
registration ID.
We recommend you to flag the old registration ID from gcmregistrar() and don't use that to send anymore once you have the registration token from getToken(). (I believe you probably has implemented a solution to detect such)
Our solution was to simply remove old registration tokens from our push server before registering new user.
We did not use GCMRegistrar.unregister() as we observed that it was able to unregister new tokens (obtained via getToken()).
UPDATE:
I just wanted to provide a quick update to anyone interested in this subject.
It seems that this issue was fixed as when we tested our registration mechanism recently, it turned out that new GCM tokens replace (and unregister) old tokens.
Registration token may change upon certain scenarios even going forward. While cononical registration ID is also a good idea. Use tokenRefresh as shown here as well.
#Override
public void onTokenRefresh() {
// Fetch updated Instance ID token and notify our app's server of any changes (if applicable).
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);
}
Suggesting based on this line "it is needed for key rotation and to handle special cases" in method reference.
I am developing a Cocoa Mac app which dynamically generates and registers itself for URL schemes. However, when the application registers itself to handle a newly generated URL scheme (e.g. myscheme1423://), I would like to prevent the application from responding to any previously registered URL schemes.
I am using LSSetDefaultHandlerForURLScheme() for the purpose of registering a URL scheme; in conjunction, the application automatically overwrites it's Info.plist to contain the new scheme. As you may know, the LSSetDefaultHandlerForURLScheme() function adds the given bundleID/scheme to a Launch Services database. However, I couldn't find an equivalent Launch Services function to remove the same bundleID/scheme pair from the database.
I know that I could simply ignore any external events which originated from a URL scheme other than the one for which the app is actively registered, but it feels to me that there should be a simple way to completely wipe out the system's knowledge of the previous scheme. If my application goes through the process of registering for a new scheme more than a few hundred times, a point will come where a significant amount of space (for a Plist, at least) is being taken up on disk by a plethora of pointless pieces of data (i.e. the old Launch Services entries).
I just fired up a playground and began playing. This is utterly undocumented but it appears to work.
Try passing ("None" as CFString) for the second parameter of
LSSetDefaultHandlerForURLScheme()