Office Hours — Today, January 22

Saturday, January 19

Mark M.
has entered the room
Mark M.
turned on guest access
Tad
has entered the room
Tad
Hey Mark...
Mark M.
hello, Tad!
how can I help you today?
Tad
Thanks for responding yesterday to my Stack Overflow post...
A couple of follow-up questions I had
I'm running down the notion you had about TOS violation - that was a good point.
In terms of security though, can you elaborate a bit on what caused you to think there may be an issue?
Jan 22
9:00 AM
Tad
I'm not storing any credentials - I go through the flow I've seen that is supposed to be what I thought was best practice:
Mark M.
unless you (and the users) are explicitly expecting other apps to be able to use your DocumentsProviders, I wouldn't go the route that you're going
Tad
I spawn off an intent that causes chrome or webview to come up and require the user to log in to their DB account, then I get an access token back and I use that for all access.
Mark M.
I don't know enough about the cloud service URLs to know whether leaking that information to other apps might represent a security issue or not
Tad
I guess I don't see any harm in other apps using the DocumentsProvider I wrote - as long as I am not violating any TOS as you suggest, they will have to have also retrieved an access token.
Mark M.
that's your call
Tad
IOW, the provider won't work without a token.
But I am curious why you think this is still a bad idea.
Is it not due to a programmatic thing, but more of a "that's not how Android community expects it" kind of thing?
Mark M.
IMHO, adding some device-wide stuff (DocumentsProvider) for what ostensibly is an in-app thing (browsing media) is not a good idea
Tad
OK - I hear you. I'm curious WHY you think it is a bad idea...confusing for users? Not the right "culture"?
Mark M.
it opens you up to risks
Tad
Please elaborate
Risks of....?
Mark M.
other apps using the data that you are publishing
unless that's part of your business model, of course
but I get the sense that you're deeming that to be a side effect
in which case, I'd go a different route
9:05 AM
Mark M.
you're not going to be focusing on the security or privacy aspects, because you're going to be focused on your own app and its use of the provider
and that's risky
case in point: the Android Scripting Environment
Tad
The DP requires an access token to work. It also requires that this token be saved in Settings (which maybe you also think is a bad idea?). For every call that requires a token, it checks the settings (to see if it got updated) and retrieves the token. Thus, if a user of this DP doesn't know about that token or doesn't know the settings, etc. it simply won't work.
Mark M.
waaaaaaaaaaay back in the early days of Android, ASE was created to allow users to write scripts in various languages (Perl, Python, PHP, etc.)
but the ASE devs wanted those scripts to be able to perform some Android-y things, like get at location data
so, they wrote an embedded Web service, exposing a bunch of Android APIs via Web service APIs, to localhost on a particular port
and as part of script execution, they would inject a global variable (or the equivalent) that served as a language-specific client to that Web service
as a result, scripts could call functions on some "Android" object and be able to access that API
it was a slick solution to enabling basic Android access to languages that lacked any direct support for it
it also was a privacy issue, because *any* app on the device could hit that Web service
the ASE devs didn't think about that, because their focus was on their script engines accessing the Web service
Tad
I see.
Mark M.
when ASE was eventually rebooted as Scripting Layer for Android (SL4A), they revised the Web service to include a unique path segment
which they passed into their script engines, so they could form valid paths, but other apps could not
9:10 AM
Mark M.
I am not saying that what you're doing is identical, but there are similarities: offering a system-wide API for what really should be an in-app thing
this all comes back to one decision: "I decided to use the Android File Chooser"
using the Storage Access Framework as a client is perfectly fine
developing DocumentsProvider implementations is fine... if your objective is to offer a system-wide source of documents
Tad
I get what you are saying.
Mark M.
I just don't like seeing that sort of system-wide thing being done as a side-effect, for ASE-style "oops" scenarios
there are a bazillion file picker libraries and a bazillion gallery libraries for Android
even if none of them are sufficiently pluggable for other data sources, it suggests that writing such code is not that difficult
Tad
The flip side to that is making the users (elderly people in my case) potentially go through multiple chooser experiences to be able to get the content they want...that's what I was trying to avoid.
But I hear you on taking care on the unintended side-effects of opening up access to this content to other apps.
Mark M.
you can have one chooser, but IMHO it needs to be your own, not a system one
Tad
Right. I'm big on not re-inventing the wheel :)
Mark M.
sure, but there are bazillion existing wheels
personally, I think having a provide DocumentsProvider would be a nice addition to the SDK
9:15 AM
Mark M.
er, sorry, a private DocumentsProvider
but, unless they snuck that in when I wasn't looking, there's no option for that
you could try to play some games with enabling/disabling your DocumentsProvider on the fly
I don't know how well that would work, but it might limit the side-effect visibility, if you really wanted to stick with your current approach
but another benefit to having your own UI is that you get past the problem that triggered your SO question
you won't be stuck with the whole DocumentsContract interface between your content consumers (Glide, ExoPlayer) and the content sources (DropBox, et. al.)
Tad
OK - thanks for the insight. I'll muse on that a bit. I actually think from a security standpoint the app is technically pretty tight (I don't surface the access token anywhere, it is encrypted in the DB, I don't publish how it is used in the DP, etc.), so I don't think the token itself can get hi-jacked, but certainly retrieving the documents could be re-used by other apps, which is exactly your point.
Yep - I agree with your latter points.
I was thinking about that last night.
Mark M.
to put it another way: your SO question looks like yak shaving
Tad
"yak shaving"....(!) That's a new one. What does that mean?
Mark M.
ah, here's the link I was looking for: https://seths.blog/2005/03/dont_shave_that/
Tad
I just looked it up.
Funny.
9:20 AM
Tad
Hey let me ask you something else - I did manage to find a couple of posts from you a few years back about using a ParcelFileDescriptor for streaming, but it still looked to me like it was being used in a larger context to stream down locally and hand back a handle to a file stored locally.
Is it possible to use a PFD to a dynamic real-time stream to it's caller?
Mark M.
if you don't need rewind, you can use a ParcelFileDescriptor pipe
Tad
Or is a ParcelFileDescriptor ("File") always intended to ultimately represent a file stored locally?
Is there a good example of doing that?
Mark M.
I have some pipe examples in *The Busy Coder's Guide to Android App Development*
Tad
ok I'll check those out.
Mark M.
the problem is that you don't wind up with a seekable stream on the client side
ParcelFileDescriptors backed by files can be rewound
Tad
Right, I remember that discussion in the post I read.
Mark M.
ParcelFileDescriptors backed by pipes cannot
in your case, while Glide might not need to rewind, I bet ExoPlayer does
Tad
The other thing that was confusing to me - is I don't understand how these objects get used by the recipients. So for example, if I were to implement a pipe and return that from my DocumentsProvider the way I return one now based on a physical file, could Glide and/or ExoPlayer just "use it"?
Somewhere, it seems that it has to still resolve to a URI (since that's what those products expect).
Mark M.
I haven't tried pipes with DocumentsProvider specifically, only ContentProvider (on which DocumentsProvider is based)
Tad
But I don't see where/how this is happening (but it clearly works with a ParcelFileDesciptor.open(File) now, which is what I'm doing in my DropBoxProvider.
At least - I have tested this with Glide, not yet ExoPlayer
Mark M.
you'd just replace that with one end of the pipe, where the other end is used by a background thread of yours
the client is oblivious to it... until they try to use Java stream APIs like mark() and reset() that won't work with the pipie
er, pipe
9:25 AM
Mark M.
so, in openFile(), instead of using ParcelFileDescriptor.open(), I use ParcelFileDescriptor.createPipe()
that returns a two-element ParcelFileDescriptor array
Tad
I understand conceptually what you are saying, but what I'm still missing is what is expected "underneath". I.e. for example the Glide API uses a .load() method that expects a URI. When that URI is content://, my provider gets called, and I return a ParcelFileDescriptor. When it is based on a physical file, this somehow under the covers gets converted back to the URI that Glide needs. This will "just work" if it is a pipe?
Mark M.
the 0th element is what you return, representing the client's side of the pipe
yes
other than the seekable-stream issue
Glide uses openInputStream() on a ContentResolver
(at least, I assume that's what they're using)
Tad
Any idea about ExoPlayer?
Mark M.
presumably the same, though there are APIs on ContentResolver to get the ParcelFileDescriptor directly
but whether that ParcelFileDescriptor represents a file, pipe, or something else is not something the client knows about
and, if they all worked the same, I'd be much happier
9:35 AM
Tad
This conversation has me wondering about another security issue. What's the standard access control over persisted data in my app, i.e. I have a local DB that the app is using (just saved in the default location). My understanding is that other apps cannot see it, is that right?
Mark M.
the default location for databases is on internal storage, which is only visible to your app, plus apps running with root privileges
the latter scenario is uncommon, but it's why people root their devices
Tad
So what is the best practice for protecting sensitive data -i.e. back to my access token. I took the approach of never saving user credentials, but I do save that access token associated with a specific user's Dropbox account (this is the practice the Dropbox dev site suggests).
I actually am saving this token now in Shared Preferences, but it is encrypted. So it is useless as is in Shared Prefs.
Mark M.
somewhere on internal storage (database, SharedPreferences, or other sort of file) should be OK
Tad
What I store locally in the DB is the key to unencrypt.
Is that overkill?
Is it ok to simply store the token in Shared Prefs as-is?
Mark M.
a hardcoded or persisted key is overkill
that's not especially difficult for an attacker to get past
Tad
ok
9:40 AM
Mark M.
if the encryption were based on a user-supplied passphrase, or used hardware key storage (e.g., KeyStore), then there may be incremental value
still probably overkill, though, just for an API key
Tad
yeah, ok.
Do you have a favorite open source File Chooser that I could look into if I decide to abandon the SAF approach?
Mark M.
no, because I haven't yet needed one
that's the list I point people to, though
Tad
ok I'll check it out.
Btw - are you aware of good forums (in addition to SO) for ExoPlayer discussion?
9:45 AM
Mark M.
no, sorry, I haven't had to use ExoPlayer either, so I haven't needed to hunt around for help
Tad
OK - well, thanks for your time this morning (7am my time - I'm three cups of coffee into the day :) )!
Mark M.
you're welcome, and enjoy the caffeine! :-)
10:00 AM
Mark M.
OK, that's a wrap for today's chat
the transcript will be up on https://commonsware.com/office-hours/ shortly
the next chat is tomorrow at 4pm US Eastern / 1pm US Pacific
have a pleasant day!
Tad
has left the room
Mark M.
turned off guest access

Saturday, January 19

 

Office Hours

People in this transcript

  • Mark Murphy
  • Tad