In a web application scenario, html5 <video> tag is used as follows:
<video src="https://s3-us-west-2.amazonaws.com/mybucket/avideo.mp4">
<source type="video/mp4">
HTML5 Video is required
</video>
How can I stop somebody from directly accessing the video by copy-pasting https://s3-us-west-2.amazonaws.com/mybucket/avideo.mp4 in a browser URL bar?
See the example on this page regarding restricting access to only requests that include a specific HTTP referrer.
Related
When generating a presigned video URL from AWS S3 bucket the video will download in mp4 format if I use the URL in the web browser , however, it will not stream if I put it in the src attribute of a video tag. Below is an example of what the presigned url looks like. How can I use this url to stream?
<video width="320" height="240" controls preload="auto">
<source src="https://s3.eu-north-1.amazonaws.com/daycare.videos/iland-guard/yamit/2020-12-09/cam_0/20201209_043123.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA6OHOPCC5DWJXXO7O%2F20210309%2Feu-north-1%2Fs3%2Faws4_request&X-Amz-Date=20210309T075948Z&X-Amz-Expires=900&X-Amz-Signature=1cfb2dc7658b90714cd5b52b157f3caf878be6504a4d5f9d1a1be76a599abae8&X-Amz-SignedHeaders=host" type="video/mp4">
</video>
To test this, I did the following:
Put an .mp4 file in an Amazon S3 bucket (no Bucket Policy, no ACL)
Generated a pre-signed url using the AWS CLI aws s3 presign command
Tested the pre-signed URL by pasting it into a browser Address Bar -- it worked
Substituted the pre-signed URL in your code (above) -- it worked
I did have a strange problem with a space in the filename -- I had to quote the path for the AWS CLI to include the raw space rather than using a + to represent the space.
The pre-signed URL that was generated looked like this:
https://bucketname.s3.amazonaws.com/A2%20File.mp4?AWSAccessKeyId=AKIAxxx&Signature=xxx&Expires=1615413373
I see that it uses a different format to the one presented in your question.
I replicated above, using the Node library. The code seems to work fine. The key thing to make sure is whether the presigned URL works when placed in your browser. The SDK generates a URL regardless of whether the file exists or not, so make sure the presigned URL is actually functioning.
The format of my presigned URL matches that of yours:
<video width="320" height="240" controls preload="auto">
<source src="https://bucket.s3.ca-central-1.amazonaws.com/Glass%20one/2e71b368-1d22-4c56-a690-b534eea3def8.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAQOTARPS7GGSVO5F7%2F20210915%2Fca-central-1%2Fs3%2Faws4_request&X-Amz-Date=20210915T223104Z&X-Amz-Expires=3600&X-Amz-Signature=f61fc82b7ebd55ea83290587786d61fe41cb6137624d243bf36c46257ef2aab6&X-Amz-SignedHeaders=host&x-id=GetObject" type="video/mp4">
</video>
guys so I have a problem consistently and reliably seeking videos with HTML5. I am getting the videos from an AWS S3 Bucket using Nodejs all videos are in mp4 format. I have tried multiple things to get the video's current time to move every time (most of the time it works but occasionally it doesn't move) but to no avail.
Heres my code:
router.get("/*", (req, res, next) => {
let params = {
Bucket: "bucketName",
Key: decodeURIComponent(req.path.substring(1))
};
s3.getObject(params, function(err, data) {
if (err) {
res.status(500).send(err);
} else {
res.contentType(data.ContentType);
res.send(data.Body);
}
});
});
}
I've been doing some reading and people are saying you can use byte-range requests and request the whole video through a byte-range request. This guy seems to do it with a local file but I am at a loss about how to do it with an s3 file. See post: can't seek html5 video or audio in chrome. The other suggestion I've heard of people doing is HLS encoding but I am not sure what is the best way or how to implement them can someone point me in the right direction?
I think the best answer is probably to implement an HLS or DASH streaming solution. Here is an example of HLS with S3 and CloudFront. And here is a more comprehensive Best Practices for Streaming Media Delivery.
Right now, your app server is simply reading the entire video file from S3 and then sending the entire video file contents directly to the client in an HTTP response. While it appears to work, it might be better to avoid proxying this content, and instead serve it directly to the client from S3 (or CloudFront). One way to do that, for private content, is to send the client an S3 pre-signed URL.
I tested a simple HTML5 video web page against an S3-hosted MPEG video file and was able to view it fine on Chrome, as well as seek back and forth at will. I tested with a relatively small MPEG (15MB).
<html>
<body>
<h1>Stack Overflow 65796272 Video Sample</h1>
<p>This video and associated poster are sourced from Amazon S3 via pre-signed URL.</p>
<div id="container">
<video id='video' controls="controls" preload='none' width="600" poster="https://poster-presigned-url-here">
<source id='mp4' src="https://video-presigned-url-here" type='video/mp4' />
<p>Your user agent does not support the HTML5 video element.</p>
</video>
</div>
</body>
</html>
I pre-created the poster and video pre-signed URLs using the awscli, but you can do this using an AWS SDK and serve them dynamically to your client (or inject them into the HTML sent to the client using any standard template engine such as Express.js). You can remove the poster, if not needed. Note that pre-signed URLs are time-limited.
I made a personal website (http://www.soyoungpark.online) using domain bought from GoDaddy and hosted on AWS s3. I set up everything and thought things were working until I put a simple link to my linkedin profile. When I check the network panel, I see that status code is 200 OK but for the response..there is nothing. The code itself doesn't seem to be problematic; it is simple a with href of the desired link. So I am guessing something could be wrong with my AWS s3 settings? Anyone with similar experience?
It's likely that these services include a header option called "X-Frame" that for security prevents them from being loaded within another site:
The X-Frame-Options HTTP response header can be used to indicate whether or not a browser should be allowed to render a page in a <frame>, <iframe> or <object> . Sites can use this to avoid clickjacking attacks, by ensuring that their content is not embedded into other sites. Source: X-Frame-Options
This does look to be the case when attempting to view Linkedin per your example:
Refused to display 'https://www.linkedin.com/in/exampleuser' in a frame because it set 'X-Frame-Options' to 'sameorigin'.
That said, applying a target Attribute to each to open in a new tab or window should allow these outside services to be navigated to.
e.g:
<a href="https://www.linkedin.com/in/exampleuser" target="_blank">
I get some video files from server and use in templates like this:
<div class="player-block" ng-if='hasSubscription(episode_detail.season)'>
<video ng-if='episode_detail' id="serial-video" class="video-js" controls preload="none" height="450" data-setup="{}">
<source ng-src="{{episode_detail.video_mp}}" type='video/mp4'>
<source ng-src="{{episode_detail.video}}" type='video/webm'>
<p class="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a web browser that
supports HTML5 video
</p>
</video>
</div>
my nginx config:
Thats what i mean, try to rewind video in google chrome:
http://185.143.173.143:8000/media/SPONGEBOB_TRAP_REMIX_KRUSTY_KRAB_Vine_Remix.mp4
The result is not working in chrome rewind , I read that it is necessary to configure the server to return partial content.How do I configure nginx for this?
You have max_ranges set to 0, which will disable Range request handling in nginx. The simplest thing to do would be to remove that max_ranges 0 line. However, that still might not work if the origin (185.143.173.143) does not honor the Range requests.
In order to test if the origin (185.143.173.143) itself supports range requests, you will want to use curl from your nginx machine:
curl -I -r 0-100 http://185.143.173.143/path/to/video
If the Range request worked, the status will be something like 206 Partial Content and there will be a Content-Range header indicating the requested range. The Content-Length should be 101 in this case. If you get a 200 then likely your actual origin itself does not support range requests, and you will have to debug the configuration there.
I'm trying to create signed urls for an RTMP distribution in Amazon's CloudFront. I have the following working:
Signed URLs for Web distribution (over http and https) - so I know I am able to sign URLs correctly.
Unsigned URLs for RTMP distribution - so I know I have CloudFront and S3 setup properly for RTMP.
Main question - now that I'm trying to get a signed url for RTMP, it never seems to be playable.
Part of the confusion is based on the format of the url (similar to this question). So I don't know if it matters which part of the url I sign - if I sign the whole thing (like my http urls), or if I only sign a portion, and if I include the mp4: prefix in the path.
There seem to be a lot of pseudo-similar questions on Stackoverflow, but they seem to be related to slightly different issues, and not about actually creating a signed url for RTMP.
Unfortunately there are many variations to how a RTMP url can be created, which
caused a large portion of the confusion. The following is the way that I was able to get this to work with Amazon CloudFront. To be clear, this was to be used in a *.SMIL file, so it might be different if you only need a single url.
The S3ObjectSummary object has a key for the file, which might be something like folder1/folder2/video.mp4.
Use the above key, and remove the .mp4 extension.
Sign the url as normal (I used CloudFrontService.signUrlCanned().
In the *.smil file that is generated, set the base reference to rtmp://<CloudFront RTMP Distribution Domain>/cfx/st
In the video element, set the height and width, and in the src attribute, prepend mp4: to the signed url portion.
Here is an example SMIL file.
<smil data-livestyle-extension="available">
<head>
<meta base="rtmp://some-cloud-front-domain.net/cfx/st"/>
</head>
<body>
<switch>
<video height="720" width="1280" src="mp4:<signed portion of video path>" />
<video height="480" width="853" src="mp4:<signed portion of video path>" />
</switch>
</body>
</smil>
With Python/boto3, I've managed to sign media files with the rsa_signer
http://boto3.readthedocs.io/en/latest/reference/services/cloudfront.html#generate-a-signed-url-for-amazon-cloudfront
by signing only the path to the media file.
Let's say you want to stream an S3 bucket media file located at 'videos/test.mp4'
Following the boto3 example from the link above, here's what you can do to serve the file with flowplayer:
signed_url = cloudfront_signer.generate_presigned_url(
'videos/test.mp4', date_less_than=expire_date)
Then in the flowplayer code (Django template syntax):
<div class="flowplayer fp-slim" data-rtmp="rtmp://{{cloudfront_domain_prefix}}.cloudfront.net:1935/cfx/st">
<video>
<source type="video/flash" src="mp4:{{signed_src}}">
</video>
</div>