Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A privacy concern of "Media Capture and Streams" #630

Closed
NalaGinrut opened this issue Oct 17, 2019 · 17 comments
Closed

A privacy concern of "Media Capture and Streams" #630

NalaGinrut opened this issue Oct 17, 2019 · 17 comments
Labels
privacy-tracker Group bringing to attention of Privacy, or tracked by the Privacy Group but not needing response. question

Comments

@NalaGinrut
Copy link
Member

NalaGinrut commented Oct 17, 2019

Hi folks!
The PING is trying to review the privacy issues of WebAudio, and I was told that one of my concerns should be asked in media capture.
Here's the original discussion, it's appreciated if anyone could provide further information, thanks! cc @svgeesus

@youennf
Copy link
Contributor

youennf commented Oct 17, 2019

Is it fair to describe your concern as: user agents may decide to prompt the user once for microphone access and will store that permission indefinitely so that web sites could from time to time access the microphone without explicit user permission?

@NalaGinrut
Copy link
Member Author

hi @youennf
Let me elaborate it in details:

  1. Users may decide to give the MIC permission just once, and the next time, the explicit permission is required;
  2. Users may choose to give the MIC permission permanently till the session is expired.
    I asked it to confirm if there's a similar mechanism in the current design.

@youennf
Copy link
Contributor

youennf commented Oct 18, 2019

It is currently up to the user agent to decide for the user or provide the user a way to choose 1 or 2.
I think the current status right now is something like:

  • Firefox prompts for every call to getUserMedia
  • Safari will prompt once per document (modulo a timeout at which point user will be reprompted)
  • Chrome will prompt once per document's origin
    Are you looking for the spec to provide implementation guidelines in that area? or requirements?

@NalaGinrut
Copy link
Member Author

@youennf Thanks for the explanation!

Are you looking for the spec to provide implementation guidelines in that area? or requirements?
I think a clear guideline will be helpful to push it further. Of course, although the implementation may choose to ignore it, it's still possible to negotiate after making the rule explicit.

On the other hand. Is it possible to add one-shot API or parameter to distinct 2 different activities explicitly? I think the final decision could be made by the WebApp author by choosing proper APIs wisely. Or maybe it's out of our reach?

@jan-ivar
Copy link
Member

cc @snyderp

@jan-ivar
Copy link
Member

@NalaGinrut thanks, I share your concerns. Thanks for raising them!

This is why Firefox supports "single-use" (until stream is stopped), with opt-in to ☐ Remember this decision in our permission prompts. Does this cover both your use cases?

I think the final decision could be made by the WebApp author by choosing proper APIs wisely.

That is one way to go. However, I worry how many sites would opt in voluntarily to prompting their users more often in practice, which is why Firefox leaves this privacy decision in the hands of users, not sites today.

@pes10k
Copy link

pes10k commented Oct 18, 2019

@jan-ivar @NalaGinrut thanks for the discussion and pulling me in. My feeling is the same as @jan-ivar 's, that its not sufficient to design web standards to allow well behaved sites to behave well, but to also prevent malicious sites from being malicious.

Chrome will prompt once per document's origin
Is this double keyed? My understanding is yes, that the spec requires permissions to be double keyed. But I'd like to double check my understanding with the group :)

@NalaGinrut
Copy link
Member Author

NalaGinrut commented Oct 19, 2019

hi @jan-ivar

This is why Firefox supports "single-use" (until stream is stopped), with opt-in to ☐ Remember this decision in our permission prompts. Does this cover both your use cases?

Yes, from the perspective of the behavior, it's exactly what I expect. ;-)
Here's my extra question about media-stream-track-stop:

A source that is notified of a track ending will be stopped, unless other MediaStreamTrack objects depend on it.

This description is a bit vague for me, what are the other MediaStreamTrack objects? I'm not sure if it's possible to hijack the related MediaStreamTrack objects so that hijack the MIC.

I think the final decision could be made by the WebApp author by choosing proper APIs wisely.

That is one way to go. However, I worry how many sites would opt in voluntarily to prompting their users more often in practice, which is why Firefox leaves this privacy decision in the hands of users, not sites today.

Yes, I agree with you. My proposal is in the hope that people can still try to push/negotiate with the WebApp authors after we make rules explicitly.

@guest271314
Copy link

guest271314 commented Oct 19, 2019

A source that is notified of a track ending will be stopped, unless other MediaStreamTrack objects depend on it.

This description is a bit vague for me, what are the other MediaStreamTrack objects? I'm not sure if it's possible to hijack the related MediaStreamTrack objects so that hijack the MIC.

Good question.

There does not appear to be a concrete example in code at the specification.

Following the link to "stopped" "source" appears to be referring to the media source device.

Attempting to reproduce the result described at the language of the specification in code, consider

navigator.mediaDevices.getUserMedia({video: true})
.then(stream => {
  const video = document.createElement("video");
  video.muted = video.autoplay = true;
  document.body.insertAdjacentElement("beforeend", video);
  const [track] = stream.getVideoTracks();
  const otherTrack = track.clone();
  const otherStream = new MediaStream([otherTrack]);
  video.srcObject = otherStream;
  setTimeout(() => {
    track.stop();
    track.enabled = false;
    console.log(track.readyState, otherTrack.readyState, stream.active, otherStream.active); 
   }, 3000);
})
.catch(console.error);

when track is "stopped" permission is not "revoked", "source" is not "stopped", otherTrack is not "ended", otherTrack continues playback at <video> element.

In this case without a cloned otherTrack permission is "revoked" when track is "stopped" (at Firefox), does not continue playback at <video> element, "source" is "stopped"

navigator.mediaDevices.getUserMedia({video: true})
.then(stream => {
  const video = document.createElement("video");
  video.muted = video.autoplay = true;
  document.body.insertAdjacentElement("beforeend", video);
  const [track] = stream.getVideoTracks();
  video.srcObject = stream;
  setTimeout(() => {
    track.stop();
    track.enabled = false;
    console.log(track.readyState, stream.active); 
   }, 3000);
})
.catch(console.error);

@jan-ivar
Copy link
Member

A source is stopped when all its tracks are either stopped or garbage collected, e.g. when otherTrack.stop() is called in @guest271314's example.

@jan-ivar
Copy link
Member

jan-ivar commented Oct 20, 2019

Is this double keyed? My understanding is yes, that the spec requires permissions to be double keyed.

I believe Chrome grants to top-level origin, which delegates permission through feature policy. This is what Firefox plans to implement as well.

The benefits are comparable to double-keying: no permissions are ever persisted to the iframe's origin, neither by itself nor across different top-level origins.

@NalaGinrut
Copy link
Member Author

NalaGinrut commented Oct 23, 2019

@guest271314 Thanks for the code!
May I ask why the clone can hang with the original object? I guess there are some references are shared between them. If it's pure functional (referential transparency) then it's safe.
For a specific implementation, it is the author's decision whether to make it pure functional. But I'm concerning that if it's proper to propose it to be impure in a standard spec. This makes me care about the hijacking issue by malicious injected script.

@guest271314
Copy link

guest271314 commented Oct 23, 2019

May I ask why the clone can hang with the original object?

The code attempts to reproduce the language of the specification, and is probably not the only means available to achieve that output.

This makes me care about the hijacking issue by malicious injected script.

Not sure what you mean. How and what exactly would a malicious script be hijacking?

@NalaGinrut
Copy link
Member Author

NalaGinrut commented Oct 23, 2019

May I ask why the clone can hang with the original object?

The code attempts to reproduce the language of the specification, and is probably not the only means available to achieve that output.

I understand your code attempts to reproduce the specification, and I have no question with how you write the code. My question is trying to figure out why the spec permits the other object to hook the object which has enabled MIC even temporarily. Usually, if a clone operation copied all the subtree of an object, then it's ought to be collected by GC without concerning the cloned object. But it seems not here.

Not sure what you mean. How and what exactly would a malicious script be hijacking?

If I understand it correctly, the first object which enabled MIC has been hanging by the cloned object. So that the first object can never be freed iff the cloned object is kept intendedly. In this situation, I think the MIC is still working, right?
Please correct me if I was wrong.

I'm not trying to be picky here, and I know the condition to hijack is extream. :-)
My purpose is to confirm whether the second object (the cloned one) can be used to keep the first object (enabled MIC) working to listen.

@jan-ivar
Copy link
Member

The other object is kept alive by video.srcObject = otherStream; in @guest271314's example.

All clones are equal; there's no original. E.g. there's no functional difference between:

const [track1] = (await navigator.mediaDevices.getUserMedia({audio:true})).getTracks();
const [track2] = (await navigator.mediaDevices.getUserMedia({audio:true})).getTracks();

and

const [track1] = (await navigator.mediaDevices.getUserMedia({audio:true})).getTracks();
const track2 = track1.clone();

track1 and track2 are for all intents and purposes concurrent and independent uses of the microphone. The microphone necessarily has to stay on until both tracks are either stopped or gone.

@jan-ivar
Copy link
Member

In fact, we're giving people this workaround to extend permission to the end of session in Firefox:

keepalive = stream.clone();
for (const track of keepalive.getTracks()) track.enabled = false;

The spec has strong privacy indicator requirements, so everything should work as before, except you'll see gray (not red) camera/mic indicators in the URL bar when everything is off, and they'll turn red again instead of re-prompting user when site requests access anew.

Sadly, this depends on #642.

@youennf
Copy link
Contributor

youennf commented Jan 9, 2020

@NalaGinrut, I am closing this issue.
Please file more specific issues if you think they are not already answered or tracked.

@youennf youennf closed this as completed Jan 16, 2020
@plehegar plehegar added the privacy-tracker Group bringing to attention of Privacy, or tracked by the Privacy Group but not needing response. label Feb 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
privacy-tracker Group bringing to attention of Privacy, or tracked by the Privacy Group but not needing response. question
Projects
None yet
Development

No branches or pull requests

7 participants